summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--api/current.txt1124
-rw-r--r--cmds/bu/src/com/android/commands/bu/Backup.java7
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java30
-rw-r--r--core/java/android/app/ActionBar.java21
-rw-r--r--core/java/android/app/Activity.java48
-rw-r--r--core/java/android/app/ActivityManager.java21
-rw-r--r--core/java/android/app/ContextImpl.java23
-rw-r--r--core/java/android/app/Notification.java11
-rw-r--r--core/java/android/app/VoiceInteractor.java140
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java20
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--core/java/android/app/backup/BackupTransport.java175
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java2
-rw-r--r--core/java/android/bluetooth/BluetoothLeAdvertiseScanData.java645
-rw-r--r--core/java/android/bluetooth/BluetoothLeAdvertiser.java615
-rw-r--r--core/java/android/bluetooth/BluetoothLeScanner.java759
-rw-r--r--core/java/android/bluetooth/IBluetoothGatt.aidl17
-rw-r--r--core/java/android/bluetooth/le/AdvertiseCallback.java68
-rw-r--r--core/java/android/bluetooth/le/AdvertiseSettings.aidl (renamed from core/java/android/bluetooth/BluetoothLeScanFilter.aidl)4
-rw-r--r--core/java/android/bluetooth/le/AdvertiseSettings.java218
-rw-r--r--core/java/android/bluetooth/le/AdvertisementData.aidl (renamed from core/java/android/bluetooth/BluetoothLeAdvertiser.aidl)4
-rw-r--r--core/java/android/bluetooth/le/AdvertisementData.java344
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeAdvertiser.java368
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeScanner.java371
-rw-r--r--core/java/android/bluetooth/le/ScanCallback.java79
-rw-r--r--core/java/android/bluetooth/le/ScanFilter.aidl (renamed from core/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl)4
-rw-r--r--core/java/android/bluetooth/le/ScanFilter.java (renamed from core/java/android/bluetooth/BluetoothLeScanFilter.java)269
-rw-r--r--core/java/android/bluetooth/le/ScanRecord.java278
-rw-r--r--core/java/android/bluetooth/le/ScanResult.aidl (renamed from core/java/android/bluetooth/BluetoothLeScanner.aidl)5
-rw-r--r--core/java/android/bluetooth/le/ScanResult.java162
-rw-r--r--core/java/android/bluetooth/le/ScanSettings.aidl19
-rw-r--r--core/java/android/bluetooth/le/ScanSettings.java221
-rw-r--r--core/java/android/content/Context.java44
-rw-r--r--core/java/android/content/ContextWrapper.java5
-rw-r--r--core/java/android/content/IRestrictionsManager.aidl30
-rw-r--r--core/java/android/content/RestrictionEntry.java157
-rw-r--r--core/java/android/content/RestrictionsManager.java344
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl7
-rw-r--r--core/java/android/content/pm/LauncherActivityInfo.java44
-rw-r--r--core/java/android/content/pm/LauncherApps.java147
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java24
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java28
-rw-r--r--core/java/android/hardware/camera2/impl/CameraMetadataNative.java2
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePair.java158
-rw-r--r--core/java/android/hardware/camera2/params/MeteringRectangle.java56
-rw-r--r--core/java/android/hardware/hdmi/HdmiCec.java4
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java7
-rw-r--r--core/java/android/inputmethodservice/SoftInputWindow.java4
-rw-r--r--core/java/android/net/ConnectivityManager.java223
-rw-r--r--core/java/android/net/ConnectivityServiceProtocol.java70
-rw-r--r--core/java/android/net/IConnectivityManager.aidl4
-rw-r--r--core/java/android/net/NetworkAgent.java337
-rw-r--r--core/java/android/net/NetworkFactory.java275
-rw-r--r--core/java/android/net/NetworkInfo.java14
-rw-r--r--core/java/android/net/NetworkRequest.java22
-rw-r--r--core/java/android/nfc/cardemulation/AidGroup.java17
-rw-r--r--core/java/android/nfc/cardemulation/CardEmulation.java51
-rw-r--r--core/java/android/os/BaseBundle.java (renamed from core/java/android/os/CommonBundle.java)114
-rw-r--r--core/java/android/os/Bundle.java363
-rw-r--r--core/java/android/os/Environment.java4
-rw-r--r--core/java/android/os/PersistableBundle.java470
-rw-r--r--core/java/android/preference/SeekBarVolumizer.java91
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java33
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java243
-rw-r--r--core/java/android/speech/tts/Markup.java537
-rw-r--r--core/java/android/speech/tts/SynthesisRequestV2.java38
-rw-r--r--core/java/android/speech/tts/TextToSpeechClient.java67
-rw-r--r--core/java/android/speech/tts/TextToSpeechService.java53
-rw-r--r--core/java/android/speech/tts/Utterance.java595
-rw-r--r--core/java/android/tv/ITvInputClient.aidl3
-rw-r--r--core/java/android/tv/ITvInputSessionCallback.aidl3
-rw-r--r--core/java/android/tv/TvInputManager.java66
-rw-r--r--core/java/android/tv/TvInputService.java65
-rw-r--r--core/java/android/tv/TvView.java19
-rw-r--r--core/java/android/view/GLES20Canvas.java114
-rw-r--r--core/java/android/view/GLES20RecordingCanvas.java2
-rw-r--r--core/java/android/view/GLRenderer.java1527
-rw-r--r--core/java/android/view/HardwareCanvas.java42
-rw-r--r--core/java/android/view/HardwareLayer.java140
-rw-r--r--core/java/android/view/HardwareRenderer.java21
-rw-r--r--core/java/android/view/RenderNodeAnimator.java16
-rw-r--r--core/java/android/view/ThreadedRenderer.java22
-rw-r--r--core/java/android/view/View.java6
-rw-r--r--core/java/android/view/ViewRootImpl.java11
-rw-r--r--core/java/android/view/WindowManagerPolicy.java5
-rw-r--r--core/java/android/widget/AbsListView.java35
-rw-r--r--core/java/android/widget/ActionMenuView.java7
-rw-r--r--core/java/android/widget/DatePicker.java4
-rw-r--r--core/java/android/widget/Toolbar.java190
-rw-r--r--core/java/com/android/internal/app/IMediaContainerService.aidl10
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractor.aidl4
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractorCallback.aidl1
-rw-r--r--core/java/com/android/internal/app/ToolbarActionBar.java179
-rw-r--r--core/java/com/android/internal/app/WindowDecorActionBar.java6
-rw-r--r--core/java/com/android/internal/backup/BackupConstants.java28
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java20
-rw-r--r--core/java/com/android/internal/content/NativeLibraryHelper.java15
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java91
-rw-r--r--core/java/com/android/internal/os/HandlerCaller.java4
-rw-r--r--core/java/com/android/internal/policy/IKeyguardService.aidl5
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java61
-rw-r--r--core/java/com/android/internal/util/Preconditions.java27
-rw-r--r--core/java/com/android/internal/util/Protocol.java4
-rw-r--r--core/java/com/android/internal/util/VirtualRefBasePtr.java10
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java17
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java42
-rw-r--r--core/java/com/android/internal/widget/ToolbarWidgetWrapper.java19
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android/graphics/Camera.cpp4
-rw-r--r--core/jni/android/graphics/Canvas.cpp210
-rw-r--r--core/jni/android/graphics/Graphics.cpp4
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h1
-rw-r--r--core/jni/android/graphics/MaskFilter.cpp3
-rw-r--r--core/jni/android/graphics/NinePatch.cpp4
-rw-r--r--core/jni/android/graphics/Picture.cpp2
-rw-r--r--core/jni/android_media_AudioFormat.h54
-rw-r--r--core/jni/android_media_AudioSystem.cpp1115
-rw-r--r--core/jni/android_view_GLES20Canvas.cpp112
-rw-r--r--core/jni/android_view_GLRenderer.cpp203
-rw-r--r--core/jni/android_view_GraphicBuffer.cpp13
-rw-r--r--core/jni/android_view_HardwareLayer.cpp33
-rw-r--r--core/jni/android_view_RenderNodeAnimator.cpp6
-rw-r--r--core/jni/android_view_Surface.cpp12
-rw-r--r--core/jni/android_view_TextureView.cpp14
-rw-r--r--core/jni/android_view_ThreadedRenderer.cpp32
-rw-r--r--core/jni/com_android_internal_content_NativeLibraryHelper.cpp41
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/anim/input_method_exit.xml2
-rw-r--r--core/res/res/anim/lock_screen_behind_enter.xml15
-rw-r--r--core/res/res/anim/lock_screen_wallpaper_behind_enter.xml28
-rw-r--r--core/res/res/anim/voice_activity_open_enter.xml1
-rw-r--r--core/res/res/anim/voice_layer_enter.xml1
-rw-r--r--core/res/res/color/btn_default_quantum_dark.xml4
-rw-r--r--core/res/res/color/btn_default_quantum_light.xml4
-rw-r--r--core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.pngbin170 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.pngbin147 -> 0 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.pngbin194 -> 0 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.pngbin327 -> 0 bytes
-rw-r--r--core/res/res/drawable/ic_lock_bugreport.xml37
-rw-r--r--core/res/res/drawable/item_background_borderless_quantum.xml (renamed from core/res/res/drawable/list_selector_quantum.xml)7
-rw-r--r--core/res/res/drawable/item_background_quantum.xml7
-rw-r--r--core/res/res/layout/alert_dialog_quantum.xml19
-rw-r--r--core/res/res/values/attrs.xml11
-rw-r--r--core/res/res/values/colors_quantum.xml10
-rw-r--r--core/res/res/values/config.xml1
-rw-r--r--core/res/res/values/dimens_quantum.xml5
-rw-r--r--core/res/res/values/donottranslate_quantum.xml2
-rw-r--r--core/res/res/values/public.xml3
-rw-r--r--core/res/res/values/strings.xml2
-rw-r--r--core/res/res/values/styles.xml6
-rw-r--r--core/res/res/values/styles_quantum.xml47
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/res/res/values/themes.xml3
-rw-r--r--core/res/res/values/themes_quantum.xml46
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java (renamed from core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScanFilterTest.java)95
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java (renamed from core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java)12
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java (renamed from core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScannerTest.java)14
-rw-r--r--core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java78
-rw-r--r--core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java30
-rw-r--r--data/fonts/Roboto-Black.ttfbin127948 -> 127948 bytes
-rw-r--r--data/fonts/Roboto-Bold.ttfbin127744 -> 127744 bytes
-rw-r--r--data/fonts/Roboto-BoldItalic.ttfbin134556 -> 134556 bytes
-rw-r--r--data/fonts/Roboto-Italic.ttfbin132440 -> 132440 bytes
-rw-r--r--data/fonts/Roboto-Light.ttfbin126792 -> 126792 bytes
-rw-r--r--data/fonts/Roboto-LightItalic.ttfbin133172 -> 133172 bytes
-rw-r--r--data/fonts/Roboto-Medium.ttfbin127488 -> 127488 bytes
-rw-r--r--data/fonts/Roboto-MediumItalic.ttfbin134312 -> 134312 bytes
-rw-r--r--data/fonts/Roboto-Regular.ttfbin126072 -> 126072 bytes
-rw-r--r--data/fonts/Roboto-Thin.ttfbin127584 -> 127584 bytes
-rw-r--r--data/fonts/Roboto-ThinItalic.ttfbin132860 -> 132860 bytes
-rw-r--r--data/fonts/RobotoCondensed-Bold.ttfbin127340 -> 127340 bytes
-rw-r--r--data/fonts/RobotoCondensed-BoldItalic.ttfbin135504 -> 135504 bytes
-rw-r--r--data/fonts/RobotoCondensed-Italic.ttfbin133908 -> 133908 bytes
-rw-r--r--data/fonts/RobotoCondensed-Light.ttfbin126168 -> 126168 bytes
-rw-r--r--data/fonts/RobotoCondensed-LightItalic.ttfbin134544 -> 134544 bytes
-rw-r--r--data/fonts/RobotoCondensed-Regular.ttfbin125332 -> 125332 bytes
-rw-r--r--data/keyboards/Vendor_2378_Product_1008.kl35
-rw-r--r--docs/html/google/play/billing/billing_admin.jd10
-rw-r--r--docs/html/google/play/billing/billing_testing.jd131
-rw-r--r--docs/html/google/play/billing/v2/billing_integrate.jd19
-rw-r--r--docs/html/google/play/expansion-files.jd78
-rw-r--r--docs/html/google/play/licensing/licensing-reference.jd23
-rw-r--r--docs/html/google/play/licensing/overview.jd17
-rw-r--r--docs/html/tools/device.jd4
-rw-r--r--graphics/java/android/graphics/Camera.java2
-rw-r--r--graphics/java/android/graphics/Canvas.java157
-rw-r--r--graphics/java/android/graphics/NinePatch.java4
-rw-r--r--graphics/java/android/graphics/Picture.java2
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java25
-rw-r--r--graphics/java/android/graphics/drawable/LayerDrawable.java2
-rw-r--r--graphics/java/android/graphics/drawable/Ripple.java339
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java448
-rw-r--r--libs/hwui/Android.mk11
-rw-r--r--libs/hwui/Animator.cpp4
-rw-r--r--libs/hwui/Animator.h3
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp17
-rw-r--r--libs/hwui/DeferredLayerUpdater.h14
-rw-r--r--libs/hwui/DisplayList.cpp5
-rw-r--r--libs/hwui/DisplayList.h1
-rw-r--r--libs/hwui/DisplayListOp.h38
-rw-r--r--libs/hwui/DisplayListRenderer.cpp9
-rw-r--r--libs/hwui/DisplayListRenderer.h17
-rw-r--r--libs/hwui/OpenGLRenderer.cpp4
-rw-r--r--libs/hwui/OpenGLRenderer.h2
-rw-r--r--libs/hwui/PathCache.cpp4
-rw-r--r--libs/hwui/PathCache.h5
-rw-r--r--libs/hwui/RenderNode.cpp4
-rw-r--r--libs/hwui/Renderer.h6
-rw-r--r--libs/hwui/StatefulBaseRenderer.cpp12
-rw-r--r--libs/hwui/StatefulBaseRenderer.h4
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp26
-rw-r--r--libs/hwui/renderthread/CanvasContext.h6
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp31
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h13
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp33
-rw-r--r--libs/hwui/renderthread/RenderProxy.h3
-rw-r--r--media/java/android/media/AudioManager.java170
-rw-r--r--media/java/android/media/AudioPort.java3
-rw-r--r--media/java/android/media/AudioPortEventHandler.java172
-rw-r--r--media/java/android/media/AudioService.java68
-rw-r--r--media/java/android/media/AudioSystem.java9
-rw-r--r--media/java/android/media/MediaCodec.java32
-rw-r--r--media/java/android/media/MediaMetadata.java11
-rw-r--r--media/java/android/media/RemoteControlClient.java12
-rw-r--r--media/java/android/media/session/ISession.aidl4
-rw-r--r--media/java/android/media/session/ISessionCallback.aidl4
-rw-r--r--media/java/android/media/session/ISessionController.aidl2
-rw-r--r--media/java/android/media/session/ISessionManager.aidl1
-rw-r--r--media/java/android/media/session/MediaController.java270
-rw-r--r--media/java/android/media/session/MediaSession.java492
-rw-r--r--media/java/android/media/session/MediaSessionInfo.java2
-rw-r--r--media/java/android/media/session/MediaSessionLegacyHelper.java31
-rw-r--r--media/java/android/media/session/MediaSessionManager.java19
-rw-r--r--media/java/android/media/session/MediaSessionToken.java7
-rw-r--r--media/java/android/media/session/PlaybackState.java132
-rw-r--r--media/java/android/media/session/RemoteVolumeProvider.java62
-rw-r--r--media/java/android/media/session/TransportController.java343
-rw-r--r--media/java/android/media/session/TransportPerformer.java351
-rw-r--r--media/jni/android_mtp_MtpDatabase.cpp8
-rw-r--r--media/lib/signer/Android.mk3
-rw-r--r--media/lib/signer/com.android.mediadrm.signer.xml4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java17
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java61
-rw-r--r--packages/Keyguard/Android.mk6
-rw-r--r--packages/Keyguard/res/values/dimens.xml2
-rw-r--r--packages/Keyguard/src/com/android/keyguard/AppearAnimationCreator.java29
-rw-r--r--packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java126
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java84
-rw-r--r--packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java278
-rw-r--r--packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java109
-rw-r--r--packages/Keyguard/src/com/android/keyguard/analytics/Session.java220
-rw-r--r--packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto102
-rw-r--r--packages/SystemUI/res/drawable/heads_up_scrim.xml25
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_airplane_off.xml15
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_airplane_on.xml8
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml6
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_bluetooth_connecting.xml (renamed from packages/SystemUI/res/drawable/ic_qs_zen_off.xml)8
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml10
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml6
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_cast_off.xml10
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_cast_on.xml6
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_zen_on.xml6
-rw-r--r--packages/SystemUI/res/drawable/ic_vol_zen_off.xml6
-rw-r--r--packages/SystemUI/res/drawable/ic_vol_zen_on.xml6
-rw-r--r--packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml10
-rw-r--r--packages/SystemUI/res/layout/heads_up.xml9
-rw-r--r--packages/SystemUI/res/layout/recents_empty.xml5
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml26
-rw-r--r--packages/SystemUI/res/layout/volume_panel_item.xml50
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/colors.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml16
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Constants.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java101
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java172
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java139
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java2
-rw-r--r--policy/src/com/android/internal/policy/impl/GlobalActions.java109
-rw-r--r--policy/src/com/android/internal/policy/impl/GlobalKeyManager.java18
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindow.java61
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindowManager.java22
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java4
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java4
-rw-r--r--services/Android.mk1
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java78
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java337
-rw-r--r--services/core/java/com/android/server/MountService.java12
-rw-r--r--services/core/java/com/android/server/WiredAccessoryManager.java21
-rwxr-xr-xservices/core/java/com/android/server/am/ActiveServices.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityStack.java4
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java5
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java67
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiConstants.java47
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java117
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiUtils.java74
-rw-r--r--services/core/java/com/android/server/hdmi/RequestArcAction.java21
-rw-r--r--services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java13
-rw-r--r--services/core/java/com/android/server/hdmi/SystemAudioAction.java192
-rw-r--r--services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java69
-rw-r--r--services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java50
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java169
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java70
-rw-r--r--services/core/java/com/android/server/media/MediaSessionStack.java40
-rw-r--r--services/core/java/com/android/server/notification/NotificationComparator.java5
-rw-r--r--services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java253
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java204
-rw-r--r--services/core/java/com/android/server/notification/NotificationSignalExtractor.java4
-rw-r--r--services/core/java/com/android/server/notification/NotificationUsageStats.java2
-rw-r--r--services/core/java/com/android/server/notification/RankingReconsideration.java2
-rw-r--r--services/core/java/com/android/server/notification/ValidateNotificationPeople.java19
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java52
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java587
-rwxr-xr-xservices/core/java/com/android/server/pm/PackageManagerService.java190
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java119
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java16
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java36
-rw-r--r--services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java24
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java6
-rw-r--r--services/core/jni/com_android_server_AssetAtlasService.cpp12
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java41
-rw-r--r--services/java/com/android/server/SystemServer.java20
-rw-r--r--services/restrictions/Android.mk10
-rw-r--r--services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java171
-rw-r--r--services/tests/servicestests/AndroidManifest.xml12
-rw-r--r--services/tests/servicestests/res/xml/device_admin_sample.xml29
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java135
-rw-r--r--telecomm/java/android/telecomm/CallService.java63
-rw-r--r--telecomm/java/android/telecomm/CallServiceAdapter.java56
-rw-r--r--telecomm/java/android/telecomm/Connection.java52
-rw-r--r--telecomm/java/android/telecomm/ConnectionService.java44
-rw-r--r--telecomm/java/android/telecomm/InCallAdapter.java28
-rw-r--r--telecomm/java/android/telecomm/InCallCall.java68
-rw-r--r--telecomm/java/android/telecomm/InCallService.java17
-rw-r--r--telecomm/java/com/android/internal/telecomm/ICallService.aidl4
-rw-r--r--telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl8
-rw-r--r--telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl4
-rw-r--r--telecomm/java/com/android/internal/telecomm/IInCallService.aidl2
-rw-r--r--telecomm/java/com/android/internal/telecomm/ITelecommService.aidl9
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java10
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl22
-rw-r--r--test-runner/src/android/test/MoreAsserts.java27
-rw-r--r--test-runner/src/android/test/mock/MockContext.java5
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java18
-rw-r--r--tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java20
-rw-r--r--tests/OneMedia/src/com/android/onemedia/PlayerController.java13
-rw-r--r--tests/OneMedia/src/com/android/onemedia/PlayerService.java6
-rw-r--r--tests/OneMedia/src/com/android/onemedia/PlayerSession.java43
-rw-r--r--tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java16
-rw-r--r--tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java1
-rw-r--r--tests/TtsTests/src/com/android/speech/tts/AbstractTtsSemioticClassTest.java189
-rw-r--r--tests/TtsTests/src/com/android/speech/tts/AbstractTtsTest.java106
-rw-r--r--tests/TtsTests/src/com/android/speech/tts/MarkupTest.java510
-rw-r--r--tests/TtsTests/src/com/android/speech/tts/TtsCardinalTest.java119
-rw-r--r--tests/TtsTests/src/com/android/speech/tts/TtsTextTest.java74
-rw-r--r--tests/TtsTests/src/com/android/speech/tts/UtteranceTest.java248
-rw-r--r--tests/VoiceInteraction/AndroidManifest.xml3
-rw-r--r--tests/VoiceInteraction/res/layout/test_interaction.xml12
-rw-r--r--tests/VoiceInteraction/res/layout/voice_interaction_session.xml65
-rw-r--r--tests/VoiceInteraction/res/values/strings.xml5
-rw-r--r--tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java57
-rw-r--r--tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java34
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java28
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java6
-rw-r--r--wifi/java/android/net/wifi/ScanResult.java41
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java64
-rw-r--r--wifi/java/android/net/wifi/WifiScanner.java90
-rw-r--r--wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java403
-rw-r--r--wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java24
-rw-r--r--wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java450
-rw-r--r--wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java32
-rw-r--r--wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java24
408 files changed, 17398 insertions, 11454 deletions
diff --git a/Android.mk b/Android.mk
index c58ef53..2f3a990 100644
--- a/Android.mk
+++ b/Android.mk
@@ -118,6 +118,7 @@ LOCAL_SRC_FILES += \
core/java/android/content/IIntentReceiver.aidl \
core/java/android/content/IIntentSender.aidl \
core/java/android/content/IOnPrimaryClipChangedListener.aidl \
+ core/java/android/content/IRestrictionsManager.aidl \
core/java/android/content/ISyncAdapter.aidl \
core/java/android/content/ISyncContext.aidl \
core/java/android/content/ISyncServiceAdapter.aidl \
diff --git a/api/current.txt b/api/current.txt
index 02563372..b059841 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1017,6 +1017,7 @@ package android {
field public static final int selectAllOnFocus = 16843102; // 0x101015e
field public static final int selectable = 16843238; // 0x10101e6
field public static final int selectableItemBackground = 16843534; // 0x101030e
+ field public static final int selectableItemBackgroundBorderless = 16843871; // 0x101045f
field public static final int selectedDateVerticalBar = 16843591; // 0x1010347
field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342
field public static final int sessionService = 16843841; // 0x1010441
@@ -1867,28 +1868,28 @@ package android {
field public static final int TextAppearance_Medium = 16973892; // 0x1030044
field public static final int TextAppearance_Medium_Inverse = 16973893; // 0x1030045
field public static final int TextAppearance_Quantum = 16974348; // 0x103020c
- field public static final int TextAppearance_Quantum_Body1 = 16974543; // 0x10302cf
- field public static final int TextAppearance_Quantum_Body2 = 16974542; // 0x10302ce
- field public static final int TextAppearance_Quantum_Button = 16974546; // 0x10302d2
- field public static final int TextAppearance_Quantum_Caption = 16974544; // 0x10302d0
+ field public static final int TextAppearance_Quantum_Body1 = 16974545; // 0x10302d1
+ field public static final int TextAppearance_Quantum_Body2 = 16974544; // 0x10302d0
+ field public static final int TextAppearance_Quantum_Button = 16974548; // 0x10302d4
+ field public static final int TextAppearance_Quantum_Caption = 16974546; // 0x10302d2
field public static final int TextAppearance_Quantum_DialogWindowTitle = 16974349; // 0x103020d
- field public static final int TextAppearance_Quantum_Display1 = 16974538; // 0x10302ca
- field public static final int TextAppearance_Quantum_Display2 = 16974537; // 0x10302c9
- field public static final int TextAppearance_Quantum_Display3 = 16974536; // 0x10302c8
- field public static final int TextAppearance_Quantum_Display4 = 16974535; // 0x10302c7
- field public static final int TextAppearance_Quantum_Headline = 16974539; // 0x10302cb
+ field public static final int TextAppearance_Quantum_Display1 = 16974540; // 0x10302cc
+ field public static final int TextAppearance_Quantum_Display2 = 16974539; // 0x10302cb
+ field public static final int TextAppearance_Quantum_Display3 = 16974538; // 0x10302ca
+ field public static final int TextAppearance_Quantum_Display4 = 16974537; // 0x10302c9
+ field public static final int TextAppearance_Quantum_Headline = 16974541; // 0x10302cd
field public static final int TextAppearance_Quantum_Inverse = 16974350; // 0x103020e
field public static final int TextAppearance_Quantum_Large = 16974351; // 0x103020f
field public static final int TextAppearance_Quantum_Large_Inverse = 16974352; // 0x1030210
field public static final int TextAppearance_Quantum_Medium = 16974353; // 0x1030211
field public static final int TextAppearance_Quantum_Medium_Inverse = 16974354; // 0x1030212
- field public static final int TextAppearance_Quantum_Menu = 16974545; // 0x10302d1
+ field public static final int TextAppearance_Quantum_Menu = 16974547; // 0x10302d3
field public static final int TextAppearance_Quantum_SearchResult_Subtitle = 16974355; // 0x1030213
field public static final int TextAppearance_Quantum_SearchResult_Title = 16974356; // 0x1030214
field public static final int TextAppearance_Quantum_Small = 16974357; // 0x1030215
field public static final int TextAppearance_Quantum_Small_Inverse = 16974358; // 0x1030216
- field public static final int TextAppearance_Quantum_Subhead = 16974541; // 0x10302cd
- field public static final int TextAppearance_Quantum_Title = 16974540; // 0x10302cc
+ field public static final int TextAppearance_Quantum_Subhead = 16974543; // 0x10302cf
+ field public static final int TextAppearance_Quantum_Title = 16974542; // 0x10302ce
field public static final int TextAppearance_Quantum_Widget = 16974360; // 0x1030218
field public static final int TextAppearance_Quantum_Widget_ActionBar_Menu = 16974361; // 0x1030219
field public static final int TextAppearance_Quantum_Widget_ActionBar_Subtitle = 16974362; // 0x103021a
@@ -1935,11 +1936,11 @@ package android {
field public static final int TextAppearance_Widget_TextView_SpinnerItem = 16973906; // 0x1030052
field public static final int TextAppearance_WindowTitle = 16973907; // 0x1030053
field public static final int Theme = 16973829; // 0x1030005
- field public static final int ThemeOverlay = 16974410; // 0x103024a
- field public static final int ThemeOverlay_Quantum = 16974411; // 0x103024b
- field public static final int ThemeOverlay_Quantum_ActionBarWidget = 16974414; // 0x103024e
- field public static final int ThemeOverlay_Quantum_Dark = 16974413; // 0x103024d
- field public static final int ThemeOverlay_Quantum_Light = 16974412; // 0x103024c
+ field public static final int ThemeOverlay = 16974412; // 0x103024c
+ field public static final int ThemeOverlay_Quantum = 16974413; // 0x103024d
+ field public static final int ThemeOverlay_Quantum_ActionBarWidget = 16974416; // 0x1030250
+ field public static final int ThemeOverlay_Quantum_Dark = 16974415; // 0x103024f
+ field public static final int ThemeOverlay_Quantum_Light = 16974414; // 0x103024e
field public static final int Theme_Black = 16973832; // 0x1030008
field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009
field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a
@@ -2019,26 +2020,28 @@ package android {
field public static final int Theme_Quantum_Dialog_NoActionBar = 16974385; // 0x1030231
field public static final int Theme_Quantum_Dialog_NoActionBar_MinWidth = 16974386; // 0x1030232
field public static final int Theme_Quantum_InputMethod = 16974389; // 0x1030235
- field public static final int Theme_Quantum_Light = 16974397; // 0x103023d
- field public static final int Theme_Quantum_Light_DarkActionBar = 16974398; // 0x103023e
- field public static final int Theme_Quantum_Light_Dialog = 16974399; // 0x103023f
- field public static final int Theme_Quantum_Light_DialogWhenLarge = 16974403; // 0x1030243
- field public static final int Theme_Quantum_Light_DialogWhenLarge_NoActionBar = 16974404; // 0x1030244
- field public static final int Theme_Quantum_Light_Dialog_MinWidth = 16974400; // 0x1030240
- field public static final int Theme_Quantum_Light_Dialog_NoActionBar = 16974401; // 0x1030241
- field public static final int Theme_Quantum_Light_Dialog_NoActionBar_MinWidth = 16974402; // 0x1030242
- field public static final int Theme_Quantum_Light_NoActionBar = 16974405; // 0x1030245
- field public static final int Theme_Quantum_Light_NoActionBar_Fullscreen = 16974406; // 0x1030246
- field public static final int Theme_Quantum_Light_NoActionBar_Overscan = 16974407; // 0x1030247
- field public static final int Theme_Quantum_Light_NoActionBar_TranslucentDecor = 16974408; // 0x1030248
- field public static final int Theme_Quantum_Light_Panel = 16974409; // 0x1030249
+ field public static final int Theme_Quantum_Light = 16974398; // 0x103023e
+ field public static final int Theme_Quantum_Light_DarkActionBar = 16974399; // 0x103023f
+ field public static final int Theme_Quantum_Light_Dialog = 16974400; // 0x1030240
+ field public static final int Theme_Quantum_Light_DialogWhenLarge = 16974404; // 0x1030244
+ field public static final int Theme_Quantum_Light_DialogWhenLarge_NoActionBar = 16974405; // 0x1030245
+ field public static final int Theme_Quantum_Light_Dialog_MinWidth = 16974401; // 0x1030241
+ field public static final int Theme_Quantum_Light_Dialog_NoActionBar = 16974402; // 0x1030242
+ field public static final int Theme_Quantum_Light_Dialog_NoActionBar_MinWidth = 16974403; // 0x1030243
+ field public static final int Theme_Quantum_Light_NoActionBar = 16974406; // 0x1030246
+ field public static final int Theme_Quantum_Light_NoActionBar_Fullscreen = 16974407; // 0x1030247
+ field public static final int Theme_Quantum_Light_NoActionBar_Overscan = 16974408; // 0x1030248
+ field public static final int Theme_Quantum_Light_NoActionBar_TranslucentDecor = 16974409; // 0x1030249
+ field public static final int Theme_Quantum_Light_Panel = 16974410; // 0x103024a
+ field public static final int Theme_Quantum_Light_Voice = 16974411; // 0x103024b
field public static final int Theme_Quantum_NoActionBar = 16974390; // 0x1030236
field public static final int Theme_Quantum_NoActionBar_Fullscreen = 16974391; // 0x1030237
field public static final int Theme_Quantum_NoActionBar_Overscan = 16974392; // 0x1030238
field public static final int Theme_Quantum_NoActionBar_TranslucentDecor = 16974393; // 0x1030239
field public static final int Theme_Quantum_Panel = 16974394; // 0x103023a
- field public static final int Theme_Quantum_Wallpaper = 16974395; // 0x103023b
- field public static final int Theme_Quantum_Wallpaper_NoTitleBar = 16974396; // 0x103023c
+ field public static final int Theme_Quantum_Voice = 16974395; // 0x103023b
+ field public static final int Theme_Quantum_Wallpaper = 16974396; // 0x103023c
+ field public static final int Theme_Quantum_Wallpaper_NoTitleBar = 16974397; // 0x103023d
field public static final int Theme_Translucent = 16973839; // 0x103000f
field public static final int Theme_Translucent_NoTitleBar = 16973840; // 0x1030010
field public static final int Theme_Translucent_NoTitleBar_Fullscreen = 16973841; // 0x1030011
@@ -2327,126 +2330,126 @@ package android {
field public static final int Widget_ProgressBar_Large_Inverse = 16973916; // 0x103005c
field public static final int Widget_ProgressBar_Small = 16973854; // 0x103001e
field public static final int Widget_ProgressBar_Small_Inverse = 16973917; // 0x103005d
- field public static final int Widget_Quantum = 16974415; // 0x103024f
- field public static final int Widget_Quantum_ActionBar = 16974416; // 0x1030250
- field public static final int Widget_Quantum_ActionBar_Solid = 16974417; // 0x1030251
- field public static final int Widget_Quantum_ActionBar_TabBar = 16974418; // 0x1030252
- field public static final int Widget_Quantum_ActionBar_TabText = 16974419; // 0x1030253
- field public static final int Widget_Quantum_ActionBar_TabView = 16974420; // 0x1030254
- field public static final int Widget_Quantum_ActionButton = 16974421; // 0x1030255
- field public static final int Widget_Quantum_ActionButton_CloseMode = 16974422; // 0x1030256
- field public static final int Widget_Quantum_ActionButton_Overflow = 16974423; // 0x1030257
- field public static final int Widget_Quantum_ActionMode = 16974424; // 0x1030258
- field public static final int Widget_Quantum_AutoCompleteTextView = 16974425; // 0x1030259
- field public static final int Widget_Quantum_Button = 16974426; // 0x103025a
- field public static final int Widget_Quantum_ButtonBar = 16974432; // 0x1030260
- field public static final int Widget_Quantum_ButtonBar_AlertDialog = 16974433; // 0x1030261
- field public static final int Widget_Quantum_Button_Borderless = 16974427; // 0x103025b
- field public static final int Widget_Quantum_Button_Borderless_Small = 16974428; // 0x103025c
- field public static final int Widget_Quantum_Button_Inset = 16974429; // 0x103025d
- field public static final int Widget_Quantum_Button_Small = 16974430; // 0x103025e
- field public static final int Widget_Quantum_Button_Toggle = 16974431; // 0x103025f
- field public static final int Widget_Quantum_CalendarView = 16974434; // 0x1030262
- field public static final int Widget_Quantum_CheckedTextView = 16974435; // 0x1030263
- field public static final int Widget_Quantum_CompoundButton_CheckBox = 16974436; // 0x1030264
- field public static final int Widget_Quantum_CompoundButton_RadioButton = 16974437; // 0x1030265
- field public static final int Widget_Quantum_CompoundButton_Star = 16974438; // 0x1030266
- field public static final int Widget_Quantum_DatePicker = 16974439; // 0x1030267
- field public static final int Widget_Quantum_DropDownItem = 16974440; // 0x1030268
- field public static final int Widget_Quantum_DropDownItem_Spinner = 16974441; // 0x1030269
- field public static final int Widget_Quantum_EditText = 16974442; // 0x103026a
- field public static final int Widget_Quantum_ExpandableListView = 16974443; // 0x103026b
- field public static final int Widget_Quantum_FastScroll = 16974444; // 0x103026c
- field public static final int Widget_Quantum_GridView = 16974445; // 0x103026d
- field public static final int Widget_Quantum_HorizontalScrollView = 16974446; // 0x103026e
- field public static final int Widget_Quantum_ImageButton = 16974447; // 0x103026f
- field public static final int Widget_Quantum_Light = 16974474; // 0x103028a
- field public static final int Widget_Quantum_Light_ActionBar = 16974475; // 0x103028b
- field public static final int Widget_Quantum_Light_ActionBar_Solid = 16974476; // 0x103028c
- field public static final int Widget_Quantum_Light_ActionBar_TabBar = 16974477; // 0x103028d
- field public static final int Widget_Quantum_Light_ActionBar_TabText = 16974478; // 0x103028e
- field public static final int Widget_Quantum_Light_ActionBar_TabView = 16974479; // 0x103028f
- field public static final int Widget_Quantum_Light_ActionButton = 16974480; // 0x1030290
- field public static final int Widget_Quantum_Light_ActionButton_CloseMode = 16974481; // 0x1030291
- field public static final int Widget_Quantum_Light_ActionButton_Overflow = 16974482; // 0x1030292
- field public static final int Widget_Quantum_Light_ActionMode = 16974483; // 0x1030293
- field public static final int Widget_Quantum_Light_AutoCompleteTextView = 16974484; // 0x1030294
- field public static final int Widget_Quantum_Light_Button = 16974485; // 0x1030295
- field public static final int Widget_Quantum_Light_ButtonBar = 16974491; // 0x103029b
- field public static final int Widget_Quantum_Light_ButtonBar_AlertDialog = 16974492; // 0x103029c
- field public static final int Widget_Quantum_Light_Button_Borderless = 16974486; // 0x1030296
- field public static final int Widget_Quantum_Light_Button_Borderless_Small = 16974487; // 0x1030297
- field public static final int Widget_Quantum_Light_Button_Inset = 16974488; // 0x1030298
- field public static final int Widget_Quantum_Light_Button_Small = 16974489; // 0x1030299
- field public static final int Widget_Quantum_Light_Button_Toggle = 16974490; // 0x103029a
- field public static final int Widget_Quantum_Light_CalendarView = 16974493; // 0x103029d
- field public static final int Widget_Quantum_Light_CheckedTextView = 16974494; // 0x103029e
- field public static final int Widget_Quantum_Light_CompoundButton_CheckBox = 16974495; // 0x103029f
- field public static final int Widget_Quantum_Light_CompoundButton_RadioButton = 16974496; // 0x10302a0
- field public static final int Widget_Quantum_Light_CompoundButton_Star = 16974497; // 0x10302a1
- field public static final int Widget_Quantum_Light_DropDownItem = 16974498; // 0x10302a2
- field public static final int Widget_Quantum_Light_DropDownItem_Spinner = 16974499; // 0x10302a3
- field public static final int Widget_Quantum_Light_EditText = 16974500; // 0x10302a4
- field public static final int Widget_Quantum_Light_ExpandableListView = 16974501; // 0x10302a5
- field public static final int Widget_Quantum_Light_FastScroll = 16974502; // 0x10302a6
- field public static final int Widget_Quantum_Light_GridView = 16974503; // 0x10302a7
- field public static final int Widget_Quantum_Light_HorizontalScrollView = 16974504; // 0x10302a8
- field public static final int Widget_Quantum_Light_ImageButton = 16974505; // 0x10302a9
- field public static final int Widget_Quantum_Light_ListPopupWindow = 16974506; // 0x10302aa
- field public static final int Widget_Quantum_Light_ListView = 16974507; // 0x10302ab
- field public static final int Widget_Quantum_Light_ListView_DropDown = 16974508; // 0x10302ac
- field public static final int Widget_Quantum_Light_MediaRouteButton = 16974509; // 0x10302ad
- field public static final int Widget_Quantum_Light_PopupMenu = 16974510; // 0x10302ae
- field public static final int Widget_Quantum_Light_PopupMenu_Overflow = 16974511; // 0x10302af
- field public static final int Widget_Quantum_Light_PopupWindow = 16974512; // 0x10302b0
- field public static final int Widget_Quantum_Light_ProgressBar = 16974513; // 0x10302b1
- field public static final int Widget_Quantum_Light_ProgressBar_Horizontal = 16974514; // 0x10302b2
- field public static final int Widget_Quantum_Light_ProgressBar_Inverse = 16974515; // 0x10302b3
- field public static final int Widget_Quantum_Light_ProgressBar_Large = 16974516; // 0x10302b4
- field public static final int Widget_Quantum_Light_ProgressBar_Large_Inverse = 16974517; // 0x10302b5
- field public static final int Widget_Quantum_Light_ProgressBar_Small = 16974518; // 0x10302b6
- field public static final int Widget_Quantum_Light_ProgressBar_Small_Inverse = 16974519; // 0x10302b7
- field public static final int Widget_Quantum_Light_ProgressBar_Small_Title = 16974520; // 0x10302b8
- field public static final int Widget_Quantum_Light_RatingBar = 16974521; // 0x10302b9
- field public static final int Widget_Quantum_Light_RatingBar_Indicator = 16974522; // 0x10302ba
- field public static final int Widget_Quantum_Light_RatingBar_Small = 16974523; // 0x10302bb
- field public static final int Widget_Quantum_Light_ScrollView = 16974524; // 0x10302bc
- field public static final int Widget_Quantum_Light_SeekBar = 16974525; // 0x10302bd
- field public static final int Widget_Quantum_Light_SegmentedButton = 16974526; // 0x10302be
- field public static final int Widget_Quantum_Light_Spinner = 16974528; // 0x10302c0
- field public static final int Widget_Quantum_Light_StackView = 16974527; // 0x10302bf
- field public static final int Widget_Quantum_Light_Tab = 16974529; // 0x10302c1
- field public static final int Widget_Quantum_Light_TabWidget = 16974530; // 0x10302c2
- field public static final int Widget_Quantum_Light_TextView = 16974531; // 0x10302c3
- field public static final int Widget_Quantum_Light_TextView_SpinnerItem = 16974532; // 0x10302c4
- field public static final int Widget_Quantum_Light_WebTextView = 16974533; // 0x10302c5
- field public static final int Widget_Quantum_Light_WebView = 16974534; // 0x10302c6
- field public static final int Widget_Quantum_ListPopupWindow = 16974448; // 0x1030270
- field public static final int Widget_Quantum_ListView = 16974449; // 0x1030271
- field public static final int Widget_Quantum_ListView_DropDown = 16974450; // 0x1030272
- field public static final int Widget_Quantum_MediaRouteButton = 16974451; // 0x1030273
- field public static final int Widget_Quantum_PopupMenu = 16974452; // 0x1030274
- field public static final int Widget_Quantum_PopupMenu_Overflow = 16974453; // 0x1030275
- field public static final int Widget_Quantum_PopupWindow = 16974454; // 0x1030276
- field public static final int Widget_Quantum_ProgressBar = 16974455; // 0x1030277
- field public static final int Widget_Quantum_ProgressBar_Horizontal = 16974456; // 0x1030278
- field public static final int Widget_Quantum_ProgressBar_Large = 16974457; // 0x1030279
- field public static final int Widget_Quantum_ProgressBar_Small = 16974458; // 0x103027a
- field public static final int Widget_Quantum_ProgressBar_Small_Title = 16974459; // 0x103027b
- field public static final int Widget_Quantum_RatingBar = 16974460; // 0x103027c
- field public static final int Widget_Quantum_RatingBar_Indicator = 16974461; // 0x103027d
- field public static final int Widget_Quantum_RatingBar_Small = 16974462; // 0x103027e
- field public static final int Widget_Quantum_ScrollView = 16974463; // 0x103027f
- field public static final int Widget_Quantum_SeekBar = 16974464; // 0x1030280
- field public static final int Widget_Quantum_SegmentedButton = 16974465; // 0x1030281
- field public static final int Widget_Quantum_Spinner = 16974467; // 0x1030283
- field public static final int Widget_Quantum_StackView = 16974466; // 0x1030282
- field public static final int Widget_Quantum_Tab = 16974468; // 0x1030284
- field public static final int Widget_Quantum_TabWidget = 16974469; // 0x1030285
- field public static final int Widget_Quantum_TextView = 16974470; // 0x1030286
- field public static final int Widget_Quantum_TextView_SpinnerItem = 16974471; // 0x1030287
- field public static final int Widget_Quantum_WebTextView = 16974472; // 0x1030288
- field public static final int Widget_Quantum_WebView = 16974473; // 0x1030289
+ field public static final int Widget_Quantum = 16974417; // 0x1030251
+ field public static final int Widget_Quantum_ActionBar = 16974418; // 0x1030252
+ field public static final int Widget_Quantum_ActionBar_Solid = 16974419; // 0x1030253
+ field public static final int Widget_Quantum_ActionBar_TabBar = 16974420; // 0x1030254
+ field public static final int Widget_Quantum_ActionBar_TabText = 16974421; // 0x1030255
+ field public static final int Widget_Quantum_ActionBar_TabView = 16974422; // 0x1030256
+ field public static final int Widget_Quantum_ActionButton = 16974423; // 0x1030257
+ field public static final int Widget_Quantum_ActionButton_CloseMode = 16974424; // 0x1030258
+ field public static final int Widget_Quantum_ActionButton_Overflow = 16974425; // 0x1030259
+ field public static final int Widget_Quantum_ActionMode = 16974426; // 0x103025a
+ field public static final int Widget_Quantum_AutoCompleteTextView = 16974427; // 0x103025b
+ field public static final int Widget_Quantum_Button = 16974428; // 0x103025c
+ field public static final int Widget_Quantum_ButtonBar = 16974434; // 0x1030262
+ field public static final int Widget_Quantum_ButtonBar_AlertDialog = 16974435; // 0x1030263
+ field public static final int Widget_Quantum_Button_Borderless = 16974429; // 0x103025d
+ field public static final int Widget_Quantum_Button_Borderless_Small = 16974430; // 0x103025e
+ field public static final int Widget_Quantum_Button_Inset = 16974431; // 0x103025f
+ field public static final int Widget_Quantum_Button_Small = 16974432; // 0x1030260
+ field public static final int Widget_Quantum_Button_Toggle = 16974433; // 0x1030261
+ field public static final int Widget_Quantum_CalendarView = 16974436; // 0x1030264
+ field public static final int Widget_Quantum_CheckedTextView = 16974437; // 0x1030265
+ field public static final int Widget_Quantum_CompoundButton_CheckBox = 16974438; // 0x1030266
+ field public static final int Widget_Quantum_CompoundButton_RadioButton = 16974439; // 0x1030267
+ field public static final int Widget_Quantum_CompoundButton_Star = 16974440; // 0x1030268
+ field public static final int Widget_Quantum_DatePicker = 16974441; // 0x1030269
+ field public static final int Widget_Quantum_DropDownItem = 16974442; // 0x103026a
+ field public static final int Widget_Quantum_DropDownItem_Spinner = 16974443; // 0x103026b
+ field public static final int Widget_Quantum_EditText = 16974444; // 0x103026c
+ field public static final int Widget_Quantum_ExpandableListView = 16974445; // 0x103026d
+ field public static final int Widget_Quantum_FastScroll = 16974446; // 0x103026e
+ field public static final int Widget_Quantum_GridView = 16974447; // 0x103026f
+ field public static final int Widget_Quantum_HorizontalScrollView = 16974448; // 0x1030270
+ field public static final int Widget_Quantum_ImageButton = 16974449; // 0x1030271
+ field public static final int Widget_Quantum_Light = 16974476; // 0x103028c
+ field public static final int Widget_Quantum_Light_ActionBar = 16974477; // 0x103028d
+ field public static final int Widget_Quantum_Light_ActionBar_Solid = 16974478; // 0x103028e
+ field public static final int Widget_Quantum_Light_ActionBar_TabBar = 16974479; // 0x103028f
+ field public static final int Widget_Quantum_Light_ActionBar_TabText = 16974480; // 0x1030290
+ field public static final int Widget_Quantum_Light_ActionBar_TabView = 16974481; // 0x1030291
+ field public static final int Widget_Quantum_Light_ActionButton = 16974482; // 0x1030292
+ field public static final int Widget_Quantum_Light_ActionButton_CloseMode = 16974483; // 0x1030293
+ field public static final int Widget_Quantum_Light_ActionButton_Overflow = 16974484; // 0x1030294
+ field public static final int Widget_Quantum_Light_ActionMode = 16974485; // 0x1030295
+ field public static final int Widget_Quantum_Light_AutoCompleteTextView = 16974486; // 0x1030296
+ field public static final int Widget_Quantum_Light_Button = 16974487; // 0x1030297
+ field public static final int Widget_Quantum_Light_ButtonBar = 16974493; // 0x103029d
+ field public static final int Widget_Quantum_Light_ButtonBar_AlertDialog = 16974494; // 0x103029e
+ field public static final int Widget_Quantum_Light_Button_Borderless = 16974488; // 0x1030298
+ field public static final int Widget_Quantum_Light_Button_Borderless_Small = 16974489; // 0x1030299
+ field public static final int Widget_Quantum_Light_Button_Inset = 16974490; // 0x103029a
+ field public static final int Widget_Quantum_Light_Button_Small = 16974491; // 0x103029b
+ field public static final int Widget_Quantum_Light_Button_Toggle = 16974492; // 0x103029c
+ field public static final int Widget_Quantum_Light_CalendarView = 16974495; // 0x103029f
+ field public static final int Widget_Quantum_Light_CheckedTextView = 16974496; // 0x10302a0
+ field public static final int Widget_Quantum_Light_CompoundButton_CheckBox = 16974497; // 0x10302a1
+ field public static final int Widget_Quantum_Light_CompoundButton_RadioButton = 16974498; // 0x10302a2
+ field public static final int Widget_Quantum_Light_CompoundButton_Star = 16974499; // 0x10302a3
+ field public static final int Widget_Quantum_Light_DropDownItem = 16974500; // 0x10302a4
+ field public static final int Widget_Quantum_Light_DropDownItem_Spinner = 16974501; // 0x10302a5
+ field public static final int Widget_Quantum_Light_EditText = 16974502; // 0x10302a6
+ field public static final int Widget_Quantum_Light_ExpandableListView = 16974503; // 0x10302a7
+ field public static final int Widget_Quantum_Light_FastScroll = 16974504; // 0x10302a8
+ field public static final int Widget_Quantum_Light_GridView = 16974505; // 0x10302a9
+ field public static final int Widget_Quantum_Light_HorizontalScrollView = 16974506; // 0x10302aa
+ field public static final int Widget_Quantum_Light_ImageButton = 16974507; // 0x10302ab
+ field public static final int Widget_Quantum_Light_ListPopupWindow = 16974508; // 0x10302ac
+ field public static final int Widget_Quantum_Light_ListView = 16974509; // 0x10302ad
+ field public static final int Widget_Quantum_Light_ListView_DropDown = 16974510; // 0x10302ae
+ field public static final int Widget_Quantum_Light_MediaRouteButton = 16974511; // 0x10302af
+ field public static final int Widget_Quantum_Light_PopupMenu = 16974512; // 0x10302b0
+ field public static final int Widget_Quantum_Light_PopupMenu_Overflow = 16974513; // 0x10302b1
+ field public static final int Widget_Quantum_Light_PopupWindow = 16974514; // 0x10302b2
+ field public static final int Widget_Quantum_Light_ProgressBar = 16974515; // 0x10302b3
+ field public static final int Widget_Quantum_Light_ProgressBar_Horizontal = 16974516; // 0x10302b4
+ field public static final int Widget_Quantum_Light_ProgressBar_Inverse = 16974517; // 0x10302b5
+ field public static final int Widget_Quantum_Light_ProgressBar_Large = 16974518; // 0x10302b6
+ field public static final int Widget_Quantum_Light_ProgressBar_Large_Inverse = 16974519; // 0x10302b7
+ field public static final int Widget_Quantum_Light_ProgressBar_Small = 16974520; // 0x10302b8
+ field public static final int Widget_Quantum_Light_ProgressBar_Small_Inverse = 16974521; // 0x10302b9
+ field public static final int Widget_Quantum_Light_ProgressBar_Small_Title = 16974522; // 0x10302ba
+ field public static final int Widget_Quantum_Light_RatingBar = 16974523; // 0x10302bb
+ field public static final int Widget_Quantum_Light_RatingBar_Indicator = 16974524; // 0x10302bc
+ field public static final int Widget_Quantum_Light_RatingBar_Small = 16974525; // 0x10302bd
+ field public static final int Widget_Quantum_Light_ScrollView = 16974526; // 0x10302be
+ field public static final int Widget_Quantum_Light_SeekBar = 16974527; // 0x10302bf
+ field public static final int Widget_Quantum_Light_SegmentedButton = 16974528; // 0x10302c0
+ field public static final int Widget_Quantum_Light_Spinner = 16974530; // 0x10302c2
+ field public static final int Widget_Quantum_Light_StackView = 16974529; // 0x10302c1
+ field public static final int Widget_Quantum_Light_Tab = 16974531; // 0x10302c3
+ field public static final int Widget_Quantum_Light_TabWidget = 16974532; // 0x10302c4
+ field public static final int Widget_Quantum_Light_TextView = 16974533; // 0x10302c5
+ field public static final int Widget_Quantum_Light_TextView_SpinnerItem = 16974534; // 0x10302c6
+ field public static final int Widget_Quantum_Light_WebTextView = 16974535; // 0x10302c7
+ field public static final int Widget_Quantum_Light_WebView = 16974536; // 0x10302c8
+ field public static final int Widget_Quantum_ListPopupWindow = 16974450; // 0x1030272
+ field public static final int Widget_Quantum_ListView = 16974451; // 0x1030273
+ field public static final int Widget_Quantum_ListView_DropDown = 16974452; // 0x1030274
+ field public static final int Widget_Quantum_MediaRouteButton = 16974453; // 0x1030275
+ field public static final int Widget_Quantum_PopupMenu = 16974454; // 0x1030276
+ field public static final int Widget_Quantum_PopupMenu_Overflow = 16974455; // 0x1030277
+ field public static final int Widget_Quantum_PopupWindow = 16974456; // 0x1030278
+ field public static final int Widget_Quantum_ProgressBar = 16974457; // 0x1030279
+ field public static final int Widget_Quantum_ProgressBar_Horizontal = 16974458; // 0x103027a
+ field public static final int Widget_Quantum_ProgressBar_Large = 16974459; // 0x103027b
+ field public static final int Widget_Quantum_ProgressBar_Small = 16974460; // 0x103027c
+ field public static final int Widget_Quantum_ProgressBar_Small_Title = 16974461; // 0x103027d
+ field public static final int Widget_Quantum_RatingBar = 16974462; // 0x103027e
+ field public static final int Widget_Quantum_RatingBar_Indicator = 16974463; // 0x103027f
+ field public static final int Widget_Quantum_RatingBar_Small = 16974464; // 0x1030280
+ field public static final int Widget_Quantum_ScrollView = 16974465; // 0x1030281
+ field public static final int Widget_Quantum_SeekBar = 16974466; // 0x1030282
+ field public static final int Widget_Quantum_SegmentedButton = 16974467; // 0x1030283
+ field public static final int Widget_Quantum_Spinner = 16974469; // 0x1030285
+ field public static final int Widget_Quantum_StackView = 16974468; // 0x1030284
+ field public static final int Widget_Quantum_Tab = 16974470; // 0x1030286
+ field public static final int Widget_Quantum_TabWidget = 16974471; // 0x1030287
+ field public static final int Widget_Quantum_TextView = 16974472; // 0x1030288
+ field public static final int Widget_Quantum_TextView_SpinnerItem = 16974473; // 0x1030289
+ field public static final int Widget_Quantum_WebTextView = 16974474; // 0x103028a
+ field public static final int Widget_Quantum_WebView = 16974475; // 0x103028b
field public static final int Widget_RatingBar = 16973857; // 0x1030021
field public static final int Widget_ScrollView = 16973869; // 0x103002d
field public static final int Widget_SeekBar = 16973856; // 0x1030020
@@ -3427,11 +3430,11 @@ package android.app {
method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
method public android.os.Debug.MemoryInfo[] getProcessMemoryInfo(int[]);
method public java.util.List<android.app.ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState();
- method public java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
+ method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
method public java.util.List<android.app.ActivityManager.RunningAppProcessInfo> getRunningAppProcesses();
method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
- method public java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
+ method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
method public boolean isLowRamDevice();
method public static boolean isRunningInTestHarness();
method public static boolean isUserAMonkey();
@@ -5031,6 +5034,11 @@ package android.app {
method public boolean[] supportsCommands(java.lang.String[]);
}
+ public static class VoiceInteractor.AbortVoiceRequest extends android.app.VoiceInteractor.Request {
+ ctor public VoiceInteractor.AbortVoiceRequest(java.lang.CharSequence, android.os.Bundle);
+ method public void onAbortResult(android.os.Bundle);
+ }
+
public static class VoiceInteractor.CommandRequest extends android.app.VoiceInteractor.Request {
ctor public VoiceInteractor.CommandRequest(java.lang.String, android.os.Bundle);
method public void onCommandResult(android.os.Bundle);
@@ -5046,7 +5054,9 @@ package android.app {
method public void cancel();
method public android.app.Activity getActivity();
method public android.content.Context getContext();
+ method public void onAttached(android.app.Activity);
method public void onCancel();
+ method public void onDetached();
}
public final class WallpaperInfo implements android.os.Parcelable {
@@ -5220,6 +5230,7 @@ package android.app.admin {
method public void setPasswordMinimumUpperCase(android.content.ComponentName, int);
method public void setPasswordQuality(android.content.ComponentName, int);
method public void setProfileEnabled(android.content.ComponentName);
+ method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void wipeData(int);
@@ -5566,8 +5577,8 @@ package android.bluetooth {
method public boolean disable();
method public boolean enable();
method public java.lang.String getAddress();
- method public android.bluetooth.BluetoothLeAdvertiser getBluetoothLeAdvertiser();
- method public android.bluetooth.BluetoothLeScanner getBluetoothLeScanner();
+ method public android.bluetooth.le.BluetoothLeAdvertiser getBluetoothLeAdvertiser();
+ method public android.bluetooth.le.BluetoothLeScanner getBluetoothLeScanner();
method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
method public java.lang.String getName();
@@ -6210,74 +6221,71 @@ package android.bluetooth {
method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
}
- public final class BluetoothLeAdvertiseScanData {
- ctor public BluetoothLeAdvertiseScanData();
- field public static final int ADVERTISING_DATA = 0; // 0x0
- field public static final int PARSED_SCAN_RECORD = 2; // 0x2
- field public static final int SCAN_RESPONSE_DATA = 1; // 0x1
+ public final class BluetoothManager {
+ method public android.bluetooth.BluetoothAdapter getAdapter();
+ method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
+ method public int getConnectionState(android.bluetooth.BluetoothDevice, int);
+ method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int, int[]);
+ method public android.bluetooth.BluetoothGattServer openGattServer(android.content.Context, android.bluetooth.BluetoothGattServerCallback);
}
- public static abstract class BluetoothLeAdvertiseScanData.AdvertiseBaseData {
- method public int getDataType();
- method public int getManufacturerId();
- method public byte[] getManufacturerSpecificData();
- method public byte[] getServiceData();
- method public android.os.ParcelUuid getServiceDataUuid();
- method public java.util.List<android.os.ParcelUuid> getServiceUuids();
+ public abstract interface BluetoothProfile {
+ method public abstract java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method public abstract int getConnectionState(android.bluetooth.BluetoothDevice);
+ method public abstract java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ field public static final int A2DP = 2; // 0x2
+ field public static final java.lang.String EXTRA_PREVIOUS_STATE = "android.bluetooth.profile.extra.PREVIOUS_STATE";
+ field public static final java.lang.String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
+ field public static final int GATT = 7; // 0x7
+ field public static final int GATT_SERVER = 8; // 0x8
+ field public static final int HEADSET = 1; // 0x1
+ field public static final int HEALTH = 3; // 0x3
+ field public static final int STATE_CONNECTED = 2; // 0x2
+ field public static final int STATE_CONNECTING = 1; // 0x1
+ field public static final int STATE_DISCONNECTED = 0; // 0x0
+ field public static final int STATE_DISCONNECTING = 3; // 0x3
}
- public static final class BluetoothLeAdvertiseScanData.AdvertisementData extends android.bluetooth.BluetoothLeAdvertiseScanData.AdvertiseBaseData implements android.os.Parcelable {
- method public int describeContents();
- method public boolean getIncludeTxPowerLevel();
- method public static android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder newBuilder();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
+ public static abstract interface BluetoothProfile.ServiceListener {
+ method public abstract void onServiceConnected(int, android.bluetooth.BluetoothProfile);
+ method public abstract void onServiceDisconnected(int);
}
- public static final class BluetoothLeAdvertiseScanData.AdvertisementData.Builder {
- ctor public BluetoothLeAdvertiseScanData.AdvertisementData.Builder();
- method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData build();
- method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder dataType(int);
- method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder includeTxPowerLevel(boolean);
- method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder manufacturerData(int, byte[]);
- method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder serviceData(android.os.ParcelUuid, byte[]);
- method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder serviceUuids(java.util.List<android.os.ParcelUuid>);
+ public final class BluetoothServerSocket implements java.io.Closeable {
+ method public android.bluetooth.BluetoothSocket accept() throws java.io.IOException;
+ method public android.bluetooth.BluetoothSocket accept(int) throws java.io.IOException;
+ method public void close() throws java.io.IOException;
}
- public static final class BluetoothLeAdvertiseScanData.ScanRecord extends android.bluetooth.BluetoothLeAdvertiseScanData.AdvertiseBaseData {
- method public int getAdvertiseFlags();
- method public java.lang.String getLocalName();
- method public static android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord.Parser getParser();
- method public int getTxPowerLevel();
+ public final class BluetoothSocket implements java.io.Closeable {
+ method public void close() throws java.io.IOException;
+ method public void connect() throws java.io.IOException;
+ method public java.io.InputStream getInputStream() throws java.io.IOException;
+ method public java.io.OutputStream getOutputStream() throws java.io.IOException;
+ method public android.bluetooth.BluetoothDevice getRemoteDevice();
+ method public boolean isConnected();
}
- public static final class BluetoothLeAdvertiseScanData.ScanRecord.Parser {
- ctor public BluetoothLeAdvertiseScanData.ScanRecord.Parser();
- method public android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord parseFromScanRecord(byte[]);
- }
+}
- public class BluetoothLeAdvertiser {
- method public void startAdvertising(android.bluetooth.BluetoothLeAdvertiser.Settings, android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData, android.bluetooth.BluetoothLeAdvertiser.AdvertiseCallback);
- method public void startAdvertising(android.bluetooth.BluetoothLeAdvertiser.Settings, android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData, android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData, android.bluetooth.BluetoothLeAdvertiser.AdvertiseCallback);
- method public void stopAdvertising(android.bluetooth.BluetoothLeAdvertiser.Settings, android.bluetooth.BluetoothLeAdvertiser.AdvertiseCallback);
- }
+package android.bluetooth.le {
- public static abstract interface BluetoothLeAdvertiser.AdvertiseCallback {
+ public abstract class AdvertiseCallback {
+ ctor public AdvertiseCallback();
method public abstract void onFailure(int);
- method public abstract void onSuccess(android.bluetooth.BluetoothLeAdvertiser.Settings);
- field public static final int ADVERISING_NOT_STARTED = 4; // 0x4
- field public static final int ADVERTISING_ALREADY_STARTED = 3; // 0x3
- field public static final int ADVERTISING_SERVICE_UNKNOWN = 1; // 0x1
- field public static final int CONTROLLER_FAILURE = 5; // 0x5
- field public static final int TOO_MANY_ADVERTISERS = 2; // 0x2
+ method public abstract void onSuccess(android.bluetooth.le.AdvertiseSettings);
+ field public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; // 0x3
+ field public static final int ADVERTISE_FAILED_CONTROLLER_FAILURE = 5; // 0x5
+ field public static final int ADVERTISE_FAILED_NOT_STARTED = 4; // 0x4
+ field public static final int ADVERTISE_FAILED_SERVICE_UNKNOWN = 1; // 0x1
+ field public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; // 0x2
}
- public static final class BluetoothLeAdvertiser.Settings implements android.os.Parcelable {
+ public final class AdvertiseSettings implements android.os.Parcelable {
method public int describeContents();
method public int getMode();
method public int getTxPowerLevel();
method public int getType();
- method public static android.bluetooth.BluetoothLeAdvertiser.Settings.Builder newBuilder();
method public void writeToParcel(android.os.Parcel, int);
field public static final int ADVERTISE_MODE_BALANCED = 1; // 0x1
field public static final int ADVERTISE_MODE_LOW_LATENCY = 2; // 0x2
@@ -6292,14 +6300,57 @@ package android.bluetooth {
field public static final android.os.Parcelable.Creator CREATOR;
}
- public static final class BluetoothLeAdvertiser.Settings.Builder {
- method public android.bluetooth.BluetoothLeAdvertiser.Settings.Builder advertiseMode(int);
- method public android.bluetooth.BluetoothLeAdvertiser.Settings build();
- method public android.bluetooth.BluetoothLeAdvertiser.Settings.Builder txPowerLevel(int);
- method public android.bluetooth.BluetoothLeAdvertiser.Settings.Builder type(int);
+ public static final class AdvertiseSettings.Builder {
+ ctor public AdvertiseSettings.Builder();
+ method public android.bluetooth.le.AdvertiseSettings build();
+ method public android.bluetooth.le.AdvertiseSettings.Builder setAdvertiseMode(int);
+ method public android.bluetooth.le.AdvertiseSettings.Builder setTxPowerLevel(int);
+ method public android.bluetooth.le.AdvertiseSettings.Builder setType(int);
+ }
+
+ public final class AdvertisementData implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean getIncludeTxPowerLevel();
+ method public int getManufacturerId();
+ method public byte[] getManufacturerSpecificData();
+ method public byte[] getServiceData();
+ method public android.os.ParcelUuid getServiceDataUuid();
+ method public java.util.List<android.os.ParcelUuid> getServiceUuids();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static final class AdvertisementData.Builder {
+ ctor public AdvertisementData.Builder();
+ method public android.bluetooth.le.AdvertisementData build();
+ method public android.bluetooth.le.AdvertisementData.Builder setIncludeTxPowerLevel(boolean);
+ method public android.bluetooth.le.AdvertisementData.Builder setManufacturerData(int, byte[]);
+ method public android.bluetooth.le.AdvertisementData.Builder setServiceData(android.os.ParcelUuid, byte[]);
+ method public android.bluetooth.le.AdvertisementData.Builder setServiceUuids(java.util.List<android.os.ParcelUuid>);
+ }
+
+ public final class BluetoothLeAdvertiser {
+ method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertisementData, android.bluetooth.le.AdvertiseCallback);
+ method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertisementData, android.bluetooth.le.AdvertisementData, android.bluetooth.le.AdvertiseCallback);
+ method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
+ }
+
+ public final class BluetoothLeScanner {
+ method public void startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+ method public void stopScan(android.bluetooth.le.ScanCallback);
+ }
+
+ public abstract class ScanCallback {
+ ctor public ScanCallback();
+ method public abstract void onAdvertisementUpdate(android.bluetooth.le.ScanResult);
+ method public abstract void onScanFailed(int);
+ field public static final int SCAN_FAILED_ALREADY_STARTED = 1; // 0x1
+ field public static final int SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2; // 0x2
+ field public static final int SCAN_FAILED_CONTROLLER_FAILURE = 4; // 0x4
+ field public static final int SCAN_FAILED_GATT_SERVICE_FAILURE = 3; // 0x3
}
- public final class BluetoothLeScanFilter implements android.os.Parcelable {
+ public final class ScanFilter implements android.os.Parcelable {
method public int describeContents();
method public java.lang.String getDeviceAddress();
method public java.lang.String getLocalName();
@@ -6312,63 +6363,54 @@ package android.bluetooth {
method public byte[] getServiceDataMask();
method public android.os.ParcelUuid getServiceUuid();
method public android.os.ParcelUuid getServiceUuidMask();
- method public boolean matches(android.bluetooth.BluetoothLeScanner.ScanResult);
- method public static android.bluetooth.BluetoothLeScanFilter.Builder newBuilder();
+ method public boolean matches(android.bluetooth.le.ScanResult);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
- public static class BluetoothLeScanFilter.Builder {
- method public android.bluetooth.BluetoothLeScanFilter build();
- method public android.bluetooth.BluetoothLeScanFilter.Builder macAddress(java.lang.String);
- method public android.bluetooth.BluetoothLeScanFilter.Builder manufacturerData(int, byte[]);
- method public android.bluetooth.BluetoothLeScanFilter.Builder manufacturerDataMask(byte[]);
- method public android.bluetooth.BluetoothLeScanFilter.Builder name(java.lang.String);
- method public android.bluetooth.BluetoothLeScanFilter.Builder rssiRange(int, int);
- method public android.bluetooth.BluetoothLeScanFilter.Builder serviceData(byte[]);
- method public android.bluetooth.BluetoothLeScanFilter.Builder serviceDataMask(byte[]);
- method public android.bluetooth.BluetoothLeScanFilter.Builder serviceUuid(android.os.ParcelUuid);
- method public android.bluetooth.BluetoothLeScanFilter.Builder serviceUuidMask(android.os.ParcelUuid);
+ public static final class ScanFilter.Builder {
+ ctor public ScanFilter.Builder();
+ method public android.bluetooth.le.ScanFilter build();
+ method public android.bluetooth.le.ScanFilter.Builder setMacAddress(java.lang.String);
+ method public android.bluetooth.le.ScanFilter.Builder setManufacturerData(int, byte[]);
+ method public android.bluetooth.le.ScanFilter.Builder setManufacturerData(int, byte[], byte[]);
+ method public android.bluetooth.le.ScanFilter.Builder setName(java.lang.String);
+ method public android.bluetooth.le.ScanFilter.Builder setRssiRange(int, int);
+ method public android.bluetooth.le.ScanFilter.Builder setServiceData(byte[]);
+ method public android.bluetooth.le.ScanFilter.Builder setServiceData(byte[], byte[]);
+ method public android.bluetooth.le.ScanFilter.Builder setServiceUuid(android.os.ParcelUuid);
+ method public android.bluetooth.le.ScanFilter.Builder setServiceUuid(android.os.ParcelUuid, android.os.ParcelUuid);
}
- public class BluetoothLeScanner {
- method public void startScan(java.util.List<android.bluetooth.BluetoothLeScanFilter>, android.bluetooth.BluetoothLeScanner.Settings, android.bluetooth.BluetoothLeScanner.ScanCallback);
- method public void stopScan(android.bluetooth.BluetoothLeScanner.Settings);
- }
-
- public static abstract interface BluetoothLeScanner.ScanCallback {
- method public abstract void onBatchScanResults(java.util.List<android.bluetooth.BluetoothLeScanner.ScanResult>);
- method public abstract void onDeviceFound(android.bluetooth.BluetoothLeScanner.ScanResult);
- method public abstract void onDeviceLost(android.bluetooth.BluetoothDevice);
- method public abstract void onDeviceUpdate(android.bluetooth.BluetoothLeScanner.ScanResult);
- method public abstract void onScanFailed(int);
- field public static final int APPLICATION_REGISTRATION_FAILED = 2; // 0x2
- field public static final int CONTROLLER_FAILURE = 4; // 0x4
- field public static final int GATT_SERVICE_FAILURE = 3; // 0x3
- field public static final int SCAN_ALREADY_STARTED = 1; // 0x1
+ public final class ScanRecord {
+ method public int getAdvertiseFlags();
+ method public java.lang.String getLocalName();
+ method public int getManufacturerId();
+ method public byte[] getManufacturerSpecificData();
+ method public byte[] getServiceData();
+ method public android.os.ParcelUuid getServiceDataUuid();
+ method public java.util.List<android.os.ParcelUuid> getServiceUuids();
+ method public int getTxPowerLevel();
+ method public static android.bluetooth.le.ScanRecord parseFromBytes(byte[]);
}
- public static final class BluetoothLeScanner.ScanResult implements android.os.Parcelable {
- ctor public BluetoothLeScanner.ScanResult(android.bluetooth.BluetoothDevice, byte[], int, long);
+ public final class ScanResult implements android.os.Parcelable {
method public int describeContents();
method public android.bluetooth.BluetoothDevice getDevice();
method public int getRssi();
method public byte[] getScanRecord();
- method public long getTimestampMicros();
+ method public long getTimestampNanos();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
- public static final class BluetoothLeScanner.Settings implements android.os.Parcelable {
+ public final class ScanSettings implements android.os.Parcelable {
method public int describeContents();
method public int getCallbackType();
- method public long getReportDelayMicros();
+ method public long getReportDelayNanos();
method public int getScanMode();
method public int getScanResultType();
- method public static android.bluetooth.BluetoothLeScanner.Settings.Builder newBuilder();
method public void writeToParcel(android.os.Parcel, int);
- field public static final int CALLBACK_TYPE_ON_FOUND = 1; // 0x1
- field public static final int CALLBACK_TYPE_ON_LOST = 2; // 0x2
field public static final int CALLBACK_TYPE_ON_UPDATE = 0; // 0x0
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int SCAN_MODE_BALANCED = 1; // 0x1
@@ -6377,56 +6419,12 @@ package android.bluetooth {
field public static final int SCAN_RESULT_TYPE_FULL = 0; // 0x0
}
- public static class BluetoothLeScanner.Settings.Builder {
- method public android.bluetooth.BluetoothLeScanner.Settings build();
- method public android.bluetooth.BluetoothLeScanner.Settings.Builder callbackType(int);
- method public android.bluetooth.BluetoothLeScanner.Settings.Builder reportDelayMicros(long);
- method public android.bluetooth.BluetoothLeScanner.Settings.Builder scanMode(int);
- }
-
- public final class BluetoothManager {
- method public android.bluetooth.BluetoothAdapter getAdapter();
- method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
- method public int getConnectionState(android.bluetooth.BluetoothDevice, int);
- method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int, int[]);
- method public android.bluetooth.BluetoothGattServer openGattServer(android.content.Context, android.bluetooth.BluetoothGattServerCallback);
- }
-
- public abstract interface BluetoothProfile {
- method public abstract java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method public abstract int getConnectionState(android.bluetooth.BluetoothDevice);
- method public abstract java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
- field public static final int A2DP = 2; // 0x2
- field public static final java.lang.String EXTRA_PREVIOUS_STATE = "android.bluetooth.profile.extra.PREVIOUS_STATE";
- field public static final java.lang.String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
- field public static final int GATT = 7; // 0x7
- field public static final int GATT_SERVER = 8; // 0x8
- field public static final int HEADSET = 1; // 0x1
- field public static final int HEALTH = 3; // 0x3
- field public static final int STATE_CONNECTED = 2; // 0x2
- field public static final int STATE_CONNECTING = 1; // 0x1
- field public static final int STATE_DISCONNECTED = 0; // 0x0
- field public static final int STATE_DISCONNECTING = 3; // 0x3
- }
-
- public static abstract interface BluetoothProfile.ServiceListener {
- method public abstract void onServiceConnected(int, android.bluetooth.BluetoothProfile);
- method public abstract void onServiceDisconnected(int);
- }
-
- public final class BluetoothServerSocket implements java.io.Closeable {
- method public android.bluetooth.BluetoothSocket accept() throws java.io.IOException;
- method public android.bluetooth.BluetoothSocket accept(int) throws java.io.IOException;
- method public void close() throws java.io.IOException;
- }
-
- public final class BluetoothSocket implements java.io.Closeable {
- method public void close() throws java.io.IOException;
- method public void connect() throws java.io.IOException;
- method public java.io.InputStream getInputStream() throws java.io.IOException;
- method public java.io.OutputStream getOutputStream() throws java.io.IOException;
- method public android.bluetooth.BluetoothDevice getRemoteDevice();
- method public boolean isConnected();
+ public static final class ScanSettings.Builder {
+ ctor public ScanSettings.Builder();
+ method public android.bluetooth.le.ScanSettings build();
+ method public android.bluetooth.le.ScanSettings.Builder setCallbackType(int);
+ method public android.bluetooth.le.ScanSettings.Builder setReportDelayNanos(long);
+ method public android.bluetooth.le.ScanSettings.Builder setScanMode(int);
}
}
@@ -6915,6 +6913,7 @@ package android.content {
method public abstract java.io.File[] getExternalCacheDirs();
method public abstract java.io.File getExternalFilesDir(java.lang.String);
method public abstract java.io.File[] getExternalFilesDirs(java.lang.String);
+ method public abstract java.io.File[] getExternalMediaDirs();
method public abstract java.io.File getFileStreamPath(java.lang.String);
method public abstract java.io.File getFilesDir();
method public abstract android.os.Looper getMainLooper();
@@ -7027,6 +7026,7 @@ package android.content {
field public static final java.lang.String NSD_SERVICE = "servicediscovery";
field public static final java.lang.String POWER_SERVICE = "power";
field public static final java.lang.String PRINT_SERVICE = "print";
+ field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions";
field public static final java.lang.String SEARCH_SERVICE = "search";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String STORAGE_SERVICE = "storage";
@@ -7083,6 +7083,7 @@ package android.content {
method public java.io.File[] getExternalCacheDirs();
method public java.io.File getExternalFilesDir(java.lang.String);
method public java.io.File[] getExternalFilesDirs(java.lang.String);
+ method public java.io.File[] getExternalMediaDirs();
method public java.io.File getFileStreamPath(java.lang.String);
method public java.io.File getFilesDir();
method public android.os.Looper getMainLooper();
@@ -7782,12 +7783,14 @@ package android.content {
ctor public RestrictionEntry(java.lang.String, java.lang.String);
ctor public RestrictionEntry(java.lang.String, boolean);
ctor public RestrictionEntry(java.lang.String, java.lang.String[]);
+ ctor public RestrictionEntry(java.lang.String, int);
ctor public RestrictionEntry(android.os.Parcel);
method public int describeContents();
method public java.lang.String[] getAllSelectedStrings();
method public java.lang.String[] getChoiceEntries();
method public java.lang.String[] getChoiceValues();
method public java.lang.String getDescription();
+ method public int getIntValue();
method public java.lang.String getKey();
method public boolean getSelectedState();
method public java.lang.String getSelectedString();
@@ -7799,6 +7802,7 @@ package android.content {
method public void setChoiceValues(java.lang.String[]);
method public void setChoiceValues(android.content.Context, int);
method public void setDescription(java.lang.String);
+ method public void setIntValue(int);
method public void setSelectedState(boolean);
method public void setSelectedString(java.lang.String);
method public void setTitle(java.lang.String);
@@ -7807,10 +7811,36 @@ package android.content {
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int TYPE_BOOLEAN = 1; // 0x1
field public static final int TYPE_CHOICE = 2; // 0x2
+ field public static final int TYPE_INTEGER = 5; // 0x5
field public static final int TYPE_MULTI_SELECT = 4; // 0x4
field public static final int TYPE_NULL = 0; // 0x0
}
+ public class RestrictionsManager {
+ method public android.os.Bundle getApplicationRestrictions();
+ method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
+ method public boolean hasRestrictionsProvider();
+ method public void notifyPermissionResponse(java.lang.String, android.os.Bundle);
+ method public void requestPermission(java.lang.String, android.os.Bundle);
+ field public static final java.lang.String ACTION_PERMISSION_RESPONSE_RECEIVED = "android.intent.action.PERMISSION_RESPONSE_RECEIVED";
+ field public static final java.lang.String ACTION_REQUEST_PERMISSION = "android.intent.action.REQUEST_PERMISSION";
+ field public static final java.lang.String EXTRA_PACKAGE_NAME = "package_name";
+ field public static final java.lang.String EXTRA_REQUEST_BUNDLE = "request_bundle";
+ field public static final java.lang.String EXTRA_RESPONSE_BUNDLE = "response_bundle";
+ field public static final java.lang.String EXTRA_TEMPLATE_ID = "template_id";
+ field public static final java.lang.String REQUEST_KEY_APPROVE_LABEL = "android.req_template.accept";
+ field public static final java.lang.String REQUEST_KEY_DATA = "android.req_template.data";
+ field public static final java.lang.String REQUEST_KEY_DENY_LABEL = "android.req_template.reject";
+ field public static final java.lang.String REQUEST_KEY_DEVICE_NAME = "android.req_template.device";
+ field public static final java.lang.String REQUEST_KEY_ICON = "android.req_template.icon";
+ field public static final java.lang.String REQUEST_KEY_ID = "android.req_template.req_id";
+ field public static final java.lang.String REQUEST_KEY_MESSAGE = "android.req_template.mesg";
+ field public static final java.lang.String REQUEST_KEY_REQUESTOR_NAME = "android.req_template.requestor";
+ field public static final java.lang.String REQUEST_KEY_TITLE = "android.req_template.title";
+ field public static final java.lang.String REQUEST_TEMPLATE_QUESTION = "android.req_template.type.simple";
+ field public static final java.lang.String RESPONSE_KEY_BOOLEAN = "android.req_template.response";
+ }
+
public class SearchRecentSuggestionsProvider extends android.content.ContentProvider {
ctor public SearchRecentSuggestionsProvider();
method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
@@ -8199,7 +8229,7 @@ package android.content.pm {
}
public class LauncherActivityInfo {
- method public int getApplicationFlags();
+ method public android.content.pm.ApplicationInfo getApplicationInfo();
method public android.graphics.drawable.Drawable getBadgedIcon(int);
method public android.content.ComponentName getComponentName();
method public long getFirstInstallTime();
@@ -8210,21 +8240,21 @@ package android.content.pm {
}
public class LauncherApps {
- method public synchronized void addOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
+ method public void addOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
method public boolean isActivityEnabledForProfile(android.content.ComponentName, android.os.UserHandle);
method public boolean isPackageEnabledForProfile(java.lang.String, android.os.UserHandle);
- method public synchronized void removeOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
+ method public void removeOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
- method public void startActivityForProfile(android.content.ComponentName, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
+ method public void startActivityForProfile(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
}
public static abstract interface LauncherApps.OnAppsChangedListener {
- method public abstract void onPackageAdded(android.os.UserHandle, java.lang.String);
- method public abstract void onPackageChanged(android.os.UserHandle, java.lang.String);
- method public abstract void onPackageRemoved(android.os.UserHandle, java.lang.String);
- method public abstract void onPackagesAvailable(android.os.UserHandle, java.lang.String[], boolean);
- method public abstract void onPackagesUnavailable(android.os.UserHandle, java.lang.String[], boolean);
+ method public abstract void onPackageAdded(java.lang.String, android.os.UserHandle);
+ method public abstract void onPackageChanged(java.lang.String, android.os.UserHandle);
+ method public abstract void onPackageRemoved(java.lang.String, android.os.UserHandle);
+ method public abstract void onPackagesAvailable(java.lang.String[], android.os.UserHandle, boolean);
+ method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
}
public class PackageInfo implements android.os.Parcelable {
@@ -11476,6 +11506,7 @@ package android.graphics.drawable {
}
public class RippleDrawable extends android.graphics.drawable.LayerDrawable {
+ ctor public RippleDrawable(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
}
public class RotateDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
@@ -12724,6 +12755,9 @@ package android.hardware.camera2.params {
method public int getWidth();
method public int getX();
method public int getY();
+ field public static final int METERING_WEIGHT_DONT_CARE = 0; // 0x0
+ field public static final int METERING_WEIGHT_MAX = 1000; // 0x3e8
+ field public static final int METERING_WEIGHT_MIN = 0; // 0x0
}
public final class RggbChannelVector {
@@ -14295,7 +14329,6 @@ package android.media {
method public final void release();
method public final void releaseOutputBuffer(int, boolean);
method public final void releaseOutputBuffer(int, long);
- method public void setNotificationCallback(android.media.MediaCodec.NotificationCallback);
method public final void setParameters(android.os.Bundle);
method public final void setVideoScalingMode(int);
method public final void signalEndOfInputStream();
@@ -14345,10 +14378,6 @@ package android.media {
field public int numSubSamples;
}
- public static abstract interface MediaCodec.NotificationCallback {
- method public abstract void onCodecNotify(android.media.MediaCodec);
- }
-
public final class MediaCodecInfo {
method public final android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String);
method public final java.lang.String getName();
@@ -14677,6 +14706,7 @@ package android.media {
method public long getLong(java.lang.String);
method public android.media.Rating getRating(java.lang.String);
method public java.lang.String getString(java.lang.String);
+ method public java.util.Set<java.lang.String> keySet();
method public int size();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
@@ -15814,56 +15844,81 @@ package android.media.session {
public final class MediaController {
method public void addCallback(android.media.session.MediaController.Callback);
method public void addCallback(android.media.session.MediaController.Callback, android.os.Handler);
+ method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public static android.media.session.MediaController fromToken(android.media.session.MediaSessionToken);
- method public android.media.session.TransportController getTransportController();
+ method public android.media.MediaMetadata getMetadata();
+ method public android.media.session.PlaybackState getPlaybackState();
+ method public int getRatingType();
+ method public android.media.session.MediaController.TransportControls getTransportControls();
method public void removeCallback(android.media.session.MediaController.Callback);
- method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
- method public void sendMediaButton(int);
+ method public void sendControlCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
}
public static abstract class MediaController.Callback {
ctor public MediaController.Callback();
- method public void onEvent(java.lang.String, android.os.Bundle);
+ method public void onMetadataChanged(android.media.MediaMetadata);
+ method public void onPlaybackStateChanged(android.media.session.PlaybackState);
+ method public void onSessionEvent(java.lang.String, android.os.Bundle);
+ }
+
+ public final class MediaController.TransportControls {
+ method public void fastForward();
+ method public void pause();
+ method public void play();
+ method public void rewind();
+ method public void seekTo(long);
+ method public void setRating(android.media.Rating);
+ method public void skipToNext();
+ method public void skipToPrevious();
+ method public void stop();
}
public final class MediaSession {
method public void addCallback(android.media.session.MediaSession.Callback);
method public void addCallback(android.media.session.MediaSession.Callback, android.os.Handler);
+ method public void addTransportControlsCallback(android.media.session.MediaSession.TransportControlsCallback);
+ method public void addTransportControlsCallback(android.media.session.MediaSession.TransportControlsCallback, android.os.Handler);
method public android.media.session.MediaSessionToken getSessionToken();
- method public android.media.session.TransportPerformer getTransportPerformer();
method public boolean isActive();
method public void release();
method public void removeCallback(android.media.session.MediaSession.Callback);
- method public void sendEvent(java.lang.String, android.os.Bundle);
+ method public void removeTransportControlsCallback(android.media.session.MediaSession.TransportControlsCallback);
+ method public void sendSessionEvent(java.lang.String, android.os.Bundle);
method public void setActive(boolean);
method public void setFlags(int);
method public void setLaunchPendingIntent(android.app.PendingIntent);
- method public void useLocalPlayback(int);
- method public void useRemotePlayback(android.media.session.RemoteVolumeProvider);
+ method public void setMetadata(android.media.MediaMetadata);
+ method public void setPlaybackState(android.media.session.PlaybackState);
+ method public void setPlaybackToLocal(int);
+ method public void setPlaybackToRemote(android.media.session.RemoteVolumeProvider);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
}
public static abstract class MediaSession.Callback {
ctor public MediaSession.Callback();
- method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
- method public void onMediaButton(android.content.Intent);
+ method public void onControlCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+ method public void onMediaButtonEvent(android.content.Intent);
}
- public final class MediaSessionInfo implements android.os.Parcelable {
- method public int describeContents();
- method public java.lang.String getId();
- method public java.lang.String getPackageName();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
+ public static abstract class MediaSession.TransportControlsCallback {
+ ctor public MediaSession.TransportControlsCallback();
+ method public void onFastForward();
+ method public void onPause();
+ method public void onPlay();
+ method public void onRewind();
+ method public void onSeekTo(long);
+ method public void onSetRating(android.media.Rating);
+ method public void onSkipToNext();
+ method public void onSkipToPrevious();
+ method public void onStop();
}
public final class MediaSessionManager {
method public android.media.session.MediaSession createSession(java.lang.String);
- method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
}
- public class MediaSessionToken implements android.os.Parcelable {
+ public final class MediaSessionToken implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
@@ -15875,95 +15930,50 @@ package android.media.session {
method public int describeContents();
method public long getActions();
method public long getBufferPosition();
- method public java.lang.String getErrorMessage();
+ method public java.lang.CharSequence getErrorMessage();
+ method public float getPlaybackRate();
method public long getPosition();
- method public float getRate();
method public int getState();
method public void setActions(long);
method public void setBufferPosition(long);
- method public void setErrorMessage(java.lang.String);
+ method public void setErrorMessage(java.lang.CharSequence);
method public void setState(int, long, float);
method public void writeToParcel(android.os.Parcel, int);
- field public static final long ACTION_FASTFORWARD = 64L; // 0x40L
- field public static final long ACTION_NEXT_ITEM = 32L; // 0x20L
+ field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
field public static final long ACTION_PAUSE = 2L; // 0x2L
field public static final long ACTION_PLAY = 4L; // 0x4L
field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
- field public static final long ACTION_PREVIOUS_ITEM = 16L; // 0x10L
- field public static final long ACTION_RATING = 128L; // 0x80L
field public static final long ACTION_REWIND = 8L; // 0x8L
field public static final long ACTION_SEEK_TO = 256L; // 0x100L
+ field public static final long ACTION_SET_RATING = 128L; // 0x80L
+ field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
+ field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
field public static final long ACTION_STOP = 1L; // 0x1L
field public static final android.os.Parcelable.Creator CREATOR;
field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
- field public static final int PLAYSTATE_BUFFERING = 6; // 0x6
- field public static final int PLAYSTATE_ERROR = 7; // 0x7
- field public static final int PLAYSTATE_FAST_FORWARDING = 4; // 0x4
- field public static final int PLAYSTATE_NONE = 0; // 0x0
- field public static final int PLAYSTATE_PAUSED = 2; // 0x2
- field public static final int PLAYSTATE_PLAYING = 3; // 0x3
- field public static final int PLAYSTATE_REWINDING = 5; // 0x5
- field public static final int PLAYSTATE_SKIPPING_BACKWARDS = 9; // 0x9
- field public static final int PLAYSTATE_SKIPPING_FORWARDS = 10; // 0xa
- field public static final int PLAYSTATE_STOPPED = 1; // 0x1
+ field public static final int STATE_BUFFERING = 6; // 0x6
+ field public static final int STATE_ERROR = 7; // 0x7
+ field public static final int STATE_FAST_FORWARDING = 4; // 0x4
+ field public static final int STATE_NONE = 0; // 0x0
+ field public static final int STATE_PAUSED = 2; // 0x2
+ field public static final int STATE_PLAYING = 3; // 0x3
+ field public static final int STATE_REWINDING = 5; // 0x5
+ field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
+ field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
+ field public static final int STATE_STOPPED = 1; // 0x1
}
public abstract class RemoteVolumeProvider {
ctor public RemoteVolumeProvider(int, int);
- method public abstract int getCurrentVolume();
- method public final int getFlags();
method public final int getMaxVolume();
+ method public final int getVolumeControl();
method public final void notifyVolumeChanged();
- method public void onAdjustVolume(int);
- method public void onSetVolume(int);
- field public static final int FLAG_VOLUME_ABSOLUTE = 2; // 0x2
- field public static final int FLAG_VOLUME_RELATIVE = 1; // 0x1
- }
-
- public final class TransportController {
- method public void addStateListener(android.media.session.TransportController.TransportStateListener);
- method public void addStateListener(android.media.session.TransportController.TransportStateListener, android.os.Handler);
- method public void fastForward();
- method public android.media.MediaMetadata getMetadata();
- method public android.media.session.PlaybackState getPlaybackState();
- method public int getRatingType();
- method public void next();
- method public void pause();
- method public void play();
- method public void previous();
- method public void rate(android.media.Rating);
- method public void removeStateListener(android.media.session.TransportController.TransportStateListener);
- method public void rewind();
- method public void seekTo(long);
- method public void stop();
- }
-
- public static abstract class TransportController.TransportStateListener {
- ctor public TransportController.TransportStateListener();
- method public void onMetadataChanged(android.media.MediaMetadata);
- method public void onPlaybackStateChanged(android.media.session.PlaybackState);
- }
-
- public final class TransportPerformer {
- method public void addListener(android.media.session.TransportPerformer.Listener);
- method public void addListener(android.media.session.TransportPerformer.Listener, android.os.Handler);
- method public void removeListener(android.media.session.TransportPerformer.Listener);
- method public final void setMetadata(android.media.MediaMetadata);
- method public final void setPlaybackState(android.media.session.PlaybackState);
- }
-
- public static abstract class TransportPerformer.Listener {
- ctor public TransportPerformer.Listener();
- method public void onFastForward();
- method public void onNext();
- method public void onPause();
- method public void onPlay();
- method public void onPrevious();
- method public void onRate(android.media.Rating);
- method public void onRewind();
- method public void onRouteFocusChange(int);
- method public void onSeekTo(long);
- method public void onStop();
+ method public void onAdjustVolumeBy(int);
+ method public abstract int onGetCurrentVolume();
+ method public void onSetVolumeTo(int);
+ field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+ field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+ field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
}
}
@@ -17289,86 +17299,6 @@ package android.net.wifi {
method public abstract void onStartSuccess(java.lang.String);
}
- public class WifiScanner {
- method public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.HotspotInfo[]);
- method public void resetHotlist(android.net.wifi.WifiScanner.HotlistListener);
- method public void retrieveScanResults(boolean, android.net.wifi.WifiScanner.ScanListener);
- method public void setHotlist(android.net.wifi.WifiScanner.HotspotInfo[], int, android.net.wifi.WifiScanner.HotlistListener);
- method public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener);
- method public void startTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener);
- method public void stopBackgroundScan(android.net.wifi.WifiScanner.ScanListener);
- method public void stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener);
- field public static final int MAX_SCAN_PERIOD_MS = 1024000; // 0xfa000
- field public static final int MIN_SCAN_PERIOD_MS = 2000; // 0x7d0
- field public static final int REASON_CONFLICTING_REQUEST = -4; // 0xfffffffc
- field public static final int REASON_INVALID_LISTENER = -2; // 0xfffffffe
- field public static final int REASON_INVALID_REQUEST = -3; // 0xfffffffd
- field public static final int REASON_SUCCEEDED = 0; // 0x0
- field public static final int REASON_UNSPECIFIED = -1; // 0xffffffff
- field public static final int REPORT_EVENT_AFTER_BUFFER_FULL = 0; // 0x0
- field public static final int REPORT_EVENT_AFTER_EACH_SCAN = 1; // 0x1
- field public static final int REPORT_EVENT_FULL_SCAN_RESULT = 2; // 0x2
- field public static final int WIFI_BAND_24_GHZ = 1; // 0x1
- field public static final int WIFI_BAND_5_GHZ = 2; // 0x2
- field public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; // 0x4
- field public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; // 0x6
- field public static final int WIFI_BAND_BOTH = 3; // 0x3
- field public static final int WIFI_BAND_BOTH_WITH_DFS = 7; // 0x7
- field public static final int WIFI_BAND_UNSPECIFIED = 0; // 0x0
- }
-
- public static class WifiScanner.ChannelSpec {
- ctor public WifiScanner.ChannelSpec(int);
- field public int frequency;
- }
-
- public static class WifiScanner.FullScanResult implements android.os.Parcelable {
- ctor public WifiScanner.FullScanResult();
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public android.net.wifi.WifiScanner.InformationElement[] informationElements;
- field public android.net.wifi.ScanResult result;
- }
-
- public static abstract interface WifiScanner.HotlistListener {
- method public abstract void onFound(android.net.wifi.ScanResult[]);
- }
-
- public static class WifiScanner.HotspotInfo {
- ctor public WifiScanner.HotspotInfo();
- field public java.lang.String bssid;
- field public int frequencyHint;
- field public int high;
- field public int low;
- }
-
- public static class WifiScanner.InformationElement {
- ctor public WifiScanner.InformationElement();
- field public byte[] bytes;
- field public int id;
- }
-
- public static abstract interface WifiScanner.ScanListener {
- method public abstract void onFullResult(android.net.wifi.WifiScanner.FullScanResult);
- method public abstract void onPeriodChanged(int);
- method public abstract void onResults(android.net.wifi.ScanResult[]);
- }
-
- public static class WifiScanner.ScanSettings implements android.os.Parcelable {
- ctor public WifiScanner.ScanSettings();
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public int band;
- field public android.net.wifi.WifiScanner.ChannelSpec[] channels;
- field public int periodInMs;
- field public int reportEvents;
- }
-
- public static abstract interface WifiScanner.WifiChangeListener {
- method public abstract void onChanging(android.net.wifi.ScanResult[]);
- method public abstract void onQuiescence(android.net.wifi.ScanResult[]);
- }
-
public class WpsInfo implements android.os.Parcelable {
ctor public WpsInfo();
ctor public WpsInfo(android.net.wifi.WpsInfo);
@@ -17716,25 +17646,15 @@ package android.nfc {
package android.nfc.cardemulation {
- public final class AidGroup implements android.os.Parcelable {
- ctor public AidGroup(java.util.ArrayList<java.lang.String>, java.lang.String);
- method public int describeContents();
- method public java.util.ArrayList<java.lang.String> getAids();
- method public java.lang.String getCategory();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- field public static final int MAX_NUM_AIDS = 256; // 0x100
- }
-
public final class CardEmulation {
method public boolean categoryAllowsForegroundPreference(java.lang.String);
- method public android.nfc.cardemulation.AidGroup getAidGroupForService(android.content.ComponentName, java.lang.String);
+ method public java.util.List<java.lang.String> getAidsForService(android.content.ComponentName, java.lang.String);
method public static synchronized android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter);
method public int getSelectionModeForCategory(java.lang.String);
method public boolean isDefaultServiceForAid(android.content.ComponentName, java.lang.String);
method public boolean isDefaultServiceForCategory(android.content.ComponentName, java.lang.String);
- method public boolean registerAidGroupForService(android.content.ComponentName, android.nfc.cardemulation.AidGroup);
- method public boolean removeAidGroupForService(android.content.ComponentName, java.lang.String);
+ method public boolean registerAidsForService(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
+ method public boolean removeAidsForService(android.content.ComponentName, java.lang.String);
method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
method public boolean unsetPreferredService(android.app.Activity);
field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
@@ -20575,6 +20495,37 @@ package android.os {
ctor public BadParcelableException(java.lang.Exception);
}
+ public class BaseBundle {
+ method public void clear();
+ method public boolean containsKey(java.lang.String);
+ method public java.lang.Object get(java.lang.String);
+ method public double getDouble(java.lang.String);
+ method public double getDouble(java.lang.String, double);
+ method public double[] getDoubleArray(java.lang.String);
+ method public int getInt(java.lang.String);
+ method public int getInt(java.lang.String, int);
+ method public int[] getIntArray(java.lang.String);
+ method public long getLong(java.lang.String);
+ method public long getLong(java.lang.String, long);
+ method public long[] getLongArray(java.lang.String);
+ method public java.lang.String getString(java.lang.String);
+ method public java.lang.String getString(java.lang.String, java.lang.String);
+ method public java.lang.String[] getStringArray(java.lang.String);
+ method public boolean isEmpty();
+ method public java.util.Set<java.lang.String> keySet();
+ method public void putAll(android.os.PersistableBundle);
+ method public void putDouble(java.lang.String, double);
+ method public void putDoubleArray(java.lang.String, double[]);
+ method public void putInt(java.lang.String, int);
+ method public void putIntArray(java.lang.String, int[]);
+ method public void putLong(java.lang.String, long);
+ method public void putLongArray(java.lang.String, long[]);
+ method public void putString(java.lang.String, java.lang.String);
+ method public void putStringArray(java.lang.String, java.lang.String[]);
+ method public void remove(java.lang.String);
+ method public int size();
+ }
+
public class BatteryManager {
ctor public BatteryManager();
method public android.os.BatteryProperty getProperty(int) throws android.os.RemoteException;
@@ -20703,17 +20654,14 @@ package android.os {
field public static final int L = 10000; // 0x2710
}
- public final class Bundle extends android.os.CommonBundle {
+ public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
ctor public Bundle();
ctor public Bundle(java.lang.ClassLoader);
ctor public Bundle(int);
ctor public Bundle(android.os.Bundle);
ctor public Bundle(android.os.PersistableBundle);
- method public void clear();
method public java.lang.Object clone();
- method public boolean containsKey(java.lang.String);
method public int describeContents();
- method public java.lang.Object get(java.lang.String);
method public android.os.IBinder getBinder(java.lang.String);
method public boolean getBoolean(java.lang.String);
method public boolean getBoolean(java.lang.String, boolean);
@@ -20730,37 +20678,21 @@ package android.os {
method public java.lang.CharSequence[] getCharSequenceArray(java.lang.String);
method public java.util.ArrayList<java.lang.CharSequence> getCharSequenceArrayList(java.lang.String);
method public java.lang.ClassLoader getClassLoader();
- method public double getDouble(java.lang.String);
- method public double getDouble(java.lang.String, double);
- method public double[] getDoubleArray(java.lang.String);
method public float getFloat(java.lang.String);
method public float getFloat(java.lang.String, float);
method public float[] getFloatArray(java.lang.String);
- method public int getInt(java.lang.String);
- method public int getInt(java.lang.String, int);
- method public int[] getIntArray(java.lang.String);
method public java.util.ArrayList<java.lang.Integer> getIntegerArrayList(java.lang.String);
- method public long getLong(java.lang.String);
- method public long getLong(java.lang.String, long);
- method public long[] getLongArray(java.lang.String);
method public T getParcelable(java.lang.String);
method public android.os.Parcelable[] getParcelableArray(java.lang.String);
method public java.util.ArrayList<T> getParcelableArrayList(java.lang.String);
- method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
method public java.io.Serializable getSerializable(java.lang.String);
method public short getShort(java.lang.String);
method public short getShort(java.lang.String, short);
method public short[] getShortArray(java.lang.String);
method public android.util.SparseArray<T> getSparseParcelableArray(java.lang.String);
- method public java.lang.String getString(java.lang.String);
- method public java.lang.String getString(java.lang.String, java.lang.String);
- method public java.lang.String[] getStringArray(java.lang.String);
method public java.util.ArrayList<java.lang.String> getStringArrayList(java.lang.String);
method public boolean hasFileDescriptors();
- method public boolean isEmpty();
- method public java.util.Set<java.lang.String> keySet();
method public void putAll(android.os.Bundle);
- method public void putAll(android.os.PersistableBundle);
method public void putBinder(java.lang.String, android.os.IBinder);
method public void putBoolean(java.lang.String, boolean);
method public void putBooleanArray(java.lang.String, boolean[]);
@@ -20772,30 +20704,19 @@ package android.os {
method public void putCharSequence(java.lang.String, java.lang.CharSequence);
method public void putCharSequenceArray(java.lang.String, java.lang.CharSequence[]);
method public void putCharSequenceArrayList(java.lang.String, java.util.ArrayList<java.lang.CharSequence>);
- method public void putDouble(java.lang.String, double);
- method public void putDoubleArray(java.lang.String, double[]);
method public void putFloat(java.lang.String, float);
method public void putFloatArray(java.lang.String, float[]);
- method public void putInt(java.lang.String, int);
- method public void putIntArray(java.lang.String, int[]);
method public void putIntegerArrayList(java.lang.String, java.util.ArrayList<java.lang.Integer>);
- method public void putLong(java.lang.String, long);
- method public void putLongArray(java.lang.String, long[]);
method public void putParcelable(java.lang.String, android.os.Parcelable);
method public void putParcelableArray(java.lang.String, android.os.Parcelable[]);
method public void putParcelableArrayList(java.lang.String, java.util.ArrayList<? extends android.os.Parcelable>);
- method public void putPersistableBundle(java.lang.String, android.os.PersistableBundle);
method public void putSerializable(java.lang.String, java.io.Serializable);
method public void putShort(java.lang.String, short);
method public void putShortArray(java.lang.String, short[]);
method public void putSparseParcelableArray(java.lang.String, android.util.SparseArray<? extends android.os.Parcelable>);
- method public void putString(java.lang.String, java.lang.String);
- method public void putStringArray(java.lang.String, java.lang.String[]);
method public void putStringArrayList(java.lang.String, java.util.ArrayList<java.lang.String>);
method public void readFromParcel(android.os.Parcel);
- method public void remove(java.lang.String);
method public void setClassLoader(java.lang.ClassLoader);
- method public int size();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final android.os.Bundle EMPTY;
@@ -20813,9 +20734,6 @@ package android.os {
method public abstract void onCancel();
}
- abstract class CommonBundle implements java.lang.Cloneable android.os.Parcelable {
- }
-
public class ConditionVariable {
ctor public ConditionVariable();
ctor public ConditionVariable(boolean);
@@ -21395,46 +21313,14 @@ package android.os {
field public static final int PATTERN_SIMPLE_GLOB = 2; // 0x2
}
- public final class PersistableBundle extends android.os.CommonBundle {
+ public final class PersistableBundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
ctor public PersistableBundle();
- ctor public PersistableBundle(java.lang.ClassLoader);
ctor public PersistableBundle(int);
ctor public PersistableBundle(android.os.PersistableBundle);
- method public void clear();
method public java.lang.Object clone();
- method public boolean containsKey(java.lang.String);
method public int describeContents();
- method public java.lang.Object get(java.lang.String);
- method public java.lang.ClassLoader getClassLoader();
- method public double getDouble(java.lang.String);
- method public double getDouble(java.lang.String, double);
- method public double[] getDoubleArray(java.lang.String);
- method public int getInt(java.lang.String);
- method public int getInt(java.lang.String, int);
- method public int[] getIntArray(java.lang.String);
- method public long getLong(java.lang.String);
- method public long getLong(java.lang.String, long);
- method public long[] getLongArray(java.lang.String);
method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
- method public java.lang.String getString(java.lang.String);
- method public java.lang.String getString(java.lang.String, java.lang.String);
- method public java.lang.String[] getStringArray(java.lang.String);
- method public boolean isEmpty();
- method public java.util.Set<java.lang.String> keySet();
- method public void putAll(android.os.PersistableBundle);
- method public void putDouble(java.lang.String, double);
- method public void putDoubleArray(java.lang.String, double[]);
- method public void putInt(java.lang.String, int);
- method public void putIntArray(java.lang.String, int[]);
- method public void putLong(java.lang.String, long);
- method public void putLongArray(java.lang.String, long[]);
method public void putPersistableBundle(java.lang.String, android.os.PersistableBundle);
- method public void putString(java.lang.String, java.lang.String);
- method public void putStringArray(java.lang.String, java.lang.String[]);
- method public void readFromParcel(android.os.Parcel);
- method public void remove(java.lang.String);
- method public void setClassLoader(java.lang.ClassLoader);
- method public int size();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final android.os.PersistableBundle EMPTY;
@@ -26391,16 +26277,17 @@ package android.service.voice {
method public android.view.LayoutInflater getLayoutInflater();
method public android.app.Dialog getWindow();
method public void hideWindow();
+ method public void onAbortVoice(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
method public void onBackPressed();
method public abstract void onCancel(android.service.voice.VoiceInteractionSession.Request);
method public void onCloseSystemDialogs();
method public abstract void onCommand(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
method public void onComputeInsets(android.service.voice.VoiceInteractionSession.Insets);
- method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
+ method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
method public void onCreate(android.os.Bundle);
method public android.view.View onCreateContentView();
method public void onDestroy();
- method public abstract boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
+ method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
@@ -26427,6 +26314,7 @@ package android.service.voice {
}
public static class VoiceInteractionSession.Request {
+ method public void sendAbortVoiceResult(android.os.Bundle);
method public void sendCancelResult();
method public void sendCommandResult(boolean, android.os.Bundle);
method public void sendConfirmResult(boolean, android.os.Bundle);
@@ -26581,6 +26469,28 @@ package android.speech {
package android.speech.tts {
+ public final class Markup implements android.os.Parcelable {
+ ctor public Markup();
+ ctor public Markup(java.lang.String);
+ ctor public Markup(android.speech.tts.Markup);
+ method public android.speech.tts.Markup addNestedMarkup(android.speech.tts.Markup);
+ method public int describeContents();
+ method public android.speech.tts.Markup getNestedMarkup(int);
+ method public java.util.List<android.speech.tts.Markup> getNestedMarkups();
+ method public java.lang.String getParameter(java.lang.String);
+ method public java.lang.String getPlainText();
+ method public java.lang.String getType();
+ method public static android.speech.tts.Markup markupFromString(java.lang.String) throws java.lang.IllegalArgumentException;
+ method public int nestedMarkupSize();
+ method public int parametersSize();
+ method public boolean removeNestedMarkup(android.speech.tts.Markup);
+ method public void removeParameter(java.lang.String);
+ method public android.speech.tts.Markup setParameter(java.lang.String, java.lang.String);
+ method public void setPlainText(java.lang.String);
+ method public void setType(java.lang.String);
+ method public void writeToParcel(android.os.Parcel, int);
+ }
+
public final class RequestConfig {
method public android.os.Bundle getAudioParams();
method public android.speech.tts.VoiceInfo getVoice();
@@ -26643,9 +26553,10 @@ package android.speech.tts {
}
public final class SynthesisRequestV2 implements android.os.Parcelable {
- ctor public SynthesisRequestV2(java.lang.String, java.lang.String, java.lang.String, android.os.Bundle, android.os.Bundle);
+ ctor public SynthesisRequestV2(android.speech.tts.Markup, java.lang.String, java.lang.String, android.os.Bundle, android.os.Bundle);
method public int describeContents();
method public android.os.Bundle getAudioParams();
+ method public android.speech.tts.Markup getMarkup();
method public java.lang.String getText();
method public java.lang.String getUtteranceId();
method public java.lang.String getVoiceName();
@@ -26748,7 +26659,9 @@ package android.speech.tts {
method public void queueAudio(android.net.Uri, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
method public void queueSilence(long, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.TextToSpeechClient.RequestCallbacks);
method public void queueSpeak(java.lang.String, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
+ method public void queueSpeak(android.speech.tts.Markup, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
method public void queueSynthesizeToFile(java.lang.String, android.speech.tts.TextToSpeechClient.UtteranceId, java.io.File, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
+ method public void queueSynthesizeToFile(android.speech.tts.Markup, android.speech.tts.TextToSpeechClient.UtteranceId, java.io.File, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
method public void stop();
}
@@ -26822,6 +26735,85 @@ package android.speech.tts {
method protected void onVoicesInfoChange();
}
+ public class Utterance {
+ ctor public Utterance();
+ method public android.speech.tts.Utterance append(android.speech.tts.Utterance.AbstractTts<? extends android.speech.tts.Utterance.AbstractTts<?>>);
+ method public android.speech.tts.Utterance append(java.lang.String);
+ method public android.speech.tts.Utterance append(int);
+ method public android.speech.tts.Markup createMarkup();
+ method public android.speech.tts.Utterance.AbstractTts<? extends android.speech.tts.Utterance.AbstractTts<?>> get(int);
+ method public android.speech.tts.Utterance setNoWarningOnFallback(boolean);
+ method public int size();
+ method public static android.speech.tts.Utterance utteranceFromString(java.lang.String) throws java.lang.IllegalArgumentException;
+ field public static final int ANIMACY_ANIMATE = 1; // 0x1
+ field public static final int ANIMACY_INANIMATE = 2; // 0x2
+ field public static final int ANIMACY_UNKNOWN = 0; // 0x0
+ field public static final int CASE_ABLATIVE = 4; // 0x4
+ field public static final int CASE_ACCUSATIVE = 2; // 0x2
+ field public static final int CASE_DATIVE = 3; // 0x3
+ field public static final int CASE_GENITIVE = 5; // 0x5
+ field public static final int CASE_INSTRUMENTAL = 8; // 0x8
+ field public static final int CASE_LOCATIVE = 7; // 0x7
+ field public static final int CASE_NOMINATIVE = 1; // 0x1
+ field public static final int CASE_UNKNOWN = 0; // 0x0
+ field public static final int CASE_VOCATIVE = 6; // 0x6
+ field public static final int GENDER_FEMALE = 3; // 0x3
+ field public static final int GENDER_MALE = 2; // 0x2
+ field public static final int GENDER_NEUTRAL = 1; // 0x1
+ field public static final int GENDER_UNKNOWN = 0; // 0x0
+ field public static final java.lang.String KEY_NO_WARNING_ON_FALLBACK = "no_warning_on_fallback";
+ field public static final int MULTIPLICITY_DUAL = 2; // 0x2
+ field public static final int MULTIPLICITY_PLURAL = 3; // 0x3
+ field public static final int MULTIPLICITY_SINGLE = 1; // 0x1
+ field public static final int MULTIPLICITY_UNKNOWN = 0; // 0x0
+ field public static final java.lang.String TYPE_UTTERANCE = "utterance";
+ }
+
+ public static abstract class Utterance.AbstractTts {
+ ctor protected Utterance.AbstractTts();
+ ctor protected Utterance.AbstractTts(android.speech.tts.Markup);
+ method public java.lang.String generatePlainText();
+ method public android.speech.tts.Markup getMarkup();
+ method protected java.lang.String getParameter(java.lang.String);
+ method public java.lang.String getPlainText();
+ method public java.lang.String getType();
+ method protected C removeParameter(java.lang.String);
+ method protected C setParameter(java.lang.String, java.lang.String);
+ method public C setPlainText(java.lang.String);
+ field protected android.speech.tts.Markup mMarkup;
+ }
+
+ public static abstract class Utterance.AbstractTtsSemioticClass extends android.speech.tts.Utterance.AbstractTts {
+ ctor protected Utterance.AbstractTtsSemioticClass();
+ ctor protected Utterance.AbstractTtsSemioticClass(android.speech.tts.Markup);
+ method public int getAnimacy();
+ method public int getCase();
+ method public int getGender();
+ method public int getMultiplicity();
+ method public C setAnimacy(int);
+ method public C setCase(int);
+ method public C setGender(int);
+ method public C setMultiplicity(int);
+ }
+
+ public static class Utterance.TtsCardinal extends android.speech.tts.Utterance.AbstractTtsSemioticClass {
+ ctor public Utterance.TtsCardinal();
+ ctor public Utterance.TtsCardinal(int);
+ ctor public Utterance.TtsCardinal(java.lang.String);
+ method public java.lang.String getInteger();
+ method public android.speech.tts.Utterance.TtsCardinal setInteger(int);
+ method public android.speech.tts.Utterance.TtsCardinal setInteger(java.lang.String);
+ field protected static final java.lang.String TYPE_CARDINAL = "cardinal";
+ }
+
+ public static class Utterance.TtsText extends android.speech.tts.Utterance.AbstractTtsSemioticClass {
+ ctor public Utterance.TtsText();
+ ctor public Utterance.TtsText(java.lang.String);
+ method public java.lang.String getText();
+ method public android.speech.tts.Utterance.TtsText setText(java.lang.String);
+ field protected static final java.lang.String TYPE_TEXT = "text";
+ }
+
public abstract class UtteranceProgressListener {
ctor public UtteranceProgressListener();
method public abstract void onDone(java.lang.String);
@@ -27583,6 +27575,7 @@ package android.telecomm {
method public void setDisconnected(java.lang.String, int, java.lang.String);
method public void setIsCompatibleWith(java.lang.String, boolean);
method public void setOnHold(java.lang.String);
+ method public void setRequestingRingback(java.lang.String, boolean);
method public void setRinging(java.lang.String);
}
@@ -27648,6 +27641,7 @@ package android.telecomm {
ctor protected Connection();
method public final android.telecomm.CallAudioState getCallAudioState();
method public final android.net.Uri getHandle();
+ method public boolean isRequestingRingback();
method protected void onAbort();
method protected void onAnswer();
method protected void onDisconnect();
@@ -27656,6 +27650,7 @@ package android.telecomm {
method protected void onReject();
method protected void onSetAudioState(android.telecomm.CallAudioState);
method protected void onSetSignal(android.os.Bundle);
+ method protected void onSetState(int);
method protected void onStopDtmfTone();
method protected void onUnhold();
method protected void setActive();
@@ -27664,6 +27659,7 @@ package android.telecomm {
method protected void setDisconnected(int, java.lang.String);
method protected void setHandle(android.net.Uri);
method protected void setOnHold();
+ method protected void setRequestingRingback(boolean);
method protected void setRinging();
method public static java.lang.String stateToString(int);
}
@@ -27673,6 +27669,7 @@ package android.telecomm {
method public abstract void onDestroyed(android.telecomm.Connection);
method public abstract void onDisconnected(android.telecomm.Connection, int, java.lang.String);
method public abstract void onHandleChanged(android.telecomm.Connection, android.net.Uri);
+ method public abstract void onRequestingRingback(android.telecomm.Connection, boolean);
method public abstract void onSignalChanged(android.telecomm.Connection, android.os.Bundle);
method public abstract void onStateChanged(android.telecomm.Connection, int);
}
@@ -27683,6 +27680,7 @@ package android.telecomm {
method public void onDestroyed(android.telecomm.Connection);
method public void onDisconnected(android.telecomm.Connection, int, java.lang.String);
method public void onHandleChanged(android.telecomm.Connection, android.net.Uri);
+ method public void onRequestingRingback(android.telecomm.Connection, boolean);
method public void onSignalChanged(android.telecomm.Connection, android.os.Bundle);
method public void onStateChanged(android.telecomm.Connection, int);
}
@@ -27763,6 +27761,7 @@ package android.telecomm {
public abstract class InCallService extends android.app.Service {
ctor protected InCallService();
method protected abstract void addCall(android.telecomm.InCallCall);
+ method protected abstract void bringToForeground(boolean);
method protected final android.telecomm.InCallAdapter getAdapter();
method protected void onAdapterAttached(android.telecomm.InCallAdapter);
method protected abstract void onAudioStateChanged(android.telecomm.CallAudioState);
@@ -28762,6 +28761,7 @@ package android.test.mock {
method public java.io.File[] getExternalCacheDirs();
method public java.io.File getExternalFilesDir(java.lang.String);
method public java.io.File[] getExternalFilesDirs(java.lang.String);
+ method public java.io.File[] getExternalMediaDirs();
method public java.io.File getFileStreamPath(java.lang.String);
method public java.io.File getFilesDir();
method public android.os.Looper getMainLooper();
@@ -38141,8 +38141,10 @@ package android.widget {
method public void setOnMenuItemClickListener(android.widget.Toolbar.OnMenuItemClickListener);
method public void setSubtitle(int);
method public void setSubtitle(java.lang.CharSequence);
+ method public void setSubtitleTextAppearance(android.content.Context, int);
method public void setTitle(int);
method public void setTitle(java.lang.CharSequence);
+ method public void setTitleTextAppearance(android.content.Context, int);
method public boolean showOverflowMenu();
}
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index 2673031..4503726 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -20,6 +20,7 @@ import android.app.backup.IBackupManager;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.system.OsConstants;
import android.util.Log;
import java.io.IOException;
@@ -50,13 +51,11 @@ public final class Backup {
return;
}
- int socketFd = Integer.parseInt(nextArg());
-
String arg = nextArg();
if (arg.equals("backup")) {
- doFullBackup(socketFd);
+ doFullBackup(OsConstants.STDOUT_FILENO);
} else if (arg.equals("restore")) {
- doFullRestore(socketFd);
+ doFullRestore(OsConstants.STDIN_FILENO);
} else {
Log.e(TAG, "Invalid operation '" + arg + "'");
}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index b7c2c22..47047b8 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -39,6 +39,7 @@ import android.content.pm.VerificationParams;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.IUserManager;
import android.os.Process;
@@ -57,7 +58,6 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.WeakHashMap;
-
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
@@ -823,6 +823,7 @@ public final class Pm {
byte[] tag = null;
String originatingUriString = null;
String referrer = null;
+ String abi = null;
while ((opt=nextOption()) != null) {
if (opt.equals("-l")) {
@@ -893,12 +894,34 @@ public final class Pm {
System.err.println("Error: must supply argument for --referrer");
return;
}
+ } else if (opt.equals("--abi")) {
+ abi = nextOptionData();
+ if (abi == null) {
+ System.err.println("Error: must supply argument for --abi");
+ return;
+ }
} else {
System.err.println("Error: Unknown option: " + opt);
return;
}
}
+ if (abi != null) {
+ final String[] supportedAbis = Build.SUPPORTED_ABIS;
+ boolean matched = false;
+ for (String supportedAbi : supportedAbis) {
+ if (supportedAbi.equals(abi)) {
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched) {
+ System.err.println("Error: abi " + abi + " not supported on this device.");
+ return;
+ }
+ }
+
final ContainerEncryptionParams encryptionParams;
if (algo != null || iv != null || key != null || macAlgo != null || macKey != null
|| tag != null) {
@@ -976,8 +999,9 @@ public final class Pm {
VerificationParams verificationParams = new VerificationParams(verificationURI,
originatingURI, referrerURI, VerificationParams.NO_UID, null);
- mPm.installPackageWithVerificationAndEncryptionEtc(apkURI, null, obs, installFlags,
- installerPackageName, verificationParams, encryptionParams);
+ mPm.installPackageWithVerificationEncryptionAndAbiOverrideEtc(apkURI, null,
+ obs, installFlags, installerPackageName, verificationParams,
+ encryptionParams, abi);
synchronized (obs) {
while (!obs.finished) {
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index f05f4c7..d4c4318 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.Gravity;
+import android.view.KeyEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
@@ -1013,6 +1014,26 @@ public abstract class ActionBar {
return null;
}
+ /** @hide */
+ public boolean openOptionsMenu() {
+ return false;
+ }
+
+ /** @hide */
+ public boolean invalidateOptionsMenu() {
+ return false;
+ }
+
+ /** @hide */
+ public boolean onMenuKeyEvent(KeyEvent event) {
+ return false;
+ }
+
+ /** @hide */
+ public boolean collapseActionView() {
+ return false;
+ }
+
/**
* Listener interface for ActionBar navigation events.
*
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index b5281ff..23b5f29 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -716,6 +716,7 @@ public class Activity extends ContextThemeWrapper
HashMap<String, Object> children;
ArrayList<Fragment> fragments;
ArrayMap<String, LoaderManagerImpl> loaders;
+ VoiceInteractor voiceInteractor;
}
/* package */ NonConfigurationInstances mLastNonConfigurationInstances;
@@ -920,6 +921,9 @@ public class Activity extends ContextThemeWrapper
}
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this, savedInstanceState);
+ if (mVoiceInteractor != null) {
+ mVoiceInteractor.attachActivity(this);
+ }
mCalled = true;
}
@@ -1830,7 +1834,8 @@ public class Activity extends ContextThemeWrapper
}
}
}
- if (activity == null && children == null && fragments == null && !retainLoaders) {
+ if (activity == null && children == null && fragments == null && !retainLoaders
+ && mVoiceInteractor == null) {
return null;
}
@@ -1839,6 +1844,7 @@ public class Activity extends ContextThemeWrapper
nci.children = children;
nci.fragments = fragments;
nci.loaders = mAllLoaderManagers;
+ nci.voiceInteractor = mVoiceInteractor;
return nci;
}
@@ -2069,15 +2075,16 @@ public class Activity extends ContextThemeWrapper
* <p>In order to use a Toolbar within the Activity's window content the application
* must not request the window feature {@link Window#FEATURE_ACTION_BAR FEATURE_ACTION_BAR}.</p>
*
- * @param actionBar Toolbar to set as the Activity's action bar
+ * @param toolbar Toolbar to set as the Activity's action bar
*/
- public void setActionBar(@Nullable Toolbar actionBar) {
+ public void setActionBar(@Nullable Toolbar toolbar) {
if (getActionBar() instanceof WindowDecorActionBar) {
throw new IllegalStateException("This Activity already has an action bar supplied " +
"by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " +
"android:windowActionBar to false in your theme to use a Toolbar instead.");
}
- mActionBar = new ToolbarActionBar(actionBar);
+ mActionBar = new ToolbarActionBar(toolbar, getTitle(), this);
+ mActionBar.invalidateOptionsMenu();
}
/**
@@ -2443,6 +2450,10 @@ public class Activity extends ContextThemeWrapper
* but you can override this to do whatever you want.
*/
public void onBackPressed() {
+ if (mActionBar != null && mActionBar.collapseActionView()) {
+ return;
+ }
+
if (!mFragments.popBackStackImmediate()) {
finishAfterTransition();
}
@@ -2654,6 +2665,14 @@ public class Activity extends ContextThemeWrapper
*/
public boolean dispatchKeyEvent(KeyEvent event) {
onUserInteraction();
+
+ // Let action bars open menus in response to the menu key prioritized over
+ // the window handling it
+ if (event.getKeyCode() == KeyEvent.KEYCODE_MENU &&
+ mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
+ return true;
+ }
+
Window win = getWindow();
if (win.superDispatchKeyEvent(event)) {
return true;
@@ -2901,7 +2920,9 @@ public class Activity extends ContextThemeWrapper
* time it needs to be displayed.
*/
public void invalidateOptionsMenu() {
- mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+ if (mActionBar == null || !mActionBar.invalidateOptionsMenu()) {
+ mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+ }
}
/**
@@ -3111,7 +3132,9 @@ public class Activity extends ContextThemeWrapper
* open, this method does nothing.
*/
public void openOptionsMenu() {
- mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
+ if (mActionBar == null || !mActionBar.openOptionsMenu()) {
+ mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
+ }
}
/**
@@ -5632,8 +5655,14 @@ public class Activity extends ContextThemeWrapper
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
- mVoiceInteractor = voiceInteractor != null
- ? new VoiceInteractor(this, this, voiceInteractor, Looper.myLooper()) : null;
+ if (voiceInteractor != null) {
+ if (lastNonConfigurationInstances != null) {
+ mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
+ } else {
+ mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
+ Looper.myLooper());
+ }
+ }
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
@@ -5842,6 +5871,9 @@ public class Activity extends ContextThemeWrapper
if (mLoaderManager != null) {
mLoaderManager.doDestroy();
}
+ if (mVoiceInteractor != null) {
+ mVoiceInteractor.detachActivity();
+ }
}
/**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index abcb0d0..788ac56 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -738,7 +738,7 @@ public class ActivityManager {
public static final int RECENT_INCLUDE_PROFILES = 0x0004;
/**
- * Return a list of the tasks that the user has recently launched, with
+ * <p></p>Return a list of the tasks that the user has recently launched, with
* the most recent being first and older ones after in order.
*
* <p><b>Note: this method is only intended for debugging and presenting
@@ -750,6 +750,15 @@ public class ActivityManager {
* same time, assumptions made about the meaning of the data here for
* purposes of control flow will be incorrect.</p>
*
+ * @deprecated As of {@link android.os.Build.VERSION_CODES#L}, this method is
+ * no longer available to third party applications: as the introduction of
+ * document-centric recents means
+ * it can leak personal information to the caller. For backwards compatibility,
+ * it will still return a small subset of its data: at least the caller's
+ * own tasks (though see {@link #getAppTasks()} for the correct supported
+ * way to retrieve that information), and possibly some other tasks
+ * such as home that are known to not be sensitive.
+ *
* @param maxNum The maximum number of entries to return in the list. The
* actual number returned may be smaller, depending on how many tasks the
* user has started and the maximum number the system can remember.
@@ -762,6 +771,7 @@ public class ActivityManager {
* @throws SecurityException Throws SecurityException if the caller does
* not hold the {@link android.Manifest.permission#GET_TASKS} permission.
*/
+ @Deprecated
public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags)
throws SecurityException {
try {
@@ -946,6 +956,14 @@ public class ActivityManager {
* same time, assumptions made about the meaning of the data here for
* purposes of control flow will be incorrect.</p>
*
+ * @deprecated As of {@link android.os.Build.VERSION_CODES#L}, this method
+ * is no longer available to third party
+ * applications: the introduction of document-centric recents means
+ * it can leak person information to the caller. For backwards compatibility,
+ * it will still retu rn a small subset of its data: at least the caller's
+ * own tasks, and possibly some other tasks
+ * such as home that are known to not be sensitive.
+ *
* @param maxNum The maximum number of entries to return in the list. The
* actual number returned may be smaller, depending on how many tasks the
* user has started.
@@ -956,6 +974,7 @@ public class ActivityManager {
* @throws SecurityException Throws SecurityException if the caller does
* not hold the {@link android.Manifest.permission#GET_TASKS} permission.
*/
+ @Deprecated
public List<RunningTaskInfo> getRunningTasks(int maxNum)
throws SecurityException {
try {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1bd5abf..4f335bb 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -35,7 +35,9 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IIntentReceiver;
import android.content.IntentSender;
+import android.content.IRestrictionsManager;
import android.content.ReceiverCallNotAllowedException;
+import android.content.RestrictionsManager;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
@@ -251,6 +253,8 @@ class ContextImpl extends Context {
private File[] mExternalFilesDirs;
@GuardedBy("mSync")
private File[] mExternalCacheDirs;
+ @GuardedBy("mSync")
+ private File[] mExternalMediaDirs;
private static final String[] EMPTY_FILE_LIST = {};
@@ -660,6 +664,13 @@ class ContextImpl extends Context {
}
});
+ registerService(RESTRICTIONS_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(RESTRICTIONS_SERVICE);
+ IRestrictionsManager service = IRestrictionsManager.Stub.asInterface(b);
+ return new RestrictionsManager(ctx, service);
+ }
+ });
registerService(PRINT_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder iBinder = ServiceManager.getService(Context.PRINT_SERVICE);
@@ -1039,6 +1050,18 @@ class ContextImpl extends Context {
}
@Override
+ public File[] getExternalMediaDirs() {
+ synchronized (mSync) {
+ if (mExternalMediaDirs == null) {
+ mExternalMediaDirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
+ }
+
+ // Create dirs if needed
+ return ensureDirsExistOrFilter(mExternalMediaDirs);
+ }
+ }
+
+ @Override
public File getFileStreamPath(String name) {
return makeFilename(getFilesDir(), name);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 90aeaae..8dba1dc 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1671,7 +1671,6 @@ public class Notification implements Parcelable
private Notification mPublicVersion = null;
private final NotificationColorUtil mColorUtil;
private ArrayList<String> mPeople;
- private boolean mPreQuantum;
private int mColor = COLOR_DEFAULT;
/**
@@ -1694,6 +1693,15 @@ public class Notification implements Parcelable
* object.
*/
public Builder(Context context) {
+ /*
+ * Important compatibility note!
+ * Some apps out in the wild create a Notification.Builder in their Activity subclass
+ * constructor for later use. At this point Activities - themselves subclasses of
+ * ContextWrapper - do not have their inner Context populated yet. This means that
+ * any calls to Context methods from within this constructor can cause NPEs in existing
+ * apps. Any data populated from mContext should therefore be populated lazily to
+ * preserve compatibility.
+ */
mContext = context;
// Set defaults to match the defaults of a Notification
@@ -1702,7 +1710,6 @@ public class Notification implements Parcelable
mPriority = PRIORITY_DEFAULT;
mPeople = new ArrayList<String>();
- mPreQuantum = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.L;
mColorUtil = NotificationColorUtil.getInstance();
}
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index 6dc48b0..85e970c 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -30,18 +30,39 @@ import com.android.internal.app.IVoiceInteractorRequest;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
-import java.util.WeakHashMap;
+import java.util.ArrayList;
/**
- * Interface for an {@link Activity} to interact with the user through voice.
+ * Interface for an {@link Activity} to interact with the user through voice. Use
+ * {@link android.app.Activity#getVoiceInteractor() Activity.getVoiceInteractor}
+ * to retrieve the interface, if the activity is currently involved in a voice interaction.
+ *
+ * <p>The voice interactor revolves around submitting voice interaction requests to the
+ * back-end voice interaction service that is working with the user. These requests are
+ * submitted with {@link #submitRequest}, providing a new instance of a
+ * {@link Request} subclass describing the type of operation to perform -- currently the
+ * possible requests are {@link ConfirmationRequest} and {@link CommandRequest}.
+ *
+ * <p>Once a request is submitted, the voice system will process it and eventually deliver
+ * the result to the request object. The application can cancel a pending request at any
+ * time.
+ *
+ * <p>The VoiceInteractor is integrated with Activity's state saving mechanism, so that
+ * if an activity is being restarted with retained state, it will retain the current
+ * VoiceInteractor and any outstanding requests. Because of this, you should always use
+ * {@link Request#getActivity() Request.getActivity} to get back to the activity of a
+ * request, rather than holding on to the activity instance yourself, either explicitly
+ * or implicitly through a non-static inner class.
*/
public class VoiceInteractor {
static final String TAG = "VoiceInteractor";
static final boolean DEBUG = true;
- final Context mContext;
- final Activity mActivity;
final IVoiceInteractor mInteractor;
+
+ Context mContext;
+ Activity mActivity;
+
final HandlerCaller mHandlerCaller;
final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() {
@Override
@@ -60,6 +81,16 @@ public class VoiceInteractor {
request.clear();
}
break;
+ case MSG_ABORT_VOICE_RESULT:
+ request = pullRequest((IVoiceInteractorRequest)args.arg1, true);
+ if (DEBUG) Log.d(TAG, "onAbortVoice: req="
+ + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request
+ + " result=" + args.arg1);
+ if (request != null) {
+ ((AbortVoiceRequest)request).onAbortResult((Bundle) args.arg2);
+ request.clear();
+ }
+ break;
case MSG_COMMAND_RESULT:
request = pullRequest((IVoiceInteractorRequest)args.arg1, msg.arg1 != 0);
if (DEBUG) Log.d(TAG, "onCommandResult: req="
@@ -94,6 +125,12 @@ public class VoiceInteractor {
}
@Override
+ public void deliverAbortVoiceResult(IVoiceInteractorRequest request, Bundle result) {
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOO(
+ MSG_ABORT_VOICE_RESULT, request, result));
+ }
+
+ @Override
public void deliverCommandResult(IVoiceInteractorRequest request, boolean complete,
Bundle result) {
mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(
@@ -110,8 +147,9 @@ public class VoiceInteractor {
final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
static final int MSG_CONFIRMATION_RESULT = 1;
- static final int MSG_COMMAND_RESULT = 2;
- static final int MSG_CANCEL_RESULT = 3;
+ static final int MSG_ABORT_VOICE_RESULT = 2;
+ static final int MSG_COMMAND_RESULT = 3;
+ static final int MSG_CANCEL_RESULT = 4;
public static abstract class Request {
IVoiceInteractorRequest mRequestInterface;
@@ -140,6 +178,12 @@ public class VoiceInteractor {
public void onCancel() {
}
+ public void onAttached(Activity activity) {
+ }
+
+ public void onDetached() {
+ }
+
void clear() {
mRequestInterface = null;
mContext = null;
@@ -180,9 +224,42 @@ public class VoiceInteractor {
IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
IVoiceInteractorCallback callback) throws RemoteException {
- return interactor.startConfirmation(packageName, callback, mPrompt.toString(), mExtras);
+ return interactor.startConfirmation(packageName, callback, mPrompt, mExtras);
+ }
+ }
+
+ public static class AbortVoiceRequest extends Request {
+ final CharSequence mMessage;
+ final Bundle mExtras;
+
+ /**
+ * Reports that the current interaction can not be complete with voice, so the
+ * application will need to switch to a traditional input UI. Applications should
+ * only use this when they need to completely bail out of the voice interaction
+ * and switch to a traditional UI. When the response comes back, the voice
+ * system has handled the request and is ready to switch; at that point the application
+ * can start a new non-voice activity. Be sure when starting the new activity
+ * to use {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK
+ * Intent.FLAG_ACTIVITY_NEW_TASK} to keep the new activity out of the current voice
+ * interaction task.
+ *
+ * @param message Optional message to tell user about not being able to complete
+ * the interaction with voice.
+ * @param extras Additional optional information.
+ */
+ public AbortVoiceRequest(CharSequence message, Bundle extras) {
+ mMessage = message;
+ mExtras = extras;
}
- }
+
+ public void onAbortResult(Bundle result) {
+ }
+
+ IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
+ IVoiceInteractorCallback callback) throws RemoteException {
+ return interactor.startAbortVoice(packageName, callback, mMessage, mExtras);
+ }
+ }
public static class CommandRequest extends Request {
final String mCommand;
@@ -220,11 +297,11 @@ public class VoiceInteractor {
}
}
- VoiceInteractor(Context context, Activity activity, IVoiceInteractor interactor,
+ VoiceInteractor(IVoiceInteractor interactor, Context context, Activity activity,
Looper looper) {
+ mInteractor = interactor;
mContext = context;
mActivity = activity;
- mInteractor = interactor;
mHandlerCaller = new HandlerCaller(context, looper, mHandlerCallerCallback, true);
}
@@ -238,6 +315,49 @@ public class VoiceInteractor {
}
}
+ private ArrayList<Request> makeRequestList() {
+ final int N = mActiveRequests.size();
+ if (N < 1) {
+ return null;
+ }
+ ArrayList<Request> list = new ArrayList<Request>(N);
+ for (int i=0; i<N; i++) {
+ list.add(mActiveRequests.valueAt(i));
+ }
+ return list;
+ }
+
+ void attachActivity(Activity activity) {
+ if (mActivity == activity) {
+ return;
+ }
+ mContext = activity;
+ mActivity = activity;
+ ArrayList<Request> reqs = makeRequestList();
+ if (reqs != null) {
+ for (int i=0; i<reqs.size(); i++) {
+ Request req = reqs.get(i);
+ req.mContext = activity;
+ req.mActivity = activity;
+ req.onAttached(activity);
+ }
+ }
+ }
+
+ void detachActivity() {
+ ArrayList<Request> reqs = makeRequestList();
+ if (reqs != null) {
+ for (int i=0; i<reqs.size(); i++) {
+ Request req = reqs.get(i);
+ req.onDetached();
+ req.mActivity = null;
+ req.mContext = null;
+ }
+ }
+ mContext = null;
+ mActivity = null;
+ }
+
public boolean submitRequest(Request request) {
try {
IVoiceInteractorRequest ireq = request.submit(mInteractor,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6b486c4..b3b1d47 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -25,6 +25,7 @@ import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.RestrictionsManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Process;
@@ -2320,4 +2321,23 @@ public class DevicePolicyManager {
}
}
+ /**
+ * Designates a specific broadcast receiver component as the provider for
+ * making permission requests of a local or remote administrator of the user.
+ * <p/>
+ * Only a profile owner can designate the restrictions provider.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param receiver The component name of a BroadcastReceiver that handles the
+ * {@link RestrictionsManager#ACTION_REQUEST_PERMISSION} intent. If this param is null,
+ * it removes the restrictions provider previously assigned.
+ */
+ public void setRestrictionsProvider(ComponentName admin, ComponentName receiver) {
+ if (mService != null) {
+ try {
+ mService.setRestrictionsProvider(admin, receiver);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed to set permission provider on device policy service");
+ }
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index cc6d715..7f754dc 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -121,6 +121,9 @@ interface IDevicePolicyManager {
void setApplicationRestrictions(in ComponentName who, in String packageName, in Bundle settings);
Bundle getApplicationRestrictions(in ComponentName who, in String packageName);
+ void setRestrictionsProvider(in ComponentName who, in ComponentName provider);
+ ComponentName getRestrictionsProvider(int userHandle);
+
void setUserRestriction(in ComponentName who, in String key, boolean enable);
void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
void clearCrossProfileIntentFilters(in ComponentName admin);
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index da5cb10..46f082e 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -22,7 +22,6 @@ import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import com.android.internal.backup.BackupConstants;
import com.android.internal.backup.IBackupTransport;
/**
@@ -32,6 +31,13 @@ import com.android.internal.backup.IBackupTransport;
* @hide
*/
public class BackupTransport {
+ public static final int TRANSPORT_OK = 0;
+ public static final int TRANSPORT_ERROR = 1;
+ public static final int TRANSPORT_NOT_INITIALIZED = 2;
+ public static final int TRANSPORT_PACKAGE_REJECTED = 3;
+ public static final int AGENT_ERROR = 4;
+ public static final int AGENT_UNKNOWN = 5;
+
IBackupTransport mBinderImpl = new TransportImpl();
/** @hide */
public IBinder getBinder() {
@@ -99,10 +105,50 @@ public class BackupTransport {
}
// ------------------------------------------------------------------------------------
+ // Device-level operations common to both key/value and full-data storage
+
+ /**
+ * Initialize the server side storage for this device, erasing all stored data.
+ * The transport may send the request immediately, or may buffer it. After
+ * this is called, {@link #finishBackup} will be called to ensure the request
+ * is sent and received successfully.
+ *
+ * @return One of {@link BackupTransport#TRANSPORT_OK} (OK so far) or
+ * {@link BackupTransport#TRANSPORT_ERROR} (on network error or other failure).
+ */
+ public int initializeDevice() {
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ /**
+ * Erase the given application's data from the backup destination. This clears
+ * out the given package's data from the current backup set, making it as though
+ * the app had never yet been backed up. After this is called, {@link finishBackup}
+ * must be called to ensure that the operation is recorded successfully.
+ *
+ * @return the same error codes as {@link #performBackup}.
+ */
+ public int clearBackupData(PackageInfo packageInfo) {
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ /**
+ * Finish sending application data to the backup destination. This must be
+ * called after {@link #performBackup}, {@link #performFullBackup}, or {@link clearBackupData}
+ * to ensure that all data is sent and the operation properly finalized. Only when this
+ * method returns true can a backup be assumed to have succeeded.
+ *
+ * @return the same error codes as {@link #performBackup} or {@link #performFullBackup}.
+ */
+ public int finishBackup() {
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ // ------------------------------------------------------------------------------------
// Key/value incremental backup support interfaces
/**
- * Verify that this is a suitable time for a backup pass. This should return zero
+ * Verify that this is a suitable time for a key/value backup pass. This should return zero
* if a backup is reasonable right now, some positive value otherwise. This method
* will be called outside of the {@link #performBackup}/{@link #finishBackup} pair.
*
@@ -117,19 +163,6 @@ public class BackupTransport {
}
/**
- * Initialize the server side storage for this device, erasing all stored data.
- * The transport may send the request immediately, or may buffer it. After
- * this is called, {@link #finishBackup} will be called to ensure the request
- * is sent and received successfully.
- *
- * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far) or
- * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure).
- */
- public int initializeDevice() {
- return BackupConstants.TRANSPORT_ERROR;
- }
-
- /**
* Send one application's key/value data update to the backup destination. The
* transport may send the data immediately, or may buffer it. After this is called,
* {@link #finishBackup} will be called to ensure the data is sent and recorded successfully.
@@ -143,37 +176,13 @@ public class BackupTransport {
* must be erased prior to the storage of the data provided here. The purpose of this
* is to provide a guarantee that no stale data exists in the restore set when the
* device begins providing incremental backups.
- * @return one of {@link BackupConstants#TRANSPORT_OK} (OK so far),
- * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure), or
- * {@link BackupConstants#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has
+ * @return one of {@link BackupTransport#TRANSPORT_OK} (OK so far),
+ * {@link BackupTransport#TRANSPORT_ERROR} (on network error or other failure), or
+ * {@link BackupTransport#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has
* become lost due to inactivity purge or some other reason and needs re-initializing)
*/
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd) {
- return BackupConstants.TRANSPORT_ERROR;
- }
-
- /**
- * Erase the give application's data from the backup destination. This clears
- * out the given package's data from the current backup set, making it as though
- * the app had never yet been backed up. After this is called, {@link finishBackup}
- * must be called to ensure that the operation is recorded successfully.
- *
- * @return the same error codes as {@link #performBackup}.
- */
- public int clearBackupData(PackageInfo packageInfo) {
- return BackupConstants.TRANSPORT_ERROR;
- }
-
- /**
- * Finish sending application data to the backup destination. This must be
- * called after {@link #performBackup} or {@link clearBackupData} to ensure that
- * all data is sent. Only when this method returns true can a backup be assumed
- * to have succeeded.
- *
- * @return the same error codes as {@link #performBackup}.
- */
- public int finishBackup() {
- return BackupConstants.TRANSPORT_ERROR;
+ return BackupTransport.TRANSPORT_ERROR;
}
// ------------------------------------------------------------------------------------
@@ -210,12 +219,12 @@ public class BackupTransport {
* or {@link #getCurrentRestoreSet}.
* @param packages List of applications to restore (if data is available).
* Application data will be restored in the order given.
- * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far, call
- * {@link #nextRestorePackage}) or {@link BackupConstants#TRANSPORT_ERROR}
+ * @return One of {@link BackupTransport#TRANSPORT_OK} (OK so far, call
+ * {@link #nextRestorePackage}) or {@link BackupTransport#TRANSPORT_ERROR}
* (an error occurred, the restore should be aborted and rescheduled).
*/
public int startRestore(long token, PackageInfo[] packages) {
- return BackupConstants.TRANSPORT_ERROR;
+ return BackupTransport.TRANSPORT_ERROR;
}
/**
@@ -235,7 +244,7 @@ public class BackupTransport {
* @return the same error codes as {@link #startRestore}.
*/
public int getRestoreData(ParcelFileDescriptor outFd) {
- return BackupConstants.TRANSPORT_ERROR;
+ return BackupTransport.TRANSPORT_ERROR;
}
/**
@@ -247,6 +256,78 @@ public class BackupTransport {
"Transport finishRestore() not implemented");
}
+ // ------------------------------------------------------------------------------------
+ // Full backup interfaces
+
+ /**
+ * Verify that this is a suitable time for a full-data backup pass. This should return zero
+ * if a backup is reasonable right now, some positive value otherwise. This method
+ * will be called outside of the {@link #performFullBackup}/{@link #finishBackup} pair.
+ *
+ * <p>If this is not a suitable time for a backup, the transport should return a
+ * backoff delay, in milliseconds, after which the Backup Manager should try again.
+ *
+ * @return Zero if this is a suitable time for a backup pass, or a positive time delay
+ * in milliseconds to suggest deferring the backup pass for a while.
+ *
+ * @see #requestBackupTime()
+ */
+ public long requestFullBackupTime() {
+ return 0;
+ }
+
+ /**
+ * Begin the process of sending an application's full-data archive to the backend.
+ * The description of the package whose data will be delivered is provided, as well as
+ * the socket file descriptor on which the transport will receive the data itself.
+ *
+ * <p>If the package is not eligible for backup, the transport should return
+ * {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED}. In this case the system will
+ * simply proceed with the next candidate if any, or finish the full backup operation
+ * if all apps have been processed.
+ *
+ * <p>After the transport returns {@link BackupTransport#TRANSPORT_OK} from this
+ * method, the OS will proceed to call {@link #sendBackupData()} one or more times
+ * to deliver the application's data as a streamed tarball. The transport should not
+ * read() from the socket except as instructed to via the {@link #sendBackupData(int)}
+ * method.
+ *
+ * <p>After all data has been delivered to the transport, the system will call
+ * {@link #finishBackup()}. At this point the transport should commit the data to
+ * its datastore, if appropriate, and close the socket that had been provided in
+ * {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)}.
+ *
+ * @param targetPackage The package whose data is to follow.
+ * @param socket The socket file descriptor through which the data will be provided.
+ * If the transport returns {@link #TRANSPORT_PACKAGE_REJECTED} here, it must still
+ * close this file descriptor now; otherwise it should be cached for use during
+ * succeeding calls to {@link #sendBackupData(int)}, and closed in response to
+ * {@link #finishBackup()}.
+ * @return TRANSPORT_PACKAGE_REJECTED to indicate that the stated application is not
+ * to be backed up; TRANSPORT_OK to indicate that the OS may proceed with delivering
+ * backup data; TRANSPORT_ERROR to indicate a fatal error condition that precludes
+ * performing a backup at this time.
+ */
+ public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) {
+ return BackupTransport.TRANSPORT_PACKAGE_REJECTED;
+ }
+
+ /**
+ * Tells the transport to read {@code numBytes} bytes of data from the socket file
+ * descriptor provided in the {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)}
+ * call, and deliver those bytes to the datastore.
+ *
+ * @param numBytes The number of bytes of tarball data available to be read from the
+ * socket.
+ * @return TRANSPORT_OK on successful processing of the data; TRANSPORT_ERROR to
+ * indicate a fatal error situation. If an error is returned, the system will
+ * call finishBackup() and stop attempting backups until after a backoff and retry
+ * interval.
+ */
+ public int sendBackupData(int numBytes) {
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
/**
* Bridge between the actual IBackupTransport implementation and the stable API. If the
* binder interface needs to change, we use this layer to translate so that we can
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 9e1c995..42c2aeb 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -18,6 +18,8 @@ package android.bluetooth;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.bluetooth.le.BluetoothLeScanner;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
diff --git a/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.java b/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.java
deleted file mode 100644
index 2fa5e49..0000000
--- a/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.java
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * 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.
- */
-
-package android.bluetooth;
-
-import android.annotation.Nullable;
-import android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Represents Bluetooth LE advertise and scan response data. This could be either the advertisement
- * data to be advertised, or the scan record obtained from BLE scans.
- * <p>
- * The exact bluetooth advertising and scan response data fields and types are defined in Bluetooth
- * 4.0 specification, Volume 3, Part C, Section 11 and 18, as well as Supplement to the Bluetooth
- * Core Specification Version 4. Currently the following fields are allowed to be set:
- * <li>Service UUIDs which identify the bluetooth gatt services running on the device.
- * <li>Tx power level which is the transmission power level.
- * <li>Service data which is the data associated with a service.
- * <li>Manufacturer specific data which is the data associated with a particular manufacturer.
- *
- * @see BluetoothLeAdvertiser
- */
-public final class BluetoothLeAdvertiseScanData {
- private static final String TAG = "BluetoothLeAdvertiseScanData";
-
- /**
- * Bluetooth LE Advertising Data type, the data will be placed in AdvData field of advertising
- * packet.
- */
- public static final int ADVERTISING_DATA = 0;
- /**
- * Bluetooth LE scan response data, the data will be placed in ScanRspData field of advertising
- * packet.
- * <p>
- */
- public static final int SCAN_RESPONSE_DATA = 1;
- /**
- * Scan record parsed from Bluetooth LE scans. The content can contain a concatenation of
- * advertising data and scan response data.
- */
- public static final int PARSED_SCAN_RECORD = 2;
-
- /**
- * Base data type which contains the common fields for {@link AdvertisementData} and
- * {@link ScanRecord}.
- */
- public abstract static class AdvertiseBaseData {
-
- private final int mDataType;
-
- @Nullable
- private final List<ParcelUuid> mServiceUuids;
-
- private final int mManufacturerId;
- @Nullable
- private final byte[] mManufacturerSpecificData;
-
- @Nullable
- private final ParcelUuid mServiceDataUuid;
- @Nullable
- private final byte[] mServiceData;
-
- private AdvertiseBaseData(int dataType,
- List<ParcelUuid> serviceUuids,
- ParcelUuid serviceDataUuid, byte[] serviceData,
- int manufacturerId,
- byte[] manufacturerSpecificData) {
- mDataType = dataType;
- mServiceUuids = serviceUuids;
- mManufacturerId = manufacturerId;
- mManufacturerSpecificData = manufacturerSpecificData;
- mServiceDataUuid = serviceDataUuid;
- mServiceData = serviceData;
- }
-
- /**
- * Returns the type of data, indicating whether the data is advertising data, scan response
- * data or scan record.
- */
- public int getDataType() {
- return mDataType;
- }
-
- /**
- * Returns a list of service uuids within the advertisement that are used to identify the
- * bluetooth gatt services.
- */
- public List<ParcelUuid> getServiceUuids() {
- return mServiceUuids;
- }
-
- /**
- * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth
- * SIG.
- */
- public int getManufacturerId() {
- return mManufacturerId;
- }
-
- /**
- * Returns the manufacturer specific data which is the content of manufacturer specific data
- * field. The first 2 bytes of the data contain the company id.
- */
- public byte[] getManufacturerSpecificData() {
- return mManufacturerSpecificData;
- }
-
- /**
- * Returns a 16 bit uuid of the service that the service data is associated with.
- */
- public ParcelUuid getServiceDataUuid() {
- return mServiceDataUuid;
- }
-
- /**
- * Returns service data. The first two bytes should be a 16 bit service uuid associated with
- * the service data.
- */
- public byte[] getServiceData() {
- return mServiceData;
- }
-
- @Override
- public String toString() {
- return "AdvertiseBaseData [mDataType=" + mDataType + ", mServiceUuids=" + mServiceUuids
- + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData="
- + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid="
- + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + "]";
- }
- }
-
- /**
- * Advertisement data packet for Bluetooth LE advertising. This represents the data to be
- * broadcasted in Bluetooth LE advertising.
- * <p>
- * Use {@link AdvertisementData.Builder} to create an instance of {@link AdvertisementData} to
- * be advertised.
- *
- * @see BluetoothLeAdvertiser
- */
- public static final class AdvertisementData extends AdvertiseBaseData implements Parcelable {
-
- private boolean mIncludeTxPowerLevel;
-
- /**
- * Whether the transmission power level will be included in the advertisement packet.
- */
- public boolean getIncludeTxPowerLevel() {
- return mIncludeTxPowerLevel;
- }
-
- /**
- * Returns a {@link Builder} to build {@link AdvertisementData}.
- */
- public static Builder newBuilder() {
- return new Builder();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(getDataType());
- List<ParcelUuid> uuids = getServiceUuids();
- if (uuids == null) {
- dest.writeInt(0);
- } else {
- dest.writeInt(uuids.size());
- dest.writeList(uuids);
- }
-
- dest.writeInt(getManufacturerId());
- byte[] manufacturerData = getManufacturerSpecificData();
- if (manufacturerData == null) {
- dest.writeInt(0);
- } else {
- dest.writeInt(manufacturerData.length);
- dest.writeByteArray(manufacturerData);
- }
-
- ParcelUuid serviceDataUuid = getServiceDataUuid();
- if (serviceDataUuid == null) {
- dest.writeInt(0);
- } else {
- dest.writeInt(1);
- dest.writeParcelable(serviceDataUuid, flags);
- byte[] serviceData = getServiceData();
- if (serviceData == null) {
- dest.writeInt(0);
- } else {
- dest.writeInt(serviceData.length);
- dest.writeByteArray(serviceData);
- }
- }
- dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0));
- }
-
- private AdvertisementData(int dataType,
- List<ParcelUuid> serviceUuids,
- ParcelUuid serviceDataUuid, byte[] serviceData,
- int manufacturerId,
- byte[] manufacturerSpecificData, boolean mIncludeTxPowerLevel) {
- super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId,
- manufacturerSpecificData);
- this.mIncludeTxPowerLevel = mIncludeTxPowerLevel;
- }
-
- public static final Parcelable.Creator<AdvertisementData> CREATOR =
- new Creator<AdvertisementData>() {
- @Override
- public AdvertisementData[] newArray(int size) {
- return new AdvertisementData[size];
- }
-
- @Override
- public AdvertisementData createFromParcel(Parcel in) {
- Builder builder = newBuilder();
- int dataType = in.readInt();
- builder.dataType(dataType);
- if (in.readInt() > 0) {
- List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
- in.readList(uuids, ParcelUuid.class.getClassLoader());
- builder.serviceUuids(uuids);
- }
- int manufacturerId = in.readInt();
- int manufacturerDataLength = in.readInt();
- if (manufacturerDataLength > 0) {
- byte[] manufacturerData = new byte[manufacturerDataLength];
- in.readByteArray(manufacturerData);
- builder.manufacturerData(manufacturerId, manufacturerData);
- }
- if (in.readInt() == 1) {
- ParcelUuid serviceDataUuid = in.readParcelable(
- ParcelUuid.class.getClassLoader());
- int serviceDataLength = in.readInt();
- if (serviceDataLength > 0) {
- byte[] serviceData = new byte[serviceDataLength];
- in.readByteArray(serviceData);
- builder.serviceData(serviceDataUuid, serviceData);
- }
- }
- builder.includeTxPowerLevel(in.readByte() == 1);
- return builder.build();
- }
- };
-
- /**
- * Builder for {@link BluetoothLeAdvertiseScanData.AdvertisementData}. Use
- * {@link AdvertisementData#newBuilder()} to get an instance of the Builder.
- */
- public static final class Builder {
- private static final int MAX_ADVERTISING_DATA_BYTES = 31;
- // Each fields need one byte for field length and another byte for field type.
- private static final int OVERHEAD_BYTES_PER_FIELD = 2;
- // Flags field will be set by system.
- private static final int FLAGS_FIELD_BYTES = 3;
-
- private int mDataType;
- @Nullable
- private List<ParcelUuid> mServiceUuids;
- private boolean mIncludeTxPowerLevel;
- private int mManufacturerId;
- @Nullable
- private byte[] mManufacturerSpecificData;
- @Nullable
- private ParcelUuid mServiceDataUuid;
- @Nullable
- private byte[] mServiceData;
-
- /**
- * Set data type.
- *
- * @param dataType Data type, could only be
- * {@link BluetoothLeAdvertiseScanData#ADVERTISING_DATA}
- * @throws IllegalArgumentException If the {@code dataType} is invalid.
- */
- public Builder dataType(int dataType) {
- if (mDataType != ADVERTISING_DATA && mDataType != SCAN_RESPONSE_DATA) {
- throw new IllegalArgumentException("invalid data type - " + dataType);
- }
- mDataType = dataType;
- return this;
- }
-
- /**
- * Set the service uuids. Note the corresponding bluetooth Gatt services need to be
- * already added on the device before start BLE advertising.
- *
- * @param serviceUuids Service uuids to be advertised, could be 16-bit, 32-bit or
- * 128-bit uuids.
- * @throws IllegalArgumentException If the {@code serviceUuids} are null.
- */
- public Builder serviceUuids(List<ParcelUuid> serviceUuids) {
- if (serviceUuids == null) {
- throw new IllegalArgumentException("serivceUuids are null");
- }
- mServiceUuids = serviceUuids;
- return this;
- }
-
- /**
- * Add service data to advertisement.
- *
- * @param serviceDataUuid A 16 bit uuid of the service data
- * @param serviceData Service data - the first two bytes of the service data are the
- * service data uuid.
- * @throws IllegalArgumentException If the {@code serviceDataUuid} or
- * {@code serviceData} is empty.
- */
- public Builder serviceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
- if (serviceDataUuid == null || serviceData == null) {
- throw new IllegalArgumentException(
- "serviceDataUuid or serviceDataUuid is null");
- }
- mServiceDataUuid = serviceDataUuid;
- mServiceData = serviceData;
- return this;
- }
-
- /**
- * Set manufacturer id and data. See <a
- * href="https://www.bluetooth.org/en-us/specification/assigned-numbers/company-identifiers">assigned
- * manufacturer identifies</a> for the existing company identifiers.
- *
- * @param manufacturerId Manufacturer id assigned by Bluetooth SIG.
- * @param manufacturerSpecificData Manufacturer specific data - the first two bytes of
- * the manufacturer specific data are the manufacturer id.
- * @throws IllegalArgumentException If the {@code manufacturerId} is negative or
- * {@code manufacturerSpecificData} is null.
- */
- public Builder manufacturerData(int manufacturerId, byte[] manufacturerSpecificData) {
- if (manufacturerId < 0) {
- throw new IllegalArgumentException(
- "invalid manufacturerId - " + manufacturerId);
- }
- if (manufacturerSpecificData == null) {
- throw new IllegalArgumentException("manufacturerSpecificData is null");
- }
- mManufacturerId = manufacturerId;
- mManufacturerSpecificData = manufacturerSpecificData;
- return this;
- }
-
- /**
- * Whether the transmission power level should be included in the advertising packet.
- */
- public Builder includeTxPowerLevel(boolean includeTxPowerLevel) {
- mIncludeTxPowerLevel = includeTxPowerLevel;
- return this;
- }
-
- /**
- * Build the {@link BluetoothLeAdvertiseScanData}.
- *
- * @throws IllegalArgumentException If the data size is larger than 31 bytes.
- */
- public AdvertisementData build() {
- if (totalBytes() > MAX_ADVERTISING_DATA_BYTES) {
- throw new IllegalArgumentException(
- "advertisement data size is larger than 31 bytes");
- }
- return new AdvertisementData(mDataType,
- mServiceUuids,
- mServiceDataUuid,
- mServiceData, mManufacturerId, mManufacturerSpecificData,
- mIncludeTxPowerLevel);
- }
-
- // Compute the size of the advertisement data.
- private int totalBytes() {
- int size = FLAGS_FIELD_BYTES; // flags field is always set.
- if (mServiceUuids != null) {
- int num16BitUuids = 0;
- int num32BitUuids = 0;
- int num128BitUuids = 0;
- for (ParcelUuid uuid : mServiceUuids) {
- if (BluetoothUuid.is16BitUuid(uuid)) {
- ++num16BitUuids;
- } else if (BluetoothUuid.is32BitUuid(uuid)) {
- ++num32BitUuids;
- } else {
- ++num128BitUuids;
- }
- }
- // 16 bit service uuids are grouped into one field when doing advertising.
- if (num16BitUuids != 0) {
- size += OVERHEAD_BYTES_PER_FIELD +
- num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
- }
- // 32 bit service uuids are grouped into one field when doing advertising.
- if (num32BitUuids != 0) {
- size += OVERHEAD_BYTES_PER_FIELD +
- num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
- }
- // 128 bit service uuids are grouped into one field when doing advertising.
- if (num128BitUuids != 0) {
- size += OVERHEAD_BYTES_PER_FIELD +
- num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
- }
- }
- if (mServiceData != null) {
- size += OVERHEAD_BYTES_PER_FIELD + mServiceData.length;
- }
- if (mManufacturerSpecificData != null) {
- size += OVERHEAD_BYTES_PER_FIELD + mManufacturerSpecificData.length;
- }
- if (mIncludeTxPowerLevel) {
- size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
- }
- return size;
- }
- }
-
- }
-
- /**
- * Represents a scan record from Bluetooth LE scan.
- */
- public static final class ScanRecord extends AdvertiseBaseData {
- // Flags of the advertising data.
- private final int mAdvertiseFlags;
-
- // Transmission power level(in dB).
- private final int mTxPowerLevel;
-
- // Local name of the Bluetooth LE device.
- private final String mLocalName;
-
- /**
- * Returns the advertising flags indicating the discoverable mode and capability of the
- * device. Returns -1 if the flag field is not set.
- */
- public int getAdvertiseFlags() {
- return mAdvertiseFlags;
- }
-
- /**
- * Returns the transmission power level of the packet in dBm. Returns
- * {@link Integer#MIN_VALUE} if the field is not set. This value can be used to calculate
- * the path loss of a received packet using the following equation:
- * <p>
- * <code>pathloss = txPowerLevel - rssi</code>
- */
- public int getTxPowerLevel() {
- return mTxPowerLevel;
- }
-
- /**
- * Returns the local name of the BLE device. The is a UTF-8 encoded string.
- */
- @Nullable
- public String getLocalName() {
- return mLocalName;
- }
-
- ScanRecord(int dataType,
- List<ParcelUuid> serviceUuids,
- ParcelUuid serviceDataUuid, byte[] serviceData,
- int manufacturerId,
- byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel,
- String localName) {
- super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId,
- manufacturerSpecificData);
- mLocalName = localName;
- mAdvertiseFlags = advertiseFlags;
- mTxPowerLevel = txPowerLevel;
- }
-
- /**
- * Get a {@link Parser} to parse the scan record byte array into {@link ScanRecord}.
- */
- public static Parser getParser() {
- return new Parser();
- }
-
- /**
- * A parser class used to parse a Bluetooth LE scan record to
- * {@link BluetoothLeAdvertiseScanData}. Note not all field types would be parsed.
- */
- public static final class Parser {
- private static final String PARSER_TAG = "BluetoothLeAdvertiseDataParser";
-
- // The following data type values are assigned by Bluetooth SIG.
- // For more details refer to Bluetooth 4.0 specification, Volume 3, Part C, Section 18.
- private static final int DATA_TYPE_FLAGS = 0x01;
- private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;
- private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;
- private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;
- private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;
- private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;
- private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;
- private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
- private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
- private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
- private static final int DATA_TYPE_SERVICE_DATA = 0x16;
- private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
-
- // Helper method to extract bytes from byte array.
- private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
- byte[] bytes = new byte[length];
- System.arraycopy(scanRecord, start, bytes, 0, length);
- return bytes;
- }
-
- /**
- * Parse scan record to {@link BluetoothLeAdvertiseScanData.ScanRecord}.
- * <p>
- * The format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11
- * and 18.
- * <p>
- * All numerical multi-byte entities and values shall use little-endian
- * <strong>byte</strong> order.
- *
- * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
- */
- public ScanRecord parseFromScanRecord(byte[] scanRecord) {
- if (scanRecord == null) {
- return null;
- }
-
- int currentPos = 0;
- int advertiseFlag = -1;
- List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
- String localName = null;
- int txPowerLevel = Integer.MIN_VALUE;
- ParcelUuid serviceDataUuid = null;
- byte[] serviceData = null;
- int manufacturerId = -1;
- byte[] manufacturerSpecificData = null;
-
- try {
- while (currentPos < scanRecord.length) {
- // length is unsigned int.
- int length = scanRecord[currentPos++] & 0xFF;
- if (length == 0) {
- break;
- }
- // Note the length includes the length of the field type itself.
- int dataLength = length - 1;
- // fieldType is unsigned int.
- int fieldType = scanRecord[currentPos++] & 0xFF;
- switch (fieldType) {
- case DATA_TYPE_FLAGS:
- advertiseFlag = scanRecord[currentPos] & 0xFF;
- break;
- case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:
- case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:
- parseServiceUuid(scanRecord, currentPos,
- dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
- break;
- case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
- case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
- parseServiceUuid(scanRecord, currentPos, dataLength,
- BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
- break;
- case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
- case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
- parseServiceUuid(scanRecord, currentPos, dataLength,
- BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids);
- break;
- case DATA_TYPE_LOCAL_NAME_SHORT:
- case DATA_TYPE_LOCAL_NAME_COMPLETE:
- localName = new String(
- extractBytes(scanRecord, currentPos, dataLength));
- break;
- case DATA_TYPE_TX_POWER_LEVEL:
- txPowerLevel = scanRecord[currentPos];
- break;
- case DATA_TYPE_SERVICE_DATA:
- serviceData = extractBytes(scanRecord, currentPos, dataLength);
- // The first two bytes of the service data are service data uuid.
- int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
- byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
- serviceUuidLength);
- serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes);
- break;
- case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
- manufacturerSpecificData = extractBytes(scanRecord, currentPos,
- dataLength);
- // The first two bytes of the manufacturer specific data are
- // manufacturer ids in little endian.
- manufacturerId = ((manufacturerSpecificData[1] & 0xFF) << 8) +
- (manufacturerSpecificData[0] & 0xFF);
- break;
- default:
- // Just ignore, we don't handle such data type.
- break;
- }
- currentPos += dataLength;
- }
-
- if (serviceUuids.isEmpty()) {
- serviceUuids = null;
- }
- return new ScanRecord(PARSED_SCAN_RECORD,
- serviceUuids, serviceDataUuid, serviceData,
- manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel,
- localName);
- } catch (IndexOutOfBoundsException e) {
- Log.e(PARSER_TAG,
- "unable to parse scan record: " + Arrays.toString(scanRecord));
- return null;
- }
- }
-
- // Parse service uuids.
- private int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
- int uuidLength, List<ParcelUuid> serviceUuids) {
- while (dataLength > 0) {
- byte[] uuidBytes = extractBytes(scanRecord, currentPos,
- uuidLength);
- serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
- dataLength -= uuidLength;
- currentPos += uuidLength;
- }
- return currentPos;
- }
- }
- }
-
-}
diff --git a/core/java/android/bluetooth/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/BluetoothLeAdvertiser.java
deleted file mode 100644
index 30c90c4..0000000
--- a/core/java/android/bluetooth/BluetoothLeAdvertiser.java
+++ /dev/null
@@ -1,615 +0,0 @@
-/*
- * 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.
- */
-
-package android.bluetooth;
-
-import android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-
-/**
- * This class provides a way to perform Bluetooth LE advertise operations, such as start and stop
- * advertising. An advertiser can broadcast up to 31 bytes of advertisement data represented by
- * {@link BluetoothLeAdvertiseScanData.AdvertisementData}.
- * <p>
- * To get an instance of {@link BluetoothLeAdvertiser}, call the
- * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method.
- * <p>
- * Note most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
- *
- * @see BluetoothLeAdvertiseScanData.AdvertisementData
- */
-public class BluetoothLeAdvertiser {
-
- private static final String TAG = "BluetoothLeAdvertiser";
-
- /**
- * The {@link Settings} provide a way to adjust advertising preferences for each individual
- * advertisement. Use {@link Settings.Builder} to create a {@link Settings} instance.
- */
- public static final class Settings implements Parcelable {
- /**
- * Perform Bluetooth LE advertising in low power mode. This is the default and preferred
- * advertising mode as it consumes the least power.
- */
- public static final int ADVERTISE_MODE_LOW_POWER = 0;
- /**
- * Perform Bluetooth LE advertising in balanced power mode. This is balanced between
- * advertising frequency and power consumption.
- */
- public static final int ADVERTISE_MODE_BALANCED = 1;
- /**
- * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest
- * power consumption and should not be used for background continuous advertising.
- */
- public static final int ADVERTISE_MODE_LOW_LATENCY = 2;
-
- /**
- * Advertise using the lowest transmission(tx) power level. An app can use low transmission
- * power to restrict the visibility range of its advertising packet.
- */
- public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0;
- /**
- * Advertise using low tx power level.
- */
- public static final int ADVERTISE_TX_POWER_LOW = 1;
- /**
- * Advertise using medium tx power level.
- */
- public static final int ADVERTISE_TX_POWER_MEDIUM = 2;
- /**
- * Advertise using high tx power level. This is corresponding to largest visibility range of
- * the advertising packet.
- */
- public static final int ADVERTISE_TX_POWER_HIGH = 3;
-
- /**
- * Non-connectable undirected advertising event, as defined in Bluetooth Specification V4.0
- * vol6, part B, section 4.4.2 - Advertising state.
- */
- public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0;
- /**
- * Scannable undirected advertise type, as defined in same spec mentioned above. This event
- * type allows a scanner to send a scan request asking additional information about the
- * advertiser.
- */
- public static final int ADVERTISE_TYPE_SCANNABLE = 1;
- /**
- * Connectable undirected advertising type, as defined in same spec mentioned above. This
- * event type allows a scanner to send scan request asking additional information about the
- * advertiser. It also allows an initiator to send a connect request for connection.
- */
- public static final int ADVERTISE_TYPE_CONNECTABLE = 2;
-
- private final int mAdvertiseMode;
- private final int mAdvertiseTxPowerLevel;
- private final int mAdvertiseEventType;
-
- private Settings(int advertiseMode, int advertiseTxPowerLevel,
- int advertiseEventType) {
- mAdvertiseMode = advertiseMode;
- mAdvertiseTxPowerLevel = advertiseTxPowerLevel;
- mAdvertiseEventType = advertiseEventType;
- }
-
- private Settings(Parcel in) {
- mAdvertiseMode = in.readInt();
- mAdvertiseTxPowerLevel = in.readInt();
- mAdvertiseEventType = in.readInt();
- }
-
- /**
- * Creates a {@link Builder} to construct a {@link Settings} object.
- */
- public static Builder newBuilder() {
- return new Builder();
- }
-
- /**
- * Returns the advertise mode.
- */
- public int getMode() {
- return mAdvertiseMode;
- }
-
- /**
- * Returns the tx power level for advertising.
- */
- public int getTxPowerLevel() {
- return mAdvertiseTxPowerLevel;
- }
-
- /**
- * Returns the advertise event type.
- */
- public int getType() {
- return mAdvertiseEventType;
- }
-
- @Override
- public String toString() {
- return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel="
- + mAdvertiseTxPowerLevel + ", mAdvertiseEventType=" + mAdvertiseEventType + "]";
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mAdvertiseMode);
- dest.writeInt(mAdvertiseTxPowerLevel);
- dest.writeInt(mAdvertiseEventType);
- }
-
- public static final Parcelable.Creator<Settings> CREATOR =
- new Creator<BluetoothLeAdvertiser.Settings>() {
- @Override
- public Settings[] newArray(int size) {
- return new Settings[size];
- }
-
- @Override
- public Settings createFromParcel(Parcel in) {
- return new Settings(in);
- }
- };
-
- /**
- * Builder class for {@link BluetoothLeAdvertiser.Settings}. Caller should use
- * {@link Settings#newBuilder()} to get an instance of the builder.
- */
- public static final class Builder {
- private int mMode = ADVERTISE_MODE_LOW_POWER;
- private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM;
- private int mType = ADVERTISE_TYPE_NON_CONNECTABLE;
-
- // Private constructor, use Settings.newBuilder() get an instance of BUILDER.
- private Builder() {
- }
-
- /**
- * Set advertise mode to control the advertising power and latency.
- *
- * @param advertiseMode Bluetooth LE Advertising mode, can only be one of
- * {@link Settings#ADVERTISE_MODE_LOW_POWER},
- * {@link Settings#ADVERTISE_MODE_BALANCED}, or
- * {@link Settings#ADVERTISE_MODE_LOW_LATENCY}.
- * @throws IllegalArgumentException If the advertiseMode is invalid.
- */
- public Builder advertiseMode(int advertiseMode) {
- if (advertiseMode < ADVERTISE_MODE_LOW_POWER
- || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) {
- throw new IllegalArgumentException("unknown mode " + advertiseMode);
- }
- mMode = advertiseMode;
- return this;
- }
-
- /**
- * Set advertise tx power level to control the transmission power level for the
- * advertising.
- *
- * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one
- * of {@link Settings#ADVERTISE_TX_POWER_ULTRA_LOW},
- * {@link Settings#ADVERTISE_TX_POWER_LOW},
- * {@link Settings#ADVERTISE_TX_POWER_MEDIUM} or
- * {@link Settings#ADVERTISE_TX_POWER_HIGH}.
- * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
- */
- public Builder txPowerLevel(int txPowerLevel) {
- if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW
- || txPowerLevel > ADVERTISE_TX_POWER_HIGH) {
- throw new IllegalArgumentException("unknown tx power level " + txPowerLevel);
- }
- mTxPowerLevel = txPowerLevel;
- return this;
- }
-
- /**
- * Set advertise type to control the event type of advertising.
- *
- * @param type Bluetooth LE Advertising type, can be either
- * {@link Settings#ADVERTISE_TYPE_NON_CONNECTABLE},
- * {@link Settings#ADVERTISE_TYPE_SCANNABLE} or
- * {@link Settings#ADVERTISE_TYPE_CONNECTABLE}.
- * @throws IllegalArgumentException If the {@code type} is invalid.
- */
- public Builder type(int type) {
- if (type < ADVERTISE_TYPE_NON_CONNECTABLE
- || type > ADVERTISE_TYPE_CONNECTABLE) {
- throw new IllegalArgumentException("unknown advertise type " + type);
- }
- mType = type;
- return this;
- }
-
- /**
- * Build the {@link Settings} object.
- */
- public Settings build() {
- return new Settings(mMode, mTxPowerLevel, mType);
- }
- }
- }
-
- /**
- * Callback of Bluetooth LE advertising, which is used to deliver operation status for start and
- * stop advertising.
- */
- public interface AdvertiseCallback {
-
- /**
- * The operation is success.
- *
- * @hide
- */
- public static final int SUCCESS = 0;
- /**
- * Fails to start advertising as the advertisement data contains services that are not added
- * to the local bluetooth Gatt server.
- */
- public static final int ADVERTISING_SERVICE_UNKNOWN = 1;
- /**
- * Fails to start advertising as system runs out of quota for advertisers.
- */
- public static final int TOO_MANY_ADVERTISERS = 2;
-
- /**
- * Fails to start advertising as the advertising is already started.
- */
- public static final int ADVERTISING_ALREADY_STARTED = 3;
- /**
- * Fails to stop advertising as the advertising is not started.
- */
- public static final int ADVERISING_NOT_STARTED = 4;
-
- /**
- * Operation fails due to bluetooth controller failure.
- */
- public static final int CONTROLLER_FAILURE = 5;
-
- /**
- * Callback when advertising operation succeeds.
- *
- * @param settingsInEffect The actual settings used for advertising, which may be different
- * from what the app asks.
- */
- public void onSuccess(Settings settingsInEffect);
-
- /**
- * Callback when advertising operation fails.
- *
- * @param errorCode Error code for failures.
- */
- public void onFailure(int errorCode);
- }
-
- private final IBluetoothGatt mBluetoothGatt;
- private final Handler mHandler;
- private final Map<Settings, AdvertiseCallbackWrapper>
- mLeAdvertisers = new HashMap<Settings, AdvertiseCallbackWrapper>();
-
- // Package private constructor, use BluetoothAdapter.getLeAdvertiser() instead.
- BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) {
- mBluetoothGatt = bluetoothGatt;
- mHandler = new Handler(Looper.getMainLooper());
- }
-
- /**
- * Bluetooth GATT interface callbacks for advertising.
- */
- private static class AdvertiseCallbackWrapper extends IBluetoothGattCallback.Stub {
- private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000;
- private final AdvertiseCallback mAdvertiseCallback;
- private final AdvertisementData mAdvertisement;
- private final AdvertisementData mScanResponse;
- private final Settings mSettings;
- private final IBluetoothGatt mBluetoothGatt;
-
- // mLeHandle 0: not registered
- // -1: scan stopped
- // >0: registered and scan started
- private int mLeHandle;
- private boolean isAdvertising = false;
-
- public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,
- AdvertisementData advertiseData, AdvertisementData scanResponse, Settings settings,
- IBluetoothGatt bluetoothGatt) {
- mAdvertiseCallback = advertiseCallback;
- mAdvertisement = advertiseData;
- mScanResponse = scanResponse;
- mSettings = settings;
- mBluetoothGatt = bluetoothGatt;
- mLeHandle = 0;
- }
-
- public boolean advertiseStarted() {
- boolean started = false;
- synchronized (this) {
- if (mLeHandle == -1) {
- return false;
- }
- try {
- wait(LE_CALLBACK_TIMEOUT_MILLIS);
- } catch (InterruptedException e) {
- Log.e(TAG, "Callback reg wait interrupted: " + e);
- }
- started = (mLeHandle > 0 && isAdvertising);
- }
- return started;
- }
-
- public boolean advertiseStopped() {
- synchronized (this) {
- try {
- wait(LE_CALLBACK_TIMEOUT_MILLIS);
- } catch (InterruptedException e) {
- Log.e(TAG, "Callback reg wait interrupted: " + e);
- }
- return !isAdvertising;
- }
- }
-
- /**
- * Application interface registered - app is ready to go
- */
- @Override
- public void onClientRegistered(int status, int clientIf) {
- Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf);
- synchronized (this) {
- if (status == BluetoothGatt.GATT_SUCCESS) {
- mLeHandle = clientIf;
- try {
- mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement,
- mScanResponse, mSettings);
- } catch (RemoteException e) {
- Log.e(TAG, "fail to start le advertise: " + e);
- mLeHandle = -1;
- notifyAll();
- } catch (Exception e) {
- Log.e(TAG, "fail to start advertise: " + e.getStackTrace());
- }
- } else {
- // registration failed
- mLeHandle = -1;
- notifyAll();
- }
- }
- }
-
- @Override
- public void onClientConnectionState(int status, int clientIf,
- boolean connected, String address) {
- // no op
- }
-
- @Override
- public void onScanResult(String address, int rssi, byte[] advData) {
- // no op
- }
-
- @Override
- public void onGetService(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid) {
- // no op
- }
-
- @Override
- public void onGetIncludedService(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int inclSrvcType, int inclSrvcInstId,
- ParcelUuid inclSrvcUuid) {
- // no op
- }
-
- @Override
- public void onGetCharacteristic(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int charProps) {
- // no op
- }
-
- @Override
- public void onGetDescriptor(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int descInstId, ParcelUuid descUuid) {
- // no op
- }
-
- @Override
- public void onSearchComplete(String address, int status) {
- // no op
- }
-
- @Override
- public void onCharacteristicRead(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid, byte[] value) {
- // no op
- }
-
- @Override
- public void onCharacteristicWrite(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid) {
- // no op
- }
-
- @Override
- public void onNotify(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- byte[] value) {
- // no op
- }
-
- @Override
- public void onDescriptorRead(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int descInstId, ParcelUuid descrUuid, byte[] value) {
- // no op
- }
-
- @Override
- public void onDescriptorWrite(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int descInstId, ParcelUuid descrUuid) {
- // no op
- }
-
- @Override
- public void onExecuteWrite(String address, int status) {
- // no op
- }
-
- @Override
- public void onReadRemoteRssi(String address, int rssi, int status) {
- // no op
- }
-
- @Override
- public void onAdvertiseStateChange(int advertiseState, int status) {
- // no op
- }
-
- @Override
- public void onMultiAdvertiseCallback(int status) {
- synchronized (this) {
- if (status == 0) {
- isAdvertising = !isAdvertising;
- if (!isAdvertising) {
- try {
- mBluetoothGatt.unregisterClient(mLeHandle);
- mLeHandle = -1;
- } catch (RemoteException e) {
- Log.e(TAG, "remote exception when unregistering", e);
- }
- }
- mAdvertiseCallback.onSuccess(null);
- } else {
- mAdvertiseCallback.onFailure(status);
- }
- notifyAll();
- }
-
- }
-
- /**
- * Callback reporting LE ATT MTU.
- *
- * @hide
- */
- public void onConfigureMTU(String address, int mtu, int status) {
- // no op
- }
- }
-
- /**
- * Start Bluetooth LE Advertising.
- *
- * @param settings {@link Settings} for Bluetooth LE advertising.
- * @param advertiseData {@link AdvertisementData} to be advertised.
- * @param callback {@link AdvertiseCallback} for advertising status.
- */
- public void startAdvertising(Settings settings,
- AdvertisementData advertiseData, final AdvertiseCallback callback) {
- startAdvertising(settings, advertiseData, null, callback);
- }
-
- /**
- * Start Bluetooth LE Advertising.
- * @param settings {@link Settings} for Bluetooth LE advertising.
- * @param advertiseData {@link AdvertisementData} to be advertised in advertisement packet.
- * @param scanResponse {@link AdvertisementData} for scan response.
- * @param callback {@link AdvertiseCallback} for advertising status.
- */
- public void startAdvertising(Settings settings,
- AdvertisementData advertiseData, AdvertisementData scanResponse,
- final AdvertiseCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- if (mLeAdvertisers.containsKey(settings)) {
- postCallbackFailure(callback, AdvertiseCallback.ADVERTISING_ALREADY_STARTED);
- return;
- }
- AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData,
- scanResponse, settings, mBluetoothGatt);
- UUID uuid = UUID.randomUUID();
- try {
- mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
- if (wrapper.advertiseStarted()) {
- mLeAdvertisers.put(settings, wrapper);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "failed to stop advertising", e);
- }
- }
-
- /**
- * Stop Bluetooth LE advertising. Returns immediately, the operation status will be delivered
- * through the {@code callback}.
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- *
- * @param settings {@link Settings} used to start Bluetooth LE advertising.
- * @param callback {@link AdvertiseCallback} for delivering stopping advertising status.
- */
- public void stopAdvertising(final Settings settings, final AdvertiseCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(settings);
- if (wrapper == null) {
- postCallbackFailure(callback, AdvertiseCallback.ADVERISING_NOT_STARTED);
- return;
- }
- try {
- mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle);
- if (wrapper.advertiseStopped()) {
- mLeAdvertisers.remove(settings);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "failed to stop advertising", e);
- }
- }
-
- private void postCallbackFailure(final AdvertiseCallback callback, final int error) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- callback.onFailure(error);
- }
- });
- }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeScanner.java b/core/java/android/bluetooth/BluetoothLeScanner.java
deleted file mode 100644
index ed3188b..0000000
--- a/core/java/android/bluetooth/BluetoothLeScanner.java
+++ /dev/null
@@ -1,759 +0,0 @@
-/*
- * 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.
- */
-
-package android.bluetooth;
-
-import android.annotation.Nullable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class provides methods to perform scan related operations for Bluetooth LE devices. An
- * application can scan for a particular type of BLE devices using {@link BluetoothLeScanFilter}. It
- * can also request different types of callbacks for delivering the result.
- * <p>
- * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of
- * {@link BluetoothLeScanner}.
- * <p>
- * Note most of the scan methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
- *
- * @see BluetoothLeScanFilter
- */
-public class BluetoothLeScanner {
-
- private static final String TAG = "BluetoothLeScanner";
- private static final boolean DBG = true;
-
- /**
- * Settings for Bluetooth LE scan.
- */
- public static final class Settings implements Parcelable {
- /**
- * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes
- * the least power.
- */
- public static final int SCAN_MODE_LOW_POWER = 0;
- /**
- * Perform Bluetooth LE scan in balanced power mode.
- */
- public static final int SCAN_MODE_BALANCED = 1;
- /**
- * Scan using highest duty cycle. It's recommended only using this mode when the application
- * is running in foreground.
- */
- public static final int SCAN_MODE_LOW_LATENCY = 2;
-
- /**
- * Callback each time when a bluetooth advertisement is found.
- */
- public static final int CALLBACK_TYPE_ON_UPDATE = 0;
- /**
- * Callback when a bluetooth advertisement is found for the first time.
- */
- public static final int CALLBACK_TYPE_ON_FOUND = 1;
- /**
- * Callback when a bluetooth advertisement is found for the first time, then lost.
- */
- public static final int CALLBACK_TYPE_ON_LOST = 2;
-
- /**
- * Full scan result which contains device mac address, rssi, advertising and scan response
- * and scan timestamp.
- */
- public static final int SCAN_RESULT_TYPE_FULL = 0;
- /**
- * Truncated scan result which contains device mac address, rssi and scan timestamp. Note
- * it's possible for an app to get more scan results that it asks if there are multiple apps
- * using this type. TODO: decide whether we could unhide this setting.
- *
- * @hide
- */
- public static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
-
- // Bluetooth LE scan mode.
- private int mScanMode;
-
- // Bluetooth LE scan callback type
- private int mCallbackType;
-
- // Bluetooth LE scan result type
- private int mScanResultType;
-
- // Time of delay for reporting the scan result
- private long mReportDelayMicros;
-
- public int getScanMode() {
- return mScanMode;
- }
-
- public int getCallbackType() {
- return mCallbackType;
- }
-
- public int getScanResultType() {
- return mScanResultType;
- }
-
- /**
- * Returns report delay timestamp based on the device clock.
- */
- public long getReportDelayMicros() {
- return mReportDelayMicros;
- }
-
- /**
- * Creates a new {@link Builder} to build {@link Settings} object.
- */
- public static Builder newBuilder() {
- return new Builder();
- }
-
- private Settings(int scanMode, int callbackType, int scanResultType,
- long reportDelayMicros) {
- mScanMode = scanMode;
- mCallbackType = callbackType;
- mScanResultType = scanResultType;
- mReportDelayMicros = reportDelayMicros;
- }
-
- private Settings(Parcel in) {
- mScanMode = in.readInt();
- mCallbackType = in.readInt();
- mScanResultType = in.readInt();
- mReportDelayMicros = in.readLong();
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mScanMode);
- dest.writeInt(mCallbackType);
- dest.writeInt(mScanResultType);
- dest.writeLong(mReportDelayMicros);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public static final Parcelable.Creator<Settings> CREATOR = new Creator<Settings>() {
- @Override
- public Settings[] newArray(int size) {
- return new Settings[size];
- }
-
- @Override
- public Settings createFromParcel(Parcel in) {
- return new Settings(in);
- }
- };
-
- /**
- * Builder for {@link BluetoothLeScanner.Settings}.
- */
- public static class Builder {
- private int mScanMode = SCAN_MODE_LOW_POWER;
- private int mCallbackType = CALLBACK_TYPE_ON_UPDATE;
- private int mScanResultType = SCAN_RESULT_TYPE_FULL;
- private long mReportDelayMicros = 0;
-
- // Hidden constructor.
- private Builder() {
- }
-
- /**
- * Set scan mode for Bluetooth LE scan.
- *
- * @param scanMode The scan mode can be one of {@link Settings#SCAN_MODE_LOW_POWER},
- * {@link Settings#SCAN_MODE_BALANCED} or
- * {@link Settings#SCAN_MODE_LOW_LATENCY}.
- * @throws IllegalArgumentException If the {@code scanMode} is invalid.
- */
- public Builder scanMode(int scanMode) {
- if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) {
- throw new IllegalArgumentException("invalid scan mode " + scanMode);
- }
- mScanMode = scanMode;
- return this;
- }
-
- /**
- * Set callback type for Bluetooth LE scan.
- *
- * @param callbackType The callback type for the scan. Can be either one of
- * {@link Settings#CALLBACK_TYPE_ON_UPDATE},
- * {@link Settings#CALLBACK_TYPE_ON_FOUND} or
- * {@link Settings#CALLBACK_TYPE_ON_LOST}.
- * @throws IllegalArgumentException If the {@code callbackType} is invalid.
- */
- public Builder callbackType(int callbackType) {
- if (callbackType < CALLBACK_TYPE_ON_UPDATE
- || callbackType > CALLBACK_TYPE_ON_LOST) {
- throw new IllegalArgumentException("invalid callback type - " + callbackType);
- }
- mCallbackType = callbackType;
- return this;
- }
-
- /**
- * Set scan result type for Bluetooth LE scan.
- *
- * @param scanResultType Type for scan result, could be either
- * {@link Settings#SCAN_RESULT_TYPE_FULL} or
- * {@link Settings#SCAN_RESULT_TYPE_TRUNCATED}.
- * @throws IllegalArgumentException If the {@code scanResultType} is invalid.
- * @hide
- */
- public Builder scanResultType(int scanResultType) {
- if (scanResultType < SCAN_RESULT_TYPE_FULL
- || scanResultType > SCAN_RESULT_TYPE_TRUNCATED) {
- throw new IllegalArgumentException(
- "invalid scanResultType - " + scanResultType);
- }
- mScanResultType = scanResultType;
- return this;
- }
-
- /**
- * Set report delay timestamp for Bluetooth LE scan.
- */
- public Builder reportDelayMicros(long reportDelayMicros) {
- mReportDelayMicros = reportDelayMicros;
- return this;
- }
-
- /**
- * Build {@link Settings}.
- */
- public Settings build() {
- return new Settings(mScanMode, mCallbackType, mScanResultType, mReportDelayMicros);
- }
- }
- }
-
- /**
- * ScanResult for Bluetooth LE scan.
- */
- public static final class ScanResult implements Parcelable {
- // Remote bluetooth device.
- private BluetoothDevice mDevice;
-
- // Scan record, including advertising data and scan response data.
- private byte[] mScanRecord;
-
- // Received signal strength.
- private int mRssi;
-
- // Device timestamp when the result was last seen.
- private long mTimestampMicros;
-
- // Constructor of scan result.
- public ScanResult(BluetoothDevice device, byte[] scanRecord, int rssi, long timestampMicros) {
- mDevice = device;
- mScanRecord = scanRecord;
- mRssi = rssi;
- mTimestampMicros = timestampMicros;
- }
-
- private ScanResult(Parcel in) {
- readFromParcel(in);
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- if (mDevice != null) {
- dest.writeInt(1);
- mDevice.writeToParcel(dest, flags);
- } else {
- dest.writeInt(0);
- }
- if (mScanRecord != null) {
- dest.writeInt(1);
- dest.writeByteArray(mScanRecord);
- } else {
- dest.writeInt(0);
- }
- dest.writeInt(mRssi);
- dest.writeLong(mTimestampMicros);
- }
-
- private void readFromParcel(Parcel in) {
- if (in.readInt() == 1) {
- mDevice = BluetoothDevice.CREATOR.createFromParcel(in);
- }
- if (in.readInt() == 1) {
- mScanRecord = in.createByteArray();
- }
- mRssi = in.readInt();
- mTimestampMicros = in.readLong();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * Returns the remote bluetooth device identified by the bluetooth device address.
- */
- @Nullable
- public BluetoothDevice getDevice() {
- return mDevice;
- }
-
- @Nullable /**
- * Returns the scan record, which can be a combination of advertisement and scan response.
- */
- public byte[] getScanRecord() {
- return mScanRecord;
- }
-
- /**
- * Returns the received signal strength in dBm. The valid range is [-127, 127].
- */
- public int getRssi() {
- return mRssi;
- }
-
- /**
- * Returns timestamp since boot when the scan record was observed.
- */
- public long getTimestampMicros() {
- return mTimestampMicros;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampMicros);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
- ScanResult other = (ScanResult) obj;
- return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) &&
- Objects.deepEquals(mScanRecord, other.mScanRecord)
- && (mTimestampMicros == other.mTimestampMicros);
- }
-
- @Override
- public String toString() {
- return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord="
- + Arrays.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampMicros="
- + mTimestampMicros + '}';
- }
-
- public static final Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() {
- @Override
- public ScanResult createFromParcel(Parcel source) {
- return new ScanResult(source);
- }
-
- @Override
- public ScanResult[] newArray(int size) {
- return new ScanResult[size];
- }
- };
-
- }
-
- /**
- * Callback of Bluetooth LE scans. The results of the scans will be delivered through the
- * callbacks.
- */
- public interface ScanCallback {
- /**
- * Callback when any BLE beacon is found.
- *
- * @param result A Bluetooth LE scan result.
- */
- public void onDeviceUpdate(ScanResult result);
-
- /**
- * Callback when the BLE beacon is found for the first time.
- *
- * @param result The Bluetooth LE scan result when the onFound event is triggered.
- */
- public void onDeviceFound(ScanResult result);
-
- /**
- * Callback when the BLE device was lost. Note a device has to be "found" before it's lost.
- *
- * @param device The Bluetooth device that is lost.
- */
- public void onDeviceLost(BluetoothDevice device);
-
- /**
- * Callback when batch results are delivered.
- *
- * @param results List of scan results that are previously scanned.
- */
- public void onBatchScanResults(List<ScanResult> results);
-
- /**
- * Fails to start scan as BLE scan with the same settings is already started by the app.
- */
- public static final int SCAN_ALREADY_STARTED = 1;
- /**
- * Fails to start scan as app cannot be registered.
- */
- public static final int APPLICATION_REGISTRATION_FAILED = 2;
- /**
- * Fails to start scan due to gatt service failure.
- */
- public static final int GATT_SERVICE_FAILURE = 3;
- /**
- * Fails to start scan due to controller failure.
- */
- public static final int CONTROLLER_FAILURE = 4;
-
- /**
- * Callback when scan failed.
- */
- public void onScanFailed(int errorCode);
- }
-
- private final IBluetoothGatt mBluetoothGatt;
- private final Handler mHandler;
- private final Map<Settings, BleScanCallbackWrapper> mLeScanClients;
-
- BluetoothLeScanner(IBluetoothGatt bluetoothGatt) {
- mBluetoothGatt = bluetoothGatt;
- mHandler = new Handler(Looper.getMainLooper());
- mLeScanClients = new HashMap<Settings, BleScanCallbackWrapper>();
- }
-
- /**
- * Bluetooth GATT interface callbacks
- */
- private static class BleScanCallbackWrapper extends IBluetoothGattCallback.Stub {
- private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5;
-
- private final ScanCallback mScanCallback;
- private final List<BluetoothLeScanFilter> mFilters;
- private Settings mSettings;
- private IBluetoothGatt mBluetoothGatt;
-
- // mLeHandle 0: not registered
- // -1: scan stopped
- // > 0: registered and scan started
- private int mLeHandle;
-
- public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
- List<BluetoothLeScanFilter> filters, Settings settings, ScanCallback scanCallback) {
- mBluetoothGatt = bluetoothGatt;
- mFilters = filters;
- mSettings = settings;
- mScanCallback = scanCallback;
- mLeHandle = 0;
- }
-
- public boolean scanStarted() {
- synchronized (this) {
- if (mLeHandle == -1) {
- return false;
- }
- try {
- wait(REGISTRATION_CALLBACK_TIMEOUT_SECONDS);
- } catch (InterruptedException e) {
- Log.e(TAG, "Callback reg wait interrupted: " + e);
- }
- }
- return mLeHandle > 0;
- }
-
- public void stopLeScan() {
- synchronized (this) {
- if (mLeHandle <= 0) {
- Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
- return;
- }
- try {
- mBluetoothGatt.stopScan(mLeHandle, false);
- mBluetoothGatt.unregisterClient(mLeHandle);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to stop scan and unregister" + e);
- }
- mLeHandle = -1;
- notifyAll();
- }
- }
-
- /**
- * Application interface registered - app is ready to go
- */
- @Override
- public void onClientRegistered(int status, int clientIf) {
- Log.d(TAG, "onClientRegistered() - status=" + status +
- " clientIf=" + clientIf);
-
- synchronized (this) {
- if (mLeHandle == -1) {
- if (DBG)
- Log.d(TAG, "onClientRegistered LE scan canceled");
- }
-
- if (status == BluetoothGatt.GATT_SUCCESS) {
- mLeHandle = clientIf;
- try {
- mBluetoothGatt.startScanWithFilters(mLeHandle, false, mSettings, mFilters);
- } catch (RemoteException e) {
- Log.e(TAG, "fail to start le scan: " + e);
- mLeHandle = -1;
- }
- } else {
- // registration failed
- mLeHandle = -1;
- }
- notifyAll();
- }
- }
-
- @Override
- public void onClientConnectionState(int status, int clientIf,
- boolean connected, String address) {
- // no op
- }
-
- /**
- * Callback reporting an LE scan result.
- *
- * @hide
- */
- @Override
- public void onScanResult(String address, int rssi, byte[] advData) {
- if (DBG)
- Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" + rssi);
-
- // Check null in case the scan has been stopped
- synchronized (this) {
- if (mLeHandle <= 0)
- return;
- }
- BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
- address);
- long scanMicros = TimeUnit.NANOSECONDS.toMicros(SystemClock.elapsedRealtimeNanos());
- ScanResult result = new ScanResult(device, advData, rssi,
- scanMicros);
- mScanCallback.onDeviceUpdate(result);
- }
-
- @Override
- public void onGetService(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid) {
- // no op
- }
-
- @Override
- public void onGetIncludedService(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int inclSrvcType, int inclSrvcInstId,
- ParcelUuid inclSrvcUuid) {
- // no op
- }
-
- @Override
- public void onGetCharacteristic(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int charProps) {
- // no op
- }
-
- @Override
- public void onGetDescriptor(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int descInstId, ParcelUuid descUuid) {
- // no op
- }
-
- @Override
- public void onSearchComplete(String address, int status) {
- // no op
- }
-
- @Override
- public void onCharacteristicRead(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid, byte[] value) {
- // no op
- }
-
- @Override
- public void onCharacteristicWrite(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid) {
- // no op
- }
-
- @Override
- public void onNotify(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- byte[] value) {
- // no op
- }
-
- @Override
- public void onDescriptorRead(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int descInstId, ParcelUuid descrUuid, byte[] value) {
- // no op
- }
-
- @Override
- public void onDescriptorWrite(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int descInstId, ParcelUuid descrUuid) {
- // no op
- }
-
- @Override
- public void onExecuteWrite(String address, int status) {
- // no op
- }
-
- @Override
- public void onReadRemoteRssi(String address, int rssi, int status) {
- // no op
- }
-
- @Override
- public void onAdvertiseStateChange(int advertiseState, int status) {
- // no op
- }
-
- @Override
- public void onMultiAdvertiseCallback(int status) {
- // no op
- }
-
- @Override
- public void onConfigureMTU(String address, int mtu, int status) {
- // no op
- }
- }
-
- /**
- * Scan Bluetooth LE scan. The scan results will be delivered through {@code callback}.
- *
- * @param filters {@link BluetoothLeScanFilter}s for finding exact BLE devices.
- * @param settings Settings for ble scan.
- * @param callback Callback when scan results are delivered.
- * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
- */
- public void startScan(List<BluetoothLeScanFilter> filters, Settings settings,
- final ScanCallback callback) {
- if (settings == null || callback == null) {
- throw new IllegalArgumentException("settings or callback is null");
- }
- synchronized (mLeScanClients) {
- if (mLeScanClients.get(settings) != null) {
- postCallbackError(callback, ScanCallback.SCAN_ALREADY_STARTED);
- return;
- }
- BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters,
- settings, callback);
- try {
- UUID uuid = UUID.randomUUID();
- mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
- if (wrapper.scanStarted()) {
- mLeScanClients.put(settings, wrapper);
- } else {
- postCallbackError(callback, ScanCallback.APPLICATION_REGISTRATION_FAILED);
- return;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "GATT service exception when starting scan", e);
- postCallbackError(callback, ScanCallback.GATT_SERVICE_FAILURE);
- }
- }
- }
-
- private void postCallbackError(final ScanCallback callback, final int errorCode) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- callback.onScanFailed(errorCode);
- }
- });
- }
-
- /**
- * Stop Bluetooth LE scan.
- *
- * @param settings The same settings as used in {@link #startScan}, which is used to identify
- * the BLE scan.
- */
- public void stopScan(Settings settings) {
- synchronized (mLeScanClients) {
- BleScanCallbackWrapper wrapper = mLeScanClients.remove(settings);
- if (wrapper == null) {
- return;
- }
- wrapper.stopLeScan();
- }
- }
-
- /**
- * Returns available storage size for batch scan results. It's recommended not to use batch scan
- * if available storage size is small (less than 1k bytes, for instance).
- *
- * @hide TODO: unhide when batching is supported in stack.
- */
- public int getAvailableBatchStorageSizeBytes() {
- throw new UnsupportedOperationException("not impelemented");
- }
-
- /**
- * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results
- * batched on bluetooth controller.
- *
- * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
- * used to start scan.
- * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will
- * get batch scan callback if the batch scan buffer is flushed.
- * @return Batch Scan results.
- * @hide TODO: unhide when batching is supported in stack.
- */
- public List<ScanResult> getBatchScanResults(ScanCallback callback, boolean flush) {
- throw new UnsupportedOperationException("not impelemented");
- }
-
-}
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index ceed52b..00a0750 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -17,10 +17,10 @@
package android.bluetooth;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothLeAdvertiseScanData;
-import android.bluetooth.BluetoothLeAdvertiser;
-import android.bluetooth.BluetoothLeScanFilter;
-import android.bluetooth.BluetoothLeScanner;
+import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.AdvertisementData;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanSettings;
import android.os.ParcelUuid;
import android.bluetooth.IBluetoothGattCallback;
@@ -38,13 +38,12 @@ interface IBluetoothGatt {
void startScanWithUuidsScanParam(in int appIf, in boolean isServer,
in ParcelUuid[] ids, int scanWindow, int scanInterval);
void startScanWithFilters(in int appIf, in boolean isServer,
- in BluetoothLeScanner.Settings settings,
- in List<BluetoothLeScanFilter> filters);
+ in ScanSettings settings, in List<ScanFilter> filters);
void stopScan(in int appIf, in boolean isServer);
void startMultiAdvertising(in int appIf,
- in BluetoothLeAdvertiseScanData.AdvertisementData advertiseData,
- in BluetoothLeAdvertiseScanData.AdvertisementData scanResponse,
- in BluetoothLeAdvertiser.Settings settings);
+ in AdvertisementData advertiseData,
+ in AdvertisementData scanResponse,
+ in AdvertiseSettings settings);
void stopMultiAdvertising(in int appIf);
void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback);
void unregisterClient(in int clientIf);
diff --git a/core/java/android/bluetooth/le/AdvertiseCallback.java b/core/java/android/bluetooth/le/AdvertiseCallback.java
new file mode 100644
index 0000000..f1334c2
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertiseCallback.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth.le;
+
+/**
+ * Callback of Bluetooth LE advertising, which is used to deliver advertising operation status.
+ */
+public abstract class AdvertiseCallback {
+
+ /**
+ * The operation is success.
+ *
+ * @hide
+ */
+ public static final int SUCCESS = 0;
+ /**
+ * Fails to start advertising as the advertisement data contains services that are not added to
+ * the local bluetooth GATT server.
+ */
+ public static final int ADVERTISE_FAILED_SERVICE_UNKNOWN = 1;
+ /**
+ * Fails to start advertising as system runs out of quota for advertisers.
+ */
+ public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2;
+
+ /**
+ * Fails to start advertising as the advertising is already started.
+ */
+ public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3;
+ /**
+ * Fails to stop advertising as the advertising is not started.
+ */
+ public static final int ADVERTISE_FAILED_NOT_STARTED = 4;
+
+ /**
+ * Operation fails due to bluetooth controller failure.
+ */
+ public static final int ADVERTISE_FAILED_CONTROLLER_FAILURE = 5;
+
+ /**
+ * Callback when advertising operation succeeds.
+ *
+ * @param settingsInEffect The actual settings used for advertising, which may be different from
+ * what the app asks.
+ */
+ public abstract void onSuccess(AdvertiseSettings settingsInEffect);
+
+ /**
+ * Callback when advertising operation fails.
+ *
+ * @param errorCode Error code for failures.
+ */
+ public abstract void onFailure(int errorCode);
+}
diff --git a/core/java/android/bluetooth/BluetoothLeScanFilter.aidl b/core/java/android/bluetooth/le/AdvertiseSettings.aidl
index 86ee06d..9f47d74 100644
--- a/core/java/android/bluetooth/BluetoothLeScanFilter.aidl
+++ b/core/java/android/bluetooth/le/AdvertiseSettings.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.bluetooth;
+package android.bluetooth.le;
-parcelable BluetoothLeScanFilter;
+parcelable AdvertiseSettings; \ No newline at end of file
diff --git a/core/java/android/bluetooth/le/AdvertiseSettings.java b/core/java/android/bluetooth/le/AdvertiseSettings.java
new file mode 100644
index 0000000..87d0346
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertiseSettings.java
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth.le;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The {@link AdvertiseSettings} provide a way to adjust advertising preferences for each
+ * individual advertisement. Use {@link AdvertiseSettings.Builder} to create an instance.
+ */
+public final class AdvertiseSettings implements Parcelable {
+ /**
+ * Perform Bluetooth LE advertising in low power mode. This is the default and preferred
+ * advertising mode as it consumes the least power.
+ */
+ public static final int ADVERTISE_MODE_LOW_POWER = 0;
+ /**
+ * Perform Bluetooth LE advertising in balanced power mode. This is balanced between advertising
+ * frequency and power consumption.
+ */
+ public static final int ADVERTISE_MODE_BALANCED = 1;
+ /**
+ * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest power
+ * consumption and should not be used for background continuous advertising.
+ */
+ public static final int ADVERTISE_MODE_LOW_LATENCY = 2;
+
+ /**
+ * Advertise using the lowest transmission(tx) power level. An app can use low transmission
+ * power to restrict the visibility range of its advertising packet.
+ */
+ public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0;
+ /**
+ * Advertise using low tx power level.
+ */
+ public static final int ADVERTISE_TX_POWER_LOW = 1;
+ /**
+ * Advertise using medium tx power level.
+ */
+ public static final int ADVERTISE_TX_POWER_MEDIUM = 2;
+ /**
+ * Advertise using high tx power level. This is corresponding to largest visibility range of the
+ * advertising packet.
+ */
+ public static final int ADVERTISE_TX_POWER_HIGH = 3;
+
+ /**
+ * Non-connectable undirected advertising event, as defined in Bluetooth Specification V4.1
+ * vol6, part B, section 4.4.2 - Advertising state.
+ */
+ public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0;
+ /**
+ * Scannable undirected advertise type, as defined in same spec mentioned above. This event type
+ * allows a scanner to send a scan request asking additional information about the advertiser.
+ */
+ public static final int ADVERTISE_TYPE_SCANNABLE = 1;
+ /**
+ * Connectable undirected advertising type, as defined in same spec mentioned above. This event
+ * type allows a scanner to send scan request asking additional information about the
+ * advertiser. It also allows an initiator to send a connect request for connection.
+ */
+ public static final int ADVERTISE_TYPE_CONNECTABLE = 2;
+
+ private final int mAdvertiseMode;
+ private final int mAdvertiseTxPowerLevel;
+ private final int mAdvertiseEventType;
+
+ private AdvertiseSettings(int advertiseMode, int advertiseTxPowerLevel,
+ int advertiseEventType) {
+ mAdvertiseMode = advertiseMode;
+ mAdvertiseTxPowerLevel = advertiseTxPowerLevel;
+ mAdvertiseEventType = advertiseEventType;
+ }
+
+ private AdvertiseSettings(Parcel in) {
+ mAdvertiseMode = in.readInt();
+ mAdvertiseTxPowerLevel = in.readInt();
+ mAdvertiseEventType = in.readInt();
+ }
+
+ /**
+ * Returns the advertise mode.
+ */
+ public int getMode() {
+ return mAdvertiseMode;
+ }
+
+ /**
+ * Returns the tx power level for advertising.
+ */
+ public int getTxPowerLevel() {
+ return mAdvertiseTxPowerLevel;
+ }
+
+ /**
+ * Returns the advertise event type.
+ */
+ public int getType() {
+ return mAdvertiseEventType;
+ }
+
+ @Override
+ public String toString() {
+ return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel="
+ + mAdvertiseTxPowerLevel + ", mAdvertiseEventType=" + mAdvertiseEventType + "]";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mAdvertiseMode);
+ dest.writeInt(mAdvertiseTxPowerLevel);
+ dest.writeInt(mAdvertiseEventType);
+ }
+
+ public static final Parcelable.Creator<AdvertiseSettings> CREATOR =
+ new Creator<AdvertiseSettings>() {
+ @Override
+ public AdvertiseSettings[] newArray(int size) {
+ return new AdvertiseSettings[size];
+ }
+
+ @Override
+ public AdvertiseSettings createFromParcel(Parcel in) {
+ return new AdvertiseSettings(in);
+ }
+ };
+
+ /**
+ * Builder class for {@link AdvertiseSettings}.
+ */
+ public static final class Builder {
+ private int mMode = ADVERTISE_MODE_LOW_POWER;
+ private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM;
+ private int mType = ADVERTISE_TYPE_NON_CONNECTABLE;
+
+ /**
+ * Set advertise mode to control the advertising power and latency.
+ *
+ * @param advertiseMode Bluetooth LE Advertising mode, can only be one of
+ * {@link AdvertiseSettings#ADVERTISE_MODE_LOW_POWER},
+ * {@link AdvertiseSettings#ADVERTISE_MODE_BALANCED}, or
+ * {@link AdvertiseSettings#ADVERTISE_MODE_LOW_LATENCY}.
+ * @throws IllegalArgumentException If the advertiseMode is invalid.
+ */
+ public Builder setAdvertiseMode(int advertiseMode) {
+ if (advertiseMode < ADVERTISE_MODE_LOW_POWER
+ || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) {
+ throw new IllegalArgumentException("unknown mode " + advertiseMode);
+ }
+ mMode = advertiseMode;
+ return this;
+ }
+
+ /**
+ * Set advertise tx power level to control the transmission power level for the advertising.
+ *
+ * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one of
+ * {@link AdvertiseSettings#ADVERTISE_TX_POWER_ULTRA_LOW},
+ * {@link AdvertiseSettings#ADVERTISE_TX_POWER_LOW},
+ * {@link AdvertiseSettings#ADVERTISE_TX_POWER_MEDIUM} or
+ * {@link AdvertiseSettings#ADVERTISE_TX_POWER_HIGH}.
+ * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
+ */
+ public Builder setTxPowerLevel(int txPowerLevel) {
+ if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW
+ || txPowerLevel > ADVERTISE_TX_POWER_HIGH) {
+ throw new IllegalArgumentException("unknown tx power level " + txPowerLevel);
+ }
+ mTxPowerLevel = txPowerLevel;
+ return this;
+ }
+
+ /**
+ * Set advertise type to control the event type of advertising.
+ *
+ * @param type Bluetooth LE Advertising type, can be either
+ * {@link AdvertiseSettings#ADVERTISE_TYPE_NON_CONNECTABLE},
+ * {@link AdvertiseSettings#ADVERTISE_TYPE_SCANNABLE} or
+ * {@link AdvertiseSettings#ADVERTISE_TYPE_CONNECTABLE}.
+ * @throws IllegalArgumentException If the {@code type} is invalid.
+ */
+ public Builder setType(int type) {
+ if (type < ADVERTISE_TYPE_NON_CONNECTABLE
+ || type > ADVERTISE_TYPE_CONNECTABLE) {
+ throw new IllegalArgumentException("unknown advertise type " + type);
+ }
+ mType = type;
+ return this;
+ }
+
+ /**
+ * Build the {@link AdvertiseSettings} object.
+ */
+ public AdvertiseSettings build() {
+ return new AdvertiseSettings(mMode, mTxPowerLevel, mType);
+ }
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothLeAdvertiser.aidl b/core/java/android/bluetooth/le/AdvertisementData.aidl
index 3108610..3da1321 100644
--- a/core/java/android/bluetooth/BluetoothLeAdvertiser.aidl
+++ b/core/java/android/bluetooth/le/AdvertisementData.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.bluetooth;
+package android.bluetooth.le;
-parcelable BluetoothLeAdvertiser.Settings; \ No newline at end of file
+parcelable AdvertisementData; \ No newline at end of file
diff --git a/core/java/android/bluetooth/le/AdvertisementData.java b/core/java/android/bluetooth/le/AdvertisementData.java
new file mode 100644
index 0000000..c587204
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertisementData.java
@@ -0,0 +1,344 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth.le;
+
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothUuid;
+import android.os.Parcel;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Advertisement data packet for Bluetooth LE advertising. This represents the data to be
+ * broadcasted in Bluetooth LE advertising as well as the scan response for active scan.
+ * <p>
+ * Use {@link AdvertisementData.Builder} to create an instance of {@link AdvertisementData} to be
+ * advertised.
+ *
+ * @see BluetoothLeAdvertiser
+ * @see ScanRecord
+ */
+public final class AdvertisementData implements Parcelable {
+
+ @Nullable
+ private final List<ParcelUuid> mServiceUuids;
+
+ private final int mManufacturerId;
+ @Nullable
+ private final byte[] mManufacturerSpecificData;
+
+ @Nullable
+ private final ParcelUuid mServiceDataUuid;
+ @Nullable
+ private final byte[] mServiceData;
+
+ private boolean mIncludeTxPowerLevel;
+
+ private AdvertisementData(List<ParcelUuid> serviceUuids,
+ ParcelUuid serviceDataUuid, byte[] serviceData,
+ int manufacturerId,
+ byte[] manufacturerSpecificData, boolean includeTxPowerLevel) {
+ mServiceUuids = serviceUuids;
+ mManufacturerId = manufacturerId;
+ mManufacturerSpecificData = manufacturerSpecificData;
+ mServiceDataUuid = serviceDataUuid;
+ mServiceData = serviceData;
+ mIncludeTxPowerLevel = includeTxPowerLevel;
+ }
+
+ /**
+ * Returns a list of service uuids within the advertisement that are used to identify the
+ * bluetooth GATT services.
+ */
+ public List<ParcelUuid> getServiceUuids() {
+ return mServiceUuids;
+ }
+
+ /**
+ * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth
+ * SIG.
+ */
+ public int getManufacturerId() {
+ return mManufacturerId;
+ }
+
+ /**
+ * Returns the manufacturer specific data which is the content of manufacturer specific data
+ * field. The first 2 bytes of the data contain the company id.
+ */
+ public byte[] getManufacturerSpecificData() {
+ return mManufacturerSpecificData;
+ }
+
+ /**
+ * Returns a 16 bit uuid of the service that the service data is associated with.
+ */
+ public ParcelUuid getServiceDataUuid() {
+ return mServiceDataUuid;
+ }
+
+ /**
+ * Returns service data. The first two bytes should be a 16 bit service uuid associated with the
+ * service data.
+ */
+ public byte[] getServiceData() {
+ return mServiceData;
+ }
+
+ /**
+ * Whether the transmission power level will be included in the advertisement packet.
+ */
+ public boolean getIncludeTxPowerLevel() {
+ return mIncludeTxPowerLevel;
+ }
+
+ @Override
+ public String toString() {
+ return "AdvertisementData [mServiceUuids=" + mServiceUuids + ", mManufacturerId="
+ + mManufacturerId + ", mManufacturerSpecificData="
+ + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid="
+ + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData)
+ + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + "]";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mServiceUuids == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(mServiceUuids.size());
+ dest.writeList(mServiceUuids);
+ }
+
+ dest.writeInt(mManufacturerId);
+ if (mManufacturerSpecificData == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(mManufacturerSpecificData.length);
+ dest.writeByteArray(mManufacturerSpecificData);
+ }
+
+ if (mServiceDataUuid == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ dest.writeParcelable(mServiceDataUuid, flags);
+ if (mServiceData == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(mServiceData.length);
+ dest.writeByteArray(mServiceData);
+ }
+ }
+ dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0));
+ }
+
+ public static final Parcelable.Creator<AdvertisementData> CREATOR =
+ new Creator<AdvertisementData>() {
+ @Override
+ public AdvertisementData[] newArray(int size) {
+ return new AdvertisementData[size];
+ }
+
+ @Override
+ public AdvertisementData createFromParcel(Parcel in) {
+ Builder builder = new Builder();
+ if (in.readInt() > 0) {
+ List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
+ in.readList(uuids, ParcelUuid.class.getClassLoader());
+ builder.setServiceUuids(uuids);
+ }
+ int manufacturerId = in.readInt();
+ int manufacturerDataLength = in.readInt();
+ if (manufacturerDataLength > 0) {
+ byte[] manufacturerData = new byte[manufacturerDataLength];
+ in.readByteArray(manufacturerData);
+ builder.setManufacturerData(manufacturerId, manufacturerData);
+ }
+ if (in.readInt() == 1) {
+ ParcelUuid serviceDataUuid = in.readParcelable(
+ ParcelUuid.class.getClassLoader());
+ int serviceDataLength = in.readInt();
+ if (serviceDataLength > 0) {
+ byte[] serviceData = new byte[serviceDataLength];
+ in.readByteArray(serviceData);
+ builder.setServiceData(serviceDataUuid, serviceData);
+ }
+ }
+ builder.setIncludeTxPowerLevel(in.readByte() == 1);
+ return builder.build();
+ }
+ };
+
+ /**
+ * Builder for {@link AdvertisementData}.
+ */
+ public static final class Builder {
+ private static final int MAX_ADVERTISING_DATA_BYTES = 31;
+ // Each fields need one byte for field length and another byte for field type.
+ private static final int OVERHEAD_BYTES_PER_FIELD = 2;
+ // Flags field will be set by system.
+ private static final int FLAGS_FIELD_BYTES = 3;
+
+ @Nullable
+ private List<ParcelUuid> mServiceUuids;
+ private boolean mIncludeTxPowerLevel;
+ private int mManufacturerId;
+ @Nullable
+ private byte[] mManufacturerSpecificData;
+ @Nullable
+ private ParcelUuid mServiceDataUuid;
+ @Nullable
+ private byte[] mServiceData;
+
+ /**
+ * Set the service uuids. Note the corresponding bluetooth Gatt services need to be already
+ * added on the device before start BLE advertising.
+ *
+ * @param serviceUuids Service uuids to be advertised, could be 16-bit, 32-bit or 128-bit
+ * uuids.
+ * @throws IllegalArgumentException If the {@code serviceUuids} are null.
+ */
+ public Builder setServiceUuids(List<ParcelUuid> serviceUuids) {
+ if (serviceUuids == null) {
+ throw new IllegalArgumentException("serivceUuids are null");
+ }
+ mServiceUuids = serviceUuids;
+ return this;
+ }
+
+ /**
+ * Add service data to advertisement.
+ *
+ * @param serviceDataUuid A 16 bit uuid of the service data
+ * @param serviceData Service data - the first two bytes of the service data are the service
+ * data uuid.
+ * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is
+ * empty.
+ */
+ public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
+ if (serviceDataUuid == null || serviceData == null) {
+ throw new IllegalArgumentException(
+ "serviceDataUuid or serviceDataUuid is null");
+ }
+ mServiceDataUuid = serviceDataUuid;
+ mServiceData = serviceData;
+ return this;
+ }
+
+ /**
+ * Set manufacturer id and data. See <a
+ * href="https://www.bluetooth.org/en-us/specification/assigned-numbers/company-identifiers">assigned
+ * manufacturer identifies</a> for the existing company identifiers.
+ *
+ * @param manufacturerId Manufacturer id assigned by Bluetooth SIG.
+ * @param manufacturerSpecificData Manufacturer specific data - the first two bytes of the
+ * manufacturer specific data are the manufacturer id.
+ * @throws IllegalArgumentException If the {@code manufacturerId} is negative or
+ * {@code manufacturerSpecificData} is null.
+ */
+ public Builder setManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) {
+ if (manufacturerId < 0) {
+ throw new IllegalArgumentException(
+ "invalid manufacturerId - " + manufacturerId);
+ }
+ if (manufacturerSpecificData == null) {
+ throw new IllegalArgumentException("manufacturerSpecificData is null");
+ }
+ mManufacturerId = manufacturerId;
+ mManufacturerSpecificData = manufacturerSpecificData;
+ return this;
+ }
+
+ /**
+ * Whether the transmission power level should be included in the advertising packet.
+ */
+ public Builder setIncludeTxPowerLevel(boolean includeTxPowerLevel) {
+ mIncludeTxPowerLevel = includeTxPowerLevel;
+ return this;
+ }
+
+ /**
+ * Build the {@link AdvertisementData}.
+ *
+ * @throws IllegalArgumentException If the data size is larger than 31 bytes.
+ */
+ public AdvertisementData build() {
+ if (totalBytes() > MAX_ADVERTISING_DATA_BYTES) {
+ throw new IllegalArgumentException(
+ "advertisement data size is larger than 31 bytes");
+ }
+ return new AdvertisementData(mServiceUuids,
+ mServiceDataUuid,
+ mServiceData, mManufacturerId, mManufacturerSpecificData,
+ mIncludeTxPowerLevel);
+ }
+
+ // Compute the size of the advertisement data.
+ private int totalBytes() {
+ int size = FLAGS_FIELD_BYTES; // flags field is always set.
+ if (mServiceUuids != null) {
+ int num16BitUuids = 0;
+ int num32BitUuids = 0;
+ int num128BitUuids = 0;
+ for (ParcelUuid uuid : mServiceUuids) {
+ if (BluetoothUuid.is16BitUuid(uuid)) {
+ ++num16BitUuids;
+ } else if (BluetoothUuid.is32BitUuid(uuid)) {
+ ++num32BitUuids;
+ } else {
+ ++num128BitUuids;
+ }
+ }
+ // 16 bit service uuids are grouped into one field when doing advertising.
+ if (num16BitUuids != 0) {
+ size += OVERHEAD_BYTES_PER_FIELD +
+ num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
+ }
+ // 32 bit service uuids are grouped into one field when doing advertising.
+ if (num32BitUuids != 0) {
+ size += OVERHEAD_BYTES_PER_FIELD +
+ num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
+ }
+ // 128 bit service uuids are grouped into one field when doing advertising.
+ if (num128BitUuids != 0) {
+ size += OVERHEAD_BYTES_PER_FIELD +
+ num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
+ }
+ }
+ if (mServiceData != null) {
+ size += OVERHEAD_BYTES_PER_FIELD + mServiceData.length;
+ }
+ if (mManufacturerSpecificData != null) {
+ size += OVERHEAD_BYTES_PER_FIELD + mManufacturerSpecificData.length;
+ }
+ if (mIncludeTxPowerLevel) {
+ size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
+ }
+ return size;
+ }
+ }
+}
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
new file mode 100644
index 0000000..ed43407
--- /dev/null
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -0,0 +1,368 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth.le;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothGattCallback;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * This class provides a way to perform Bluetooth LE advertise operations, such as start and stop
+ * advertising. An advertiser can broadcast up to 31 bytes of advertisement data represented by
+ * {@link AdvertisementData}.
+ * <p>
+ * To get an instance of {@link BluetoothLeAdvertiser}, call the
+ * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method.
+ * <p>
+ * Note most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @see AdvertisementData
+ */
+public final class BluetoothLeAdvertiser {
+
+ private static final String TAG = "BluetoothLeAdvertiser";
+
+ private final IBluetoothGatt mBluetoothGatt;
+ private final Handler mHandler;
+ private final Map<AdvertiseCallback, AdvertiseCallbackWrapper>
+ mLeAdvertisers = new HashMap<AdvertiseCallback, AdvertiseCallbackWrapper>();
+
+ /**
+ * Use BluetoothAdapter.getLeAdvertiser() instead.
+ *
+ * @param bluetoothGatt
+ * @hide
+ */
+ public BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) {
+ mBluetoothGatt = bluetoothGatt;
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ /**
+ * Start Bluetooth LE Advertising. The {@code advertiseData} would be broadcasted after the
+ * operation succeeds. Returns immediately, the operation status are delivered through
+ * {@code callback}.
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ *
+ * @param settings Settings for Bluetooth LE advertising.
+ * @param advertiseData Advertisement data to be broadcasted.
+ * @param callback Callback for advertising status.
+ */
+ public void startAdvertising(AdvertiseSettings settings,
+ AdvertisementData advertiseData, final AdvertiseCallback callback) {
+ startAdvertising(settings, advertiseData, null, callback);
+ }
+
+ /**
+ * Start Bluetooth LE Advertising. The {@code advertiseData} would be broadcasted after the
+ * operation succeeds. The {@code scanResponse} would be returned when the scanning device sends
+ * active scan request. Method returns immediately, the operation status are delivered through
+ * {@code callback}.
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ * @param settings Settings for Bluetooth LE advertising.
+ * @param advertiseData Advertisement data to be advertised in advertisement packet.
+ * @param scanResponse Scan response associated with the advertisement data.
+ * @param callback Callback for advertising status.
+ */
+ public void startAdvertising(AdvertiseSettings settings,
+ AdvertisementData advertiseData, AdvertisementData scanResponse,
+ final AdvertiseCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ if (mLeAdvertisers.containsKey(callback)) {
+ postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
+ return;
+ }
+ AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData,
+ scanResponse, settings, mBluetoothGatt);
+ UUID uuid = UUID.randomUUID();
+ try {
+ mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
+ if (wrapper.advertiseStarted()) {
+ mLeAdvertisers.put(callback, wrapper);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to stop advertising", e);
+ }
+ }
+
+ /**
+ * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in
+ * {@link BluetoothLeAdvertiser#startAdvertising}.
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ *
+ * @param callback {@link AdvertiseCallback} for delivering stopping advertising status.
+ */
+ public void stopAdvertising(final AdvertiseCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback);
+ if (wrapper == null) {
+ postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_NOT_STARTED);
+ return;
+ }
+ try {
+ mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle);
+ if (wrapper.advertiseStopped()) {
+ mLeAdvertisers.remove(callback);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to stop advertising", e);
+ }
+ }
+
+ /**
+ * Bluetooth GATT interface callbacks for advertising.
+ */
+ private static class AdvertiseCallbackWrapper extends IBluetoothGattCallback.Stub {
+ private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000;
+ private final AdvertiseCallback mAdvertiseCallback;
+ private final AdvertisementData mAdvertisement;
+ private final AdvertisementData mScanResponse;
+ private final AdvertiseSettings mSettings;
+ private final IBluetoothGatt mBluetoothGatt;
+
+ // mLeHandle 0: not registered
+ // -1: scan stopped
+ // >0: registered and scan started
+ private int mLeHandle;
+ private boolean isAdvertising = false;
+
+ public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,
+ AdvertisementData advertiseData, AdvertisementData scanResponse,
+ AdvertiseSettings settings,
+ IBluetoothGatt bluetoothGatt) {
+ mAdvertiseCallback = advertiseCallback;
+ mAdvertisement = advertiseData;
+ mScanResponse = scanResponse;
+ mSettings = settings;
+ mBluetoothGatt = bluetoothGatt;
+ mLeHandle = 0;
+ }
+
+ public boolean advertiseStarted() {
+ boolean started = false;
+ synchronized (this) {
+ if (mLeHandle == -1) {
+ return false;
+ }
+ try {
+ wait(LE_CALLBACK_TIMEOUT_MILLIS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Callback reg wait interrupted: ", e);
+ }
+ started = (mLeHandle > 0 && isAdvertising);
+ }
+ return started;
+ }
+
+ public boolean advertiseStopped() {
+ synchronized (this) {
+ try {
+ wait(LE_CALLBACK_TIMEOUT_MILLIS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Callback reg wait interrupted: " + e);
+ }
+ return !isAdvertising;
+ }
+ }
+
+ /**
+ * Application interface registered - app is ready to go
+ */
+ @Override
+ public void onClientRegistered(int status, int clientIf) {
+ Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf);
+ synchronized (this) {
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ mLeHandle = clientIf;
+ try {
+ mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement,
+ mScanResponse, mSettings);
+ } catch (RemoteException e) {
+ Log.e(TAG, "fail to start le advertise: " + e);
+ mLeHandle = -1;
+ notifyAll();
+ } catch (Exception e) {
+ Log.e(TAG, "fail to start advertise: " + e.getStackTrace());
+ }
+ } else {
+ // registration failed
+ mLeHandle = -1;
+ notifyAll();
+ }
+ }
+ }
+
+ @Override
+ public void onClientConnectionState(int status, int clientIf,
+ boolean connected, String address) {
+ // no op
+ }
+
+ @Override
+ public void onScanResult(String address, int rssi, byte[] advData) {
+ // no op
+ }
+
+ @Override
+ public void onGetService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid) {
+ // no op
+ }
+
+ @Override
+ public void onGetIncludedService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int inclSrvcType, int inclSrvcInstId,
+ ParcelUuid inclSrvcUuid) {
+ // no op
+ }
+
+ @Override
+ public void onGetCharacteristic(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int charProps) {
+ // no op
+ }
+
+ @Override
+ public void onGetDescriptor(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int descInstId, ParcelUuid descUuid) {
+ // no op
+ }
+
+ @Override
+ public void onSearchComplete(String address, int status) {
+ // no op
+ }
+
+ @Override
+ public void onCharacteristicRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid, byte[] value) {
+ // no op
+ }
+
+ @Override
+ public void onCharacteristicWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid) {
+ // no op
+ }
+
+ @Override
+ public void onNotify(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ byte[] value) {
+ // no op
+ }
+
+ @Override
+ public void onDescriptorRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int descInstId, ParcelUuid descrUuid, byte[] value) {
+ // no op
+ }
+
+ @Override
+ public void onDescriptorWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int descInstId, ParcelUuid descrUuid) {
+ // no op
+ }
+
+ @Override
+ public void onExecuteWrite(String address, int status) {
+ // no op
+ }
+
+ @Override
+ public void onReadRemoteRssi(String address, int rssi, int status) {
+ // no op
+ }
+
+ @Override
+ public void onAdvertiseStateChange(int advertiseState, int status) {
+ // no op
+ }
+
+ @Override
+ public void onMultiAdvertiseCallback(int status) {
+ synchronized (this) {
+ if (status == 0) {
+ isAdvertising = !isAdvertising;
+ if (!isAdvertising) {
+ try {
+ mBluetoothGatt.unregisterClient(mLeHandle);
+ mLeHandle = -1;
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception when unregistering", e);
+ }
+ }
+ mAdvertiseCallback.onSuccess(null);
+ } else {
+ mAdvertiseCallback.onFailure(status);
+ }
+ notifyAll();
+ }
+
+ }
+
+ /**
+ * Callback reporting LE ATT MTU.
+ *
+ * @hide
+ */
+ @Override
+ public void onConfigureMTU(String address, int mtu, int status) {
+ // no op
+ }
+ }
+
+ private void postCallbackFailure(final AdvertiseCallback callback, final int error) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onFailure(error);
+ }
+ });
+ }
+}
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
new file mode 100644
index 0000000..4c6346c
--- /dev/null
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -0,0 +1,371 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth.le;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothGattCallback;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * This class provides methods to perform scan related operations for Bluetooth LE devices. An
+ * application can scan for a particular type of BLE devices using {@link ScanFilter}. It can also
+ * request different types of callbacks for delivering the result.
+ * <p>
+ * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of
+ * {@link BluetoothLeScanner}.
+ * <p>
+ * Note most of the scan methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @see ScanFilter
+ */
+public final class BluetoothLeScanner {
+
+ private static final String TAG = "BluetoothLeScanner";
+ private static final boolean DBG = true;
+
+ private final IBluetoothGatt mBluetoothGatt;
+ private final Handler mHandler;
+ private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
+
+ /**
+ * @hide
+ */
+ public BluetoothLeScanner(IBluetoothGatt bluetoothGatt) {
+ mBluetoothGatt = bluetoothGatt;
+ mHandler = new Handler(Looper.getMainLooper());
+ mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>();
+ }
+
+ /**
+ * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ *
+ * @param filters {@link ScanFilter}s for finding exact BLE devices.
+ * @param settings Settings for ble scan.
+ * @param callback Callback when scan results are delivered.
+ * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
+ */
+ public void startScan(List<ScanFilter> filters, ScanSettings settings,
+ final ScanCallback callback) {
+ if (settings == null || callback == null) {
+ throw new IllegalArgumentException("settings or callback is null");
+ }
+ synchronized (mLeScanClients) {
+ if (mLeScanClients.containsKey(callback)) {
+ postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED);
+ return;
+ }
+ BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters,
+ settings, callback);
+ try {
+ UUID uuid = UUID.randomUUID();
+ mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
+ if (wrapper.scanStarted()) {
+ mLeScanClients.put(callback, wrapper);
+ } else {
+ postCallbackError(callback,
+ ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "GATT service exception when starting scan", e);
+ postCallbackError(callback, ScanCallback.SCAN_FAILED_GATT_SERVICE_FAILURE);
+ }
+ }
+ }
+
+ /**
+ * Stops an ongoing Bluetooth LE scan.
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ *
+ * @param callback
+ */
+ public void stopScan(ScanCallback callback) {
+ synchronized (mLeScanClients) {
+ BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback);
+ if (wrapper == null) {
+ return;
+ }
+ wrapper.stopLeScan();
+ }
+ }
+
+ /**
+ * Returns available storage size for batch scan results. It's recommended not to use batch scan
+ * if available storage size is small (less than 1k bytes, for instance).
+ *
+ * @hide TODO: unhide when batching is supported in stack.
+ */
+ public int getAvailableBatchStorageSizeBytes() {
+ throw new UnsupportedOperationException("not impelemented");
+ }
+
+ /**
+ * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results
+ * batched on bluetooth controller.
+ *
+ * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
+ * used to start scan.
+ * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will
+ * get batch scan callback if the batch scan buffer is flushed.
+ * @return Batch Scan results.
+ * @hide TODO: unhide when batching is supported in stack.
+ */
+ public List<ScanResult> getBatchScanResults(ScanCallback callback, boolean flush) {
+ throw new UnsupportedOperationException("not impelemented");
+ }
+
+ /**
+ * Bluetooth GATT interface callbacks
+ */
+ private static class BleScanCallbackWrapper extends IBluetoothGattCallback.Stub {
+ private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5;
+
+ private final ScanCallback mScanCallback;
+ private final List<ScanFilter> mFilters;
+ private ScanSettings mSettings;
+ private IBluetoothGatt mBluetoothGatt;
+
+ // mLeHandle 0: not registered
+ // -1: scan stopped
+ // > 0: registered and scan started
+ private int mLeHandle;
+
+ public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
+ List<ScanFilter> filters, ScanSettings settings,
+ ScanCallback scanCallback) {
+ mBluetoothGatt = bluetoothGatt;
+ mFilters = filters;
+ mSettings = settings;
+ mScanCallback = scanCallback;
+ mLeHandle = 0;
+ }
+
+ public boolean scanStarted() {
+ synchronized (this) {
+ if (mLeHandle == -1) {
+ return false;
+ }
+ try {
+ wait(REGISTRATION_CALLBACK_TIMEOUT_SECONDS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Callback reg wait interrupted: " + e);
+ }
+ }
+ return mLeHandle > 0;
+ }
+
+ public void stopLeScan() {
+ synchronized (this) {
+ if (mLeHandle <= 0) {
+ Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
+ return;
+ }
+ try {
+ mBluetoothGatt.stopScan(mLeHandle, false);
+ mBluetoothGatt.unregisterClient(mLeHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to stop scan and unregister" + e);
+ }
+ mLeHandle = -1;
+ notifyAll();
+ }
+ }
+
+ /**
+ * Application interface registered - app is ready to go
+ */
+ @Override
+ public void onClientRegistered(int status, int clientIf) {
+ Log.d(TAG, "onClientRegistered() - status=" + status +
+ " clientIf=" + clientIf);
+
+ synchronized (this) {
+ if (mLeHandle == -1) {
+ if (DBG)
+ Log.d(TAG, "onClientRegistered LE scan canceled");
+ }
+
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ mLeHandle = clientIf;
+ try {
+ mBluetoothGatt.startScanWithFilters(mLeHandle, false, mSettings, mFilters);
+ } catch (RemoteException e) {
+ Log.e(TAG, "fail to start le scan: " + e);
+ mLeHandle = -1;
+ }
+ } else {
+ // registration failed
+ mLeHandle = -1;
+ }
+ notifyAll();
+ }
+ }
+
+ @Override
+ public void onClientConnectionState(int status, int clientIf,
+ boolean connected, String address) {
+ // no op
+ }
+
+ /**
+ * Callback reporting an LE scan result.
+ *
+ * @hide
+ */
+ @Override
+ public void onScanResult(String address, int rssi, byte[] advData) {
+ if (DBG)
+ Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" + rssi);
+
+ // Check null in case the scan has been stopped
+ synchronized (this) {
+ if (mLeHandle <= 0)
+ return;
+ }
+ BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
+ address);
+ long scanNanos = SystemClock.elapsedRealtimeNanos();
+ ScanResult result = new ScanResult(device, advData, rssi,
+ scanNanos);
+ mScanCallback.onAdvertisementUpdate(result);
+ }
+
+ @Override
+ public void onGetService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid) {
+ // no op
+ }
+
+ @Override
+ public void onGetIncludedService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int inclSrvcType, int inclSrvcInstId,
+ ParcelUuid inclSrvcUuid) {
+ // no op
+ }
+
+ @Override
+ public void onGetCharacteristic(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int charProps) {
+ // no op
+ }
+
+ @Override
+ public void onGetDescriptor(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int descInstId, ParcelUuid descUuid) {
+ // no op
+ }
+
+ @Override
+ public void onSearchComplete(String address, int status) {
+ // no op
+ }
+
+ @Override
+ public void onCharacteristicRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid, byte[] value) {
+ // no op
+ }
+
+ @Override
+ public void onCharacteristicWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid) {
+ // no op
+ }
+
+ @Override
+ public void onNotify(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ byte[] value) {
+ // no op
+ }
+
+ @Override
+ public void onDescriptorRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int descInstId, ParcelUuid descrUuid, byte[] value) {
+ // no op
+ }
+
+ @Override
+ public void onDescriptorWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int descInstId, ParcelUuid descrUuid) {
+ // no op
+ }
+
+ @Override
+ public void onExecuteWrite(String address, int status) {
+ // no op
+ }
+
+ @Override
+ public void onReadRemoteRssi(String address, int rssi, int status) {
+ // no op
+ }
+
+ @Override
+ public void onAdvertiseStateChange(int advertiseState, int status) {
+ // no op
+ }
+
+ @Override
+ public void onMultiAdvertiseCallback(int status) {
+ // no op
+ }
+
+ @Override
+ public void onConfigureMTU(String address, int mtu, int status) {
+ // no op
+ }
+ }
+
+ private void postCallbackError(final ScanCallback callback, final int errorCode) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onScanFailed(errorCode);
+ }
+ });
+ }
+}
diff --git a/core/java/android/bluetooth/le/ScanCallback.java b/core/java/android/bluetooth/le/ScanCallback.java
new file mode 100644
index 0000000..50ebf50
--- /dev/null
+++ b/core/java/android/bluetooth/le/ScanCallback.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth.le;
+
+import java.util.List;
+
+/**
+ * Callback of Bluetooth LE scans. The results of the scans will be delivered through the callbacks.
+ */
+public abstract class ScanCallback {
+
+ /**
+ * Fails to start scan as BLE scan with the same settings is already started by the app.
+ */
+ public static final int SCAN_FAILED_ALREADY_STARTED = 1;
+ /**
+ * Fails to start scan as app cannot be registered.
+ */
+ public static final int SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2;
+ /**
+ * Fails to start scan due to gatt service failure.
+ */
+ public static final int SCAN_FAILED_GATT_SERVICE_FAILURE = 3;
+ /**
+ * Fails to start scan due to controller failure.
+ */
+ public static final int SCAN_FAILED_CONTROLLER_FAILURE = 4;
+
+ /**
+ * Callback when a BLE advertisement is found.
+ *
+ * @param result A Bluetooth LE scan result.
+ */
+ public abstract void onAdvertisementUpdate(ScanResult result);
+
+ /**
+ * Callback when the BLE advertisement is found for the first time.
+ *
+ * @param result The Bluetooth LE scan result when the onFound event is triggered.
+ * @hide
+ */
+ public abstract void onAdvertisementFound(ScanResult result);
+
+ /**
+ * Callback when the BLE advertisement was lost. Note a device has to be "found" before it's
+ * lost.
+ *
+ * @param result The Bluetooth scan result that was last found.
+ * @hide
+ */
+ public abstract void onAdvertisementLost(ScanResult result);
+
+ /**
+ * Callback when batch results are delivered.
+ *
+ * @param results List of scan results that are previously scanned.
+ * @hide
+ */
+ public abstract void onBatchScanResults(List<ScanResult> results);
+
+ /**
+ * Callback when scan failed.
+ */
+ public abstract void onScanFailed(int errorCode);
+}
diff --git a/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl b/core/java/android/bluetooth/le/ScanFilter.aidl
index 4aa8881..4cecfe6 100644
--- a/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl
+++ b/core/java/android/bluetooth/le/ScanFilter.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.bluetooth;
+package android.bluetooth.le;
-parcelable BluetoothLeAdvertiseScanData.AdvertisementData; \ No newline at end of file
+parcelable ScanFilter;
diff --git a/core/java/android/bluetooth/BluetoothLeScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 2ed85ba..c2e316b 100644
--- a/core/java/android/bluetooth/BluetoothLeScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package android.bluetooth;
+package android.bluetooth.le;
import android.annotation.Nullable;
-import android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord;
-import android.bluetooth.BluetoothLeScanner.ScanResult;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.Parcelable;
@@ -29,8 +29,7 @@ import java.util.Objects;
import java.util.UUID;
/**
- * {@link BluetoothLeScanFilter} abstracts different scan filters across Bluetooth Advertisement
- * packet fields.
+ * {@link ScanFilter} abstracts different scan filters across Bluetooth Advertisement packet fields.
* <p>
* Current filtering on the following fields are supported:
* <li>Service UUIDs which identify the bluetooth gatt services running on the device.
@@ -40,10 +39,10 @@ import java.util.UUID;
* <li>Service data which is the data associated with a service.
* <li>Manufacturer specific data which is the data associated with a particular manufacturer.
*
- * @see BluetoothLeAdvertiseScanData.ScanRecord
+ * @see ScanRecord
* @see BluetoothLeScanner
*/
-public final class BluetoothLeScanFilter implements Parcelable {
+public final class ScanFilter implements Parcelable {
@Nullable
private final String mLocalName;
@@ -70,7 +69,7 @@ public final class BluetoothLeScanFilter implements Parcelable {
private final int mMinRssi;
private final int mMaxRssi;
- private BluetoothLeScanFilter(String name, String macAddress, ParcelUuid uuid,
+ private ScanFilter(String name, String macAddress, ParcelUuid uuid,
ParcelUuid uuidMask, byte[] serviceData, byte[] serviceDataMask,
int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask,
int minRssi, int maxRssi) {
@@ -105,88 +104,93 @@ public final class BluetoothLeScanFilter implements Parcelable {
dest.writeInt(mServiceUuid == null ? 0 : 1);
if (mServiceUuid != null) {
dest.writeParcelable(mServiceUuid, flags);
- }
- dest.writeInt(mServiceUuidMask == null ? 0 : 1);
- if (mServiceUuidMask != null) {
- dest.writeParcelable(mServiceUuidMask, flags);
+ dest.writeInt(mServiceUuidMask == null ? 0 : 1);
+ if (mServiceUuidMask != null) {
+ dest.writeParcelable(mServiceUuidMask, flags);
+ }
}
dest.writeInt(mServiceData == null ? 0 : mServiceData.length);
if (mServiceData != null) {
dest.writeByteArray(mServiceData);
- }
- dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length);
- if (mServiceDataMask != null) {
- dest.writeByteArray(mServiceDataMask);
+ dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length);
+ if (mServiceDataMask != null) {
+ dest.writeByteArray(mServiceDataMask);
+ }
}
dest.writeInt(mManufacturerId);
dest.writeInt(mManufacturerData == null ? 0 : mManufacturerData.length);
if (mManufacturerData != null) {
dest.writeByteArray(mManufacturerData);
- }
- dest.writeInt(mManufacturerDataMask == null ? 0 : mManufacturerDataMask.length);
- if (mManufacturerDataMask != null) {
- dest.writeByteArray(mManufacturerDataMask);
+ dest.writeInt(mManufacturerDataMask == null ? 0 : mManufacturerDataMask.length);
+ if (mManufacturerDataMask != null) {
+ dest.writeByteArray(mManufacturerDataMask);
+ }
}
dest.writeInt(mMinRssi);
dest.writeInt(mMaxRssi);
}
/**
- * A {@link android.os.Parcelable.Creator} to create {@link BluetoothLeScanFilter} form parcel.
+ * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} form parcel.
*/
- public static final Creator<BluetoothLeScanFilter>
- CREATOR = new Creator<BluetoothLeScanFilter>() {
+ public static final Creator<ScanFilter>
+ CREATOR = new Creator<ScanFilter>() {
@Override
- public BluetoothLeScanFilter[] newArray(int size) {
- return new BluetoothLeScanFilter[size];
+ public ScanFilter[] newArray(int size) {
+ return new ScanFilter[size];
}
@Override
- public BluetoothLeScanFilter createFromParcel(Parcel in) {
- Builder builder = newBuilder();
+ public ScanFilter createFromParcel(Parcel in) {
+ Builder builder = new Builder();
if (in.readInt() == 1) {
- builder.name(in.readString());
+ builder.setName(in.readString());
}
if (in.readInt() == 1) {
- builder.macAddress(in.readString());
+ builder.setMacAddress(in.readString());
}
if (in.readInt() == 1) {
ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader());
- builder.serviceUuid(uuid);
- }
- if (in.readInt() == 1) {
- ParcelUuid uuidMask = in.readParcelable(ParcelUuid.class.getClassLoader());
- builder.serviceUuidMask(uuidMask);
+ builder.setServiceUuid(uuid);
+ if (in.readInt() == 1) {
+ ParcelUuid uuidMask = in.readParcelable(
+ ParcelUuid.class.getClassLoader());
+ builder.setServiceUuid(uuid, uuidMask);
+ }
}
+
int serviceDataLength = in.readInt();
if (serviceDataLength > 0) {
byte[] serviceData = new byte[serviceDataLength];
in.readByteArray(serviceData);
- builder.serviceData(serviceData);
- }
- int serviceDataMaskLength = in.readInt();
- if (serviceDataMaskLength > 0) {
- byte[] serviceDataMask = new byte[serviceDataMaskLength];
- in.readByteArray(serviceDataMask);
- builder.serviceDataMask(serviceDataMask);
+ builder.setServiceData(serviceData);
+ int serviceDataMaskLength = in.readInt();
+ if (serviceDataMaskLength > 0) {
+ byte[] serviceDataMask = new byte[serviceDataMaskLength];
+ in.readByteArray(serviceDataMask);
+ builder.setServiceData(serviceData, serviceDataMask);
+ }
}
+
int manufacturerId = in.readInt();
int manufacturerDataLength = in.readInt();
if (manufacturerDataLength > 0) {
byte[] manufacturerData = new byte[manufacturerDataLength];
in.readByteArray(manufacturerData);
- builder.manufacturerData(manufacturerId, manufacturerData);
- }
- int manufacturerDataMaskLength = in.readInt();
- if (manufacturerDataMaskLength > 0) {
- byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength];
- in.readByteArray(manufacturerDataMask);
- builder.manufacturerDataMask(manufacturerDataMask);
+ builder.setManufacturerData(manufacturerId, manufacturerData);
+ int manufacturerDataMaskLength = in.readInt();
+ if (manufacturerDataMaskLength > 0) {
+ byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength];
+ in.readByteArray(manufacturerDataMask);
+ builder.setManufacturerData(manufacturerId, manufacturerData,
+ manufacturerDataMask);
+ }
}
+
int minRssi = in.readInt();
int maxRssi = in.readInt();
- builder.rssiRange(minRssi, maxRssi);
+ builder.setRssiRange(minRssi, maxRssi);
return builder.build();
}
};
@@ -199,9 +203,10 @@ public final class BluetoothLeScanFilter implements Parcelable {
return mLocalName;
}
- @Nullable /**
- * Returns the filter set on the service uuid.
- */
+ /**
+ * Returns the filter set on the service uuid.
+ */
+ @Nullable
public ParcelUuid getServiceUuid() {
return mServiceUuid;
}
@@ -277,7 +282,7 @@ public final class BluetoothLeScanFilter implements Parcelable {
}
byte[] scanRecordBytes = scanResult.getScanRecord();
- ScanRecord scanRecord = ScanRecord.getParser().parseFromScanRecord(scanRecordBytes);
+ ScanRecord scanRecord = ScanRecord.parseFromBytes(scanRecordBytes);
// Scan record is null but there exist filters on it.
if (scanRecord == null
@@ -386,13 +391,13 @@ public final class BluetoothLeScanFilter implements Parcelable {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
- BluetoothLeScanFilter other = (BluetoothLeScanFilter) obj;
+ ScanFilter other = (ScanFilter) obj;
return Objects.equals(mLocalName, other.mLocalName) &&
Objects.equals(mMacAddress, other.mMacAddress) &&
- mManufacturerId == other.mManufacturerId &&
+ mManufacturerId == other.mManufacturerId &&
Objects.deepEquals(mManufacturerData, other.mManufacturerData) &&
Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) &&
- mMinRssi == other.mMinRssi && mMaxRssi == other.mMaxRssi &&
+ mMinRssi == other.mMinRssi && mMaxRssi == other.mMaxRssi &&
Objects.deepEquals(mServiceData, other.mServiceData) &&
Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) &&
Objects.equals(mServiceUuid, other.mServiceUuid) &&
@@ -400,17 +405,9 @@ public final class BluetoothLeScanFilter implements Parcelable {
}
/**
- * Returns the {@link Builder} for {@link BluetoothLeScanFilter}.
+ * Builder class for {@link ScanFilter}.
*/
- public static Builder newBuilder() {
- return new Builder();
- }
-
- /**
- * Builder class for {@link BluetoothLeScanFilter}. Use
- * {@link BluetoothLeScanFilter#newBuilder()} to get an instance of the {@link Builder}.
- */
- public static class Builder {
+ public static final class Builder {
private String mLocalName;
private String mMacAddress;
@@ -428,27 +425,23 @@ public final class BluetoothLeScanFilter implements Parcelable {
private int mMinRssi = Integer.MIN_VALUE;
private int mMaxRssi = Integer.MAX_VALUE;
- // Private constructor, use BluetoothLeScanFilter.newBuilder instead.
- private Builder() {
- }
-
/**
- * Set filtering on local name.
+ * Set filter on local name.
*/
- public Builder name(String localName) {
+ public Builder setName(String localName) {
mLocalName = localName;
return this;
}
/**
- * Set filtering on device mac address.
+ * Set filter on device mac address.
*
* @param macAddress The device mac address for the filter. It needs to be in the format of
* "01:02:03:AB:CD:EF". The mac address can be validated using
* {@link BluetoothAdapter#checkBluetoothAddress}.
* @throws IllegalArgumentException If the {@code macAddress} is invalid.
*/
- public Builder macAddress(String macAddress) {
+ public Builder setMacAddress(String macAddress) {
if (macAddress != null && !BluetoothAdapter.checkBluetoothAddress(macAddress)) {
throw new IllegalArgumentException("invalid mac address " + macAddress);
}
@@ -457,68 +450,115 @@ public final class BluetoothLeScanFilter implements Parcelable {
}
/**
- * Set filtering on service uuid.
+ * Set filter on service uuid.
*/
- public Builder serviceUuid(ParcelUuid serviceUuid) {
+ public Builder setServiceUuid(ParcelUuid serviceUuid) {
mServiceUuid = serviceUuid;
+ mUuidMask = null; // clear uuid mask
return this;
}
/**
- * Set partial uuid filter. The {@code uuidMask} is the bit mask for the {@code uuid} set
- * through {@link #serviceUuid(ParcelUuid)} method. Set any bit in the mask to 1 to indicate
- * a match is needed for the bit in {@code serviceUuid}, and 0 to ignore that bit.
- * <p>
- * The length of {@code uuidMask} must be the same as {@code serviceUuid}.
+ * Set filter on partial service uuid. The {@code uuidMask} is the bit mask for the
+ * {@code serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the
+ * bit in {@code serviceUuid}, and 0 to ignore that bit.
+ *
+ * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but
+ * {@code uuidMask} is not {@code null}.
*/
- public Builder serviceUuidMask(ParcelUuid uuidMask) {
+ public Builder setServiceUuid(ParcelUuid serviceUuid, ParcelUuid uuidMask) {
+ if (mUuidMask != null && mServiceUuid == null) {
+ throw new IllegalArgumentException("uuid is null while uuidMask is not null!");
+ }
+ mServiceUuid = serviceUuid;
mUuidMask = uuidMask;
return this;
}
/**
- * Set service data filter.
+ * Set filtering on service data.
*/
- public Builder serviceData(byte[] serviceData) {
+ public Builder setServiceData(byte[] serviceData) {
mServiceData = serviceData;
+ mServiceDataMask = null; // clear service data mask
return this;
}
/**
- * Set partial service data filter bit mask. For any bit in the mask, set it to 1 if it
- * needs to match the one in service data, otherwise set it to 0 to ignore that bit.
+ * Set partial filter on service data. For any bit in the mask, set it to 1 if it needs to
+ * match the one in service data, otherwise set it to 0 to ignore that bit.
* <p>
- * The {@code serviceDataMask} must have the same length of the {@code serviceData} set
- * through {@link #serviceData(byte[])}.
+ * The {@code serviceDataMask} must have the same length of the {@code serviceData}.
+ *
+ * @throws IllegalArgumentException If {@code serviceDataMask} is {@code null} while
+ * {@code serviceData} is not or {@code serviceDataMask} and {@code serviceData}
+ * has different length.
*/
- public Builder serviceDataMask(byte[] serviceDataMask) {
+ public Builder setServiceData(byte[] serviceData, byte[] serviceDataMask) {
+ if (mServiceDataMask != null) {
+ if (mServiceData == null) {
+ throw new IllegalArgumentException(
+ "serviceData is null while serviceDataMask is not null");
+ }
+ // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two
+ // byte array need to be the same.
+ if (mServiceData.length != mServiceDataMask.length) {
+ throw new IllegalArgumentException(
+ "size mismatch for service data and service data mask");
+ }
+ }
+ mServiceData = serviceData;
mServiceDataMask = serviceDataMask;
return this;
}
/**
- * Set manufacturerId and manufacturerData. A negative manufacturerId is considered as
- * invalid id.
+ * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id.
* <p>
* Note the first two bytes of the {@code manufacturerData} is the manufacturerId.
+ *
+ * @throws IllegalArgumentException If the {@code manufacturerId} is invalid.
*/
- public Builder manufacturerData(int manufacturerId, byte[] manufacturerData) {
+ public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData) {
if (manufacturerData != null && manufacturerId < 0) {
throw new IllegalArgumentException("invalid manufacture id");
}
mManufacturerId = manufacturerId;
mManufacturerData = manufacturerData;
+ mManufacturerDataMask = null; // clear manufacturer data mask
return this;
}
/**
- * Set partial manufacture data filter bit mask. For any bit in the mask, set it the 1 if it
+ * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it
* needs to match the one in manufacturer data, otherwise set it to 0.
* <p>
- * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}
- * set through {@link #manufacturerData(int, byte[])}.
+ * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}.
+ *
+ * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or
+ * {@code manufacturerData} is null while {@code manufacturerDataMask} is not,
+ * or {@code manufacturerData} and {@code manufacturerDataMask} have different
+ * length.
*/
- public Builder manufacturerDataMask(byte[] manufacturerDataMask) {
+ public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData,
+ byte[] manufacturerDataMask) {
+ if (manufacturerData != null && manufacturerId < 0) {
+ throw new IllegalArgumentException("invalid manufacture id");
+ }
+ if (mManufacturerDataMask != null) {
+ if (mManufacturerData == null) {
+ throw new IllegalArgumentException(
+ "manufacturerData is null while manufacturerDataMask is not null");
+ }
+ // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths
+ // of the two byte array need to be the same.
+ if (mManufacturerData.length != mManufacturerDataMask.length) {
+ throw new IllegalArgumentException(
+ "size mismatch for manufacturerData and manufacturerDataMask");
+ }
+ }
+ mManufacturerId = manufacturerId;
+ mManufacturerData = manufacturerData;
mManufacturerDataMask = manufacturerDataMask;
return this;
}
@@ -527,48 +567,19 @@ public final class BluetoothLeScanFilter implements Parcelable {
* Set the desired rssi range for the filter. A scan result with rssi in the range of
* [minRssi, maxRssi] will be consider as a match.
*/
- public Builder rssiRange(int minRssi, int maxRssi) {
+ public Builder setRssiRange(int minRssi, int maxRssi) {
mMinRssi = minRssi;
mMaxRssi = maxRssi;
return this;
}
/**
- * Build {@link BluetoothLeScanFilter}.
+ * Build {@link ScanFilter}.
*
* @throws IllegalArgumentException If the filter cannot be built.
*/
- public BluetoothLeScanFilter build() {
- if (mUuidMask != null && mServiceUuid == null) {
- throw new IllegalArgumentException("uuid is null while uuidMask is not null!");
- }
-
- if (mServiceDataMask != null) {
- if (mServiceData == null) {
- throw new IllegalArgumentException(
- "serviceData is null while serviceDataMask is not null");
- }
- // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two
- // byte array need to be the same.
- if (mServiceData.length != mServiceDataMask.length) {
- throw new IllegalArgumentException(
- "size mismatch for service data and service data mask");
- }
- }
-
- if (mManufacturerDataMask != null) {
- if (mManufacturerData == null) {
- throw new IllegalArgumentException(
- "manufacturerData is null while manufacturerDataMask is not null");
- }
- // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths
- // of the two byte array need to be the same.
- if (mManufacturerData.length != mManufacturerDataMask.length) {
- throw new IllegalArgumentException(
- "size mismatch for manufacturerData and manufacturerDataMask");
- }
- }
- return new BluetoothLeScanFilter(mLocalName, mMacAddress,
+ public ScanFilter build() {
+ return new ScanFilter(mLocalName, mMacAddress,
mServiceUuid, mUuidMask,
mServiceData, mServiceDataMask,
mManufacturerId, mManufacturerData, mManufacturerDataMask, mMinRssi, mMaxRssi);
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
new file mode 100644
index 0000000..bd7304b
--- /dev/null
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -0,0 +1,278 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth.le;
+
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothUuid;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents a scan record from Bluetooth LE scan.
+ */
+public final class ScanRecord {
+
+ private static final String TAG = "ScanRecord";
+
+ // The following data type values are assigned by Bluetooth SIG.
+ // For more details refer to Bluetooth 4.1 specification, Volume 3, Part C, Section 18.
+ private static final int DATA_TYPE_FLAGS = 0x01;
+ private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;
+ private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;
+ private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;
+ private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;
+ private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;
+ private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;
+ private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
+ private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
+ private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
+ private static final int DATA_TYPE_SERVICE_DATA = 0x16;
+ private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
+
+ // Flags of the advertising data.
+ private final int mAdvertiseFlags;
+
+ @Nullable
+ private final List<ParcelUuid> mServiceUuids;
+
+ private final int mManufacturerId;
+ @Nullable
+ private final byte[] mManufacturerSpecificData;
+
+ @Nullable
+ private final ParcelUuid mServiceDataUuid;
+ @Nullable
+ private final byte[] mServiceData;
+
+ // Transmission power level(in dB).
+ private final int mTxPowerLevel;
+
+ // Local name of the Bluetooth LE device.
+ private final String mLocalName;
+
+ /**
+ * Returns the advertising flags indicating the discoverable mode and capability of the device.
+ * Returns -1 if the flag field is not set.
+ */
+ public int getAdvertiseFlags() {
+ return mAdvertiseFlags;
+ }
+
+ /**
+ * Returns a list of service uuids within the advertisement that are used to identify the
+ * bluetooth gatt services.
+ */
+ public List<ParcelUuid> getServiceUuids() {
+ return mServiceUuids;
+ }
+
+ /**
+ * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth
+ * SIG.
+ */
+ public int getManufacturerId() {
+ return mManufacturerId;
+ }
+
+ /**
+ * Returns the manufacturer specific data which is the content of manufacturer specific data
+ * field. The first 2 bytes of the data contain the company id.
+ */
+ public byte[] getManufacturerSpecificData() {
+ return mManufacturerSpecificData;
+ }
+
+ /**
+ * Returns a 16 bit uuid of the service that the service data is associated with.
+ */
+ public ParcelUuid getServiceDataUuid() {
+ return mServiceDataUuid;
+ }
+
+ /**
+ * Returns service data. The first two bytes should be a 16 bit service uuid associated with the
+ * service data.
+ */
+ public byte[] getServiceData() {
+ return mServiceData;
+ }
+
+ /**
+ * Returns the transmission power level of the packet in dBm. Returns {@link Integer#MIN_VALUE}
+ * if the field is not set. This value can be used to calculate the path loss of a received
+ * packet using the following equation:
+ * <p>
+ * <code>pathloss = txPowerLevel - rssi</code>
+ */
+ public int getTxPowerLevel() {
+ return mTxPowerLevel;
+ }
+
+ /**
+ * Returns the local name of the BLE device. The is a UTF-8 encoded string.
+ */
+ @Nullable
+ public String getLocalName() {
+ return mLocalName;
+ }
+
+ private ScanRecord(List<ParcelUuid> serviceUuids,
+ ParcelUuid serviceDataUuid, byte[] serviceData,
+ int manufacturerId,
+ byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel,
+ String localName) {
+ mServiceUuids = serviceUuids;
+ mManufacturerId = manufacturerId;
+ mManufacturerSpecificData = manufacturerSpecificData;
+ mServiceDataUuid = serviceDataUuid;
+ mServiceData = serviceData;
+ mLocalName = localName;
+ mAdvertiseFlags = advertiseFlags;
+ mTxPowerLevel = txPowerLevel;
+ }
+
+ @Override
+ public String toString() {
+ return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids
+ + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData="
+ + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid="
+ + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData)
+ + ", mTxPowerLevel=" + mTxPowerLevel + ", mLocalName=" + mLocalName + "]";
+ }
+
+ /**
+ * Parse scan record bytes to {@link ScanRecord}.
+ * <p>
+ * The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18.
+ * <p>
+ * All numerical multi-byte entities and values shall use little-endian <strong>byte</strong>
+ * order.
+ *
+ * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
+ */
+ public static ScanRecord parseFromBytes(byte[] scanRecord) {
+ if (scanRecord == null) {
+ return null;
+ }
+
+ int currentPos = 0;
+ int advertiseFlag = -1;
+ List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
+ String localName = null;
+ int txPowerLevel = Integer.MIN_VALUE;
+ ParcelUuid serviceDataUuid = null;
+ byte[] serviceData = null;
+ int manufacturerId = -1;
+ byte[] manufacturerSpecificData = null;
+
+ try {
+ while (currentPos < scanRecord.length) {
+ // length is unsigned int.
+ int length = scanRecord[currentPos++] & 0xFF;
+ if (length == 0) {
+ break;
+ }
+ // Note the length includes the length of the field type itself.
+ int dataLength = length - 1;
+ // fieldType is unsigned int.
+ int fieldType = scanRecord[currentPos++] & 0xFF;
+ switch (fieldType) {
+ case DATA_TYPE_FLAGS:
+ advertiseFlag = scanRecord[currentPos] & 0xFF;
+ break;
+ case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:
+ case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:
+ parseServiceUuid(scanRecord, currentPos,
+ dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
+ break;
+ case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
+ case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
+ parseServiceUuid(scanRecord, currentPos, dataLength,
+ BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
+ break;
+ case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
+ case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
+ parseServiceUuid(scanRecord, currentPos, dataLength,
+ BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids);
+ break;
+ case DATA_TYPE_LOCAL_NAME_SHORT:
+ case DATA_TYPE_LOCAL_NAME_COMPLETE:
+ localName = new String(
+ extractBytes(scanRecord, currentPos, dataLength));
+ break;
+ case DATA_TYPE_TX_POWER_LEVEL:
+ txPowerLevel = scanRecord[currentPos];
+ break;
+ case DATA_TYPE_SERVICE_DATA:
+ serviceData = extractBytes(scanRecord, currentPos, dataLength);
+ // The first two bytes of the service data are service data uuid.
+ int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
+ byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
+ serviceUuidLength);
+ serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes);
+ break;
+ case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
+ manufacturerSpecificData = extractBytes(scanRecord, currentPos,
+ dataLength);
+ // The first two bytes of the manufacturer specific data are
+ // manufacturer ids in little endian.
+ manufacturerId = ((manufacturerSpecificData[1] & 0xFF) << 8) +
+ (manufacturerSpecificData[0] & 0xFF);
+ break;
+ default:
+ // Just ignore, we don't handle such data type.
+ break;
+ }
+ currentPos += dataLength;
+ }
+
+ if (serviceUuids.isEmpty()) {
+ serviceUuids = null;
+ }
+ return new ScanRecord(serviceUuids, serviceDataUuid, serviceData,
+ manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel,
+ localName);
+ } catch (IndexOutOfBoundsException e) {
+ Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord));
+ return null;
+ }
+ }
+
+ // Parse service uuids.
+ private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
+ int uuidLength, List<ParcelUuid> serviceUuids) {
+ while (dataLength > 0) {
+ byte[] uuidBytes = extractBytes(scanRecord, currentPos,
+ uuidLength);
+ serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
+ dataLength -= uuidLength;
+ currentPos += uuidLength;
+ }
+ return currentPos;
+ }
+
+ // Helper method to extract bytes from byte array.
+ private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
+ byte[] bytes = new byte[length];
+ System.arraycopy(scanRecord, start, bytes, 0, length);
+ return bytes;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothLeScanner.aidl b/core/java/android/bluetooth/le/ScanResult.aidl
index 8cecdd7..3943035 100644
--- a/core/java/android/bluetooth/BluetoothLeScanner.aidl
+++ b/core/java/android/bluetooth/le/ScanResult.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-package android.bluetooth;
+package android.bluetooth.le;
-parcelable BluetoothLeScanner.ScanResult;
-parcelable BluetoothLeScanner.Settings;
+parcelable ScanResult; \ No newline at end of file
diff --git a/core/java/android/bluetooth/le/ScanResult.java b/core/java/android/bluetooth/le/ScanResult.java
new file mode 100644
index 0000000..7e6e8f8
--- /dev/null
+++ b/core/java/android/bluetooth/le/ScanResult.java
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth.le;
+
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothDevice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * ScanResult for Bluetooth LE scan.
+ */
+public final class ScanResult implements Parcelable {
+ // Remote bluetooth device.
+ private BluetoothDevice mDevice;
+
+ // Scan record, including advertising data and scan response data.
+ private byte[] mScanRecord;
+
+ // Received signal strength.
+ private int mRssi;
+
+ // Device timestamp when the result was last seen.
+ private long mTimestampNanos;
+
+ /**
+ * Constructor of scan result.
+ *
+ * @hide
+ */
+ public ScanResult(BluetoothDevice device, byte[] scanRecord, int rssi,
+ long timestampNanos) {
+ mDevice = device;
+ mScanRecord = scanRecord;
+ mRssi = rssi;
+ mTimestampNanos = timestampNanos;
+ }
+
+ private ScanResult(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mDevice != null) {
+ dest.writeInt(1);
+ mDevice.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
+ if (mScanRecord != null) {
+ dest.writeInt(1);
+ dest.writeByteArray(mScanRecord);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeInt(mRssi);
+ dest.writeLong(mTimestampNanos);
+ }
+
+ private void readFromParcel(Parcel in) {
+ if (in.readInt() == 1) {
+ mDevice = BluetoothDevice.CREATOR.createFromParcel(in);
+ }
+ if (in.readInt() == 1) {
+ mScanRecord = in.createByteArray();
+ }
+ mRssi = in.readInt();
+ mTimestampNanos = in.readLong();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the remote bluetooth device identified by the bluetooth device address.
+ */
+ @Nullable
+ public BluetoothDevice getDevice() {
+ return mDevice;
+ }
+
+ /**
+ * Returns the scan record, which can be a combination of advertisement and scan response.
+ */
+ @Nullable
+ public byte[] getScanRecord() {
+ return mScanRecord;
+ }
+
+ /**
+ * Returns the received signal strength in dBm. The valid range is [-127, 127].
+ */
+ public int getRssi() {
+ return mRssi;
+ }
+
+ /**
+ * Returns timestamp since boot when the scan record was observed.
+ */
+ public long getTimestampNanos() {
+ return mTimestampNanos;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ ScanResult other = (ScanResult) obj;
+ return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) &&
+ Objects.deepEquals(mScanRecord, other.mScanRecord)
+ && (mTimestampNanos == other.mTimestampNanos);
+ }
+
+ @Override
+ public String toString() {
+ return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord="
+ + Arrays.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampNanos="
+ + mTimestampNanos + '}';
+ }
+
+ public static final Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() {
+ @Override
+ public ScanResult createFromParcel(Parcel source) {
+ return new ScanResult(source);
+ }
+
+ @Override
+ public ScanResult[] newArray(int size) {
+ return new ScanResult[size];
+ }
+ };
+
+}
diff --git a/core/java/android/bluetooth/le/ScanSettings.aidl b/core/java/android/bluetooth/le/ScanSettings.aidl
new file mode 100644
index 0000000..eb169c1
--- /dev/null
+++ b/core/java/android/bluetooth/le/ScanSettings.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth.le;
+
+parcelable ScanSettings;
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
new file mode 100644
index 0000000..0a85675
--- /dev/null
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -0,0 +1,221 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth.le;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Settings for Bluetooth LE scan.
+ */
+public final class ScanSettings implements Parcelable {
+ /**
+ * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the
+ * least power.
+ */
+ public static final int SCAN_MODE_LOW_POWER = 0;
+ /**
+ * Perform Bluetooth LE scan in balanced power mode.
+ */
+ public static final int SCAN_MODE_BALANCED = 1;
+ /**
+ * Scan using highest duty cycle. It's recommended only using this mode when the application is
+ * running in foreground.
+ */
+ public static final int SCAN_MODE_LOW_LATENCY = 2;
+
+ /**
+ * Callback each time when a bluetooth advertisement is found.
+ */
+ public static final int CALLBACK_TYPE_ON_UPDATE = 0;
+ /**
+ * Callback when a bluetooth advertisement is found for the first time.
+ *
+ * @hide
+ */
+ public static final int CALLBACK_TYPE_ON_FOUND = 1;
+ /**
+ * Callback when a bluetooth advertisement is found for the first time, then lost.
+ *
+ * @hide
+ */
+ public static final int CALLBACK_TYPE_ON_LOST = 2;
+
+ /**
+ * Full scan result which contains device mac address, rssi, advertising and scan response and
+ * scan timestamp.
+ */
+ public static final int SCAN_RESULT_TYPE_FULL = 0;
+ /**
+ * Truncated scan result which contains device mac address, rssi and scan timestamp. Note it's
+ * possible for an app to get more scan results that it asks if there are multiple apps using
+ * this type. TODO: decide whether we could unhide this setting.
+ *
+ * @hide
+ */
+ public static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
+
+ // Bluetooth LE scan mode.
+ private int mScanMode;
+
+ // Bluetooth LE scan callback type
+ private int mCallbackType;
+
+ // Bluetooth LE scan result type
+ private int mScanResultType;
+
+ // Time of delay for reporting the scan result
+ private long mReportDelayNanos;
+
+ public int getScanMode() {
+ return mScanMode;
+ }
+
+ public int getCallbackType() {
+ return mCallbackType;
+ }
+
+ public int getScanResultType() {
+ return mScanResultType;
+ }
+
+ /**
+ * Returns report delay timestamp based on the device clock.
+ */
+ public long getReportDelayNanos() {
+ return mReportDelayNanos;
+ }
+
+ private ScanSettings(int scanMode, int callbackType, int scanResultType,
+ long reportDelayNanos) {
+ mScanMode = scanMode;
+ mCallbackType = callbackType;
+ mScanResultType = scanResultType;
+ mReportDelayNanos = reportDelayNanos;
+ }
+
+ private ScanSettings(Parcel in) {
+ mScanMode = in.readInt();
+ mCallbackType = in.readInt();
+ mScanResultType = in.readInt();
+ mReportDelayNanos = in.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mScanMode);
+ dest.writeInt(mCallbackType);
+ dest.writeInt(mScanResultType);
+ dest.writeLong(mReportDelayNanos);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<ScanSettings>
+ CREATOR = new Creator<ScanSettings>() {
+ @Override
+ public ScanSettings[] newArray(int size) {
+ return new ScanSettings[size];
+ }
+
+ @Override
+ public ScanSettings createFromParcel(Parcel in) {
+ return new ScanSettings(in);
+ }
+ };
+
+ /**
+ * Builder for {@link ScanSettings}.
+ */
+ public static final class Builder {
+ private int mScanMode = SCAN_MODE_LOW_POWER;
+ private int mCallbackType = CALLBACK_TYPE_ON_UPDATE;
+ private int mScanResultType = SCAN_RESULT_TYPE_FULL;
+ private long mReportDelayNanos = 0;
+
+ /**
+ * Set scan mode for Bluetooth LE scan.
+ *
+ * @param scanMode The scan mode can be one of
+ * {@link ScanSettings#SCAN_MODE_LOW_POWER},
+ * {@link ScanSettings#SCAN_MODE_BALANCED} or
+ * {@link ScanSettings#SCAN_MODE_LOW_LATENCY}.
+ * @throws IllegalArgumentException If the {@code scanMode} is invalid.
+ */
+ public Builder setScanMode(int scanMode) {
+ if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) {
+ throw new IllegalArgumentException("invalid scan mode " + scanMode);
+ }
+ mScanMode = scanMode;
+ return this;
+ }
+
+ /**
+ * Set callback type for Bluetooth LE scan.
+ *
+ * @param callbackType The callback type for the scan. Can only be
+ * {@link ScanSettings#CALLBACK_TYPE_ON_UPDATE}.
+ * @throws IllegalArgumentException If the {@code callbackType} is invalid.
+ */
+ public Builder setCallbackType(int callbackType) {
+ if (callbackType < CALLBACK_TYPE_ON_UPDATE
+ || callbackType > CALLBACK_TYPE_ON_LOST) {
+ throw new IllegalArgumentException("invalid callback type - " + callbackType);
+ }
+ mCallbackType = callbackType;
+ return this;
+ }
+
+ /**
+ * Set scan result type for Bluetooth LE scan.
+ *
+ * @param scanResultType Type for scan result, could be either
+ * {@link ScanSettings#SCAN_RESULT_TYPE_FULL} or
+ * {@link ScanSettings#SCAN_RESULT_TYPE_TRUNCATED}.
+ * @throws IllegalArgumentException If the {@code scanResultType} is invalid.
+ * @hide
+ */
+ public Builder setScanResultType(int scanResultType) {
+ if (scanResultType < SCAN_RESULT_TYPE_FULL
+ || scanResultType > SCAN_RESULT_TYPE_TRUNCATED) {
+ throw new IllegalArgumentException(
+ "invalid scanResultType - " + scanResultType);
+ }
+ mScanResultType = scanResultType;
+ return this;
+ }
+
+ /**
+ * Set report delay timestamp for Bluetooth LE scan.
+ */
+ public Builder setReportDelayNanos(long reportDelayNanos) {
+ mReportDelayNanos = reportDelayNanos;
+ return this;
+ }
+
+ /**
+ * Build {@link ScanSettings}.
+ */
+ public ScanSettings build() {
+ return new ScanSettings(mScanMode, mCallbackType, mScanResultType,
+ mReportDelayNanos);
+ }
+ }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2ff85c6..c69e669 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -40,6 +40,7 @@ import android.os.Looper;
import android.os.StatFs;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.MediaStore;
import android.util.AttributeSet;
import android.view.DisplayAdjustments;
import android.view.Display;
@@ -929,6 +930,40 @@ public abstract class Context {
public abstract File[] getExternalCacheDirs();
/**
+ * Returns absolute paths to application-specific directories on all
+ * external storage devices where the application can place media files.
+ * These files are scanned and made available to other apps through
+ * {@link MediaStore}.
+ * <p>
+ * This is like {@link #getExternalFilesDirs} in that these files will be
+ * deleted when the application is uninstalled, however there are some
+ * important differences:
+ * <ul>
+ * <li>External files are not always available: they will disappear if the
+ * user mounts the external storage on a computer or removes it.
+ * <li>There is no security enforced with these files.
+ * </ul>
+ * <p>
+ * External storage devices returned here are considered a permanent part of
+ * the device, including both emulated external storage and physical media
+ * slots, such as SD cards in a battery compartment. The returned paths do
+ * not include transient devices, such as USB flash drives.
+ * <p>
+ * An application may store data on any or all of the returned devices. For
+ * example, an app may choose to store large files on the device with the
+ * most available space, as measured by {@link StatFs}.
+ * <p>
+ * No permissions are required to read or write to the returned paths; they
+ * are always accessible to the calling app. Write access outside of these
+ * paths on secondary external storage devices is not available.
+ * <p>
+ * Returned paths may be {@code null} if a storage device is unavailable.
+ *
+ * @see Environment#getExternalStorageState(File)
+ */
+ public abstract File[] getExternalMediaDirs();
+
+ /**
* Returns an array of strings naming the private files associated with
* this Context's application package.
*
@@ -2660,6 +2695,15 @@ public abstract class Context {
/**
* Use with {@link #getSystemService} to retrieve a
+ * {@link android.content.RestrictionsManager} for retrieving application restrictions
+ * and requesting permissions for restricted operations.
+ * @see #getSystemService
+ * @see android.content.RestrictionsManager
+ */
+ public static final String RESTRICTIONS_SERVICE = "restrictions";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a
* {@link android.app.AppOpsManager} for tracking application operations
* on the device.
*
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index c66355b..dbf9122 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -237,6 +237,11 @@ public class ContextWrapper extends Context {
}
@Override
+ public File[] getExternalMediaDirs() {
+ return mBase.getExternalMediaDirs();
+ }
+
+ @Override
public File getDir(String name, int mode) {
return mBase.getDir(name, mode);
}
diff --git a/core/java/android/content/IRestrictionsManager.aidl b/core/java/android/content/IRestrictionsManager.aidl
new file mode 100644
index 0000000..b1c0a3a
--- /dev/null
+++ b/core/java/android/content/IRestrictionsManager.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.content;
+
+import android.os.Bundle;
+
+/**
+ * Interface used by the RestrictionsManager
+ * @hide
+ */
+interface IRestrictionsManager {
+ Bundle getApplicationRestrictions(in String packageName);
+ boolean hasRestrictionsProvider();
+ void requestPermission(in String packageName, in String requestTemplate, in Bundle requestData);
+ void notifyPermissionResponse(in String packageName, in Bundle response);
+}
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 3ff53bf..62f88a9 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -73,32 +73,38 @@ public class RestrictionEntry implements Parcelable {
*/
public static final int TYPE_MULTI_SELECT = 4;
+ /**
+ * A type of restriction. Use this for storing an integer value. The range of values
+ * is from {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}.
+ */
+ public static final int TYPE_INTEGER = 5;
+
/** The type of restriction. */
- private int type;
+ private int mType;
/** The unique key that identifies the restriction. */
- private String key;
+ private String mKey;
/** The user-visible title of the restriction. */
- private String title;
+ private String mTitle;
/** The user-visible secondary description of the restriction. */
- private String description;
+ private String mDescription;
/** The user-visible set of choices used for single-select and multi-select lists. */
- private String [] choices;
+ private String [] mChoiceEntries;
/** The values corresponding to the user-visible choices. The value(s) of this entry will
* one or more of these, returned by {@link #getAllSelectedStrings()} and
* {@link #getSelectedString()}.
*/
- private String [] values;
+ private String [] mChoiceValues;
/* The chosen value, whose content depends on the type of the restriction. */
- private String currentValue;
+ private String mCurrentValue;
/* List of selected choices in the multi-select case. */
- private String[] currentValues;
+ private String[] mCurrentValues;
/**
* Constructor for {@link #TYPE_CHOICE} type.
@@ -106,9 +112,9 @@ public class RestrictionEntry implements Parcelable {
* @param selectedString the current value
*/
public RestrictionEntry(String key, String selectedString) {
- this.key = key;
- this.type = TYPE_CHOICE;
- this.currentValue = selectedString;
+ this.mKey = key;
+ this.mType = TYPE_CHOICE;
+ this.mCurrentValue = selectedString;
}
/**
@@ -117,8 +123,8 @@ public class RestrictionEntry implements Parcelable {
* @param selectedState whether this restriction is selected or not
*/
public RestrictionEntry(String key, boolean selectedState) {
- this.key = key;
- this.type = TYPE_BOOLEAN;
+ this.mKey = key;
+ this.mType = TYPE_BOOLEAN;
setSelectedState(selectedState);
}
@@ -128,9 +134,20 @@ public class RestrictionEntry implements Parcelable {
* @param selectedStrings the list of values that are currently selected
*/
public RestrictionEntry(String key, String[] selectedStrings) {
- this.key = key;
- this.type = TYPE_MULTI_SELECT;
- this.currentValues = selectedStrings;
+ this.mKey = key;
+ this.mType = TYPE_MULTI_SELECT;
+ this.mCurrentValues = selectedStrings;
+ }
+
+ /**
+ * Constructor for {@link #TYPE_INTEGER} type.
+ * @param key the unique key for this restriction
+ * @param selectedInt the integer value of the restriction
+ */
+ public RestrictionEntry(String key, int selectedInt) {
+ mKey = key;
+ mType = TYPE_INTEGER;
+ setIntValue(selectedInt);
}
/**
@@ -138,7 +155,7 @@ public class RestrictionEntry implements Parcelable {
* @param type the type for this restriction.
*/
public void setType(int type) {
- this.type = type;
+ this.mType = type;
}
/**
@@ -146,7 +163,7 @@ public class RestrictionEntry implements Parcelable {
* @return the type for this restriction
*/
public int getType() {
- return type;
+ return mType;
}
/**
@@ -155,7 +172,7 @@ public class RestrictionEntry implements Parcelable {
* single string values.
*/
public String getSelectedString() {
- return currentValue;
+ return mCurrentValue;
}
/**
@@ -164,7 +181,7 @@ public class RestrictionEntry implements Parcelable {
* null otherwise.
*/
public String[] getAllSelectedStrings() {
- return currentValues;
+ return mCurrentValues;
}
/**
@@ -172,7 +189,23 @@ public class RestrictionEntry implements Parcelable {
* @return the current selected state of the entry.
*/
public boolean getSelectedState() {
- return Boolean.parseBoolean(currentValue);
+ return Boolean.parseBoolean(mCurrentValue);
+ }
+
+ /**
+ * Returns the value of the entry as an integer when the type is {@link #TYPE_INTEGER}.
+ * @return the integer value of the entry.
+ */
+ public int getIntValue() {
+ return Integer.parseInt(mCurrentValue);
+ }
+
+ /**
+ * Sets the integer value of the entry when the type is {@link #TYPE_INTEGER}.
+ * @param value the integer value to set.
+ */
+ public void setIntValue(int value) {
+ mCurrentValue = Integer.toString(value);
}
/**
@@ -181,7 +214,7 @@ public class RestrictionEntry implements Parcelable {
* @param selectedString the string value to select.
*/
public void setSelectedString(String selectedString) {
- currentValue = selectedString;
+ mCurrentValue = selectedString;
}
/**
@@ -190,7 +223,7 @@ public class RestrictionEntry implements Parcelable {
* @param state the current selected state
*/
public void setSelectedState(boolean state) {
- currentValue = Boolean.toString(state);
+ mCurrentValue = Boolean.toString(state);
}
/**
@@ -199,7 +232,7 @@ public class RestrictionEntry implements Parcelable {
* @param allSelectedStrings the current list of selected values.
*/
public void setAllSelectedStrings(String[] allSelectedStrings) {
- currentValues = allSelectedStrings;
+ mCurrentValues = allSelectedStrings;
}
/**
@@ -216,7 +249,7 @@ public class RestrictionEntry implements Parcelable {
* @see #getAllSelectedStrings()
*/
public void setChoiceValues(String[] choiceValues) {
- values = choiceValues;
+ mChoiceValues = choiceValues;
}
/**
@@ -227,7 +260,7 @@ public class RestrictionEntry implements Parcelable {
* @see #setChoiceValues(String[])
*/
public void setChoiceValues(Context context, int stringArrayResId) {
- values = context.getResources().getStringArray(stringArrayResId);
+ mChoiceValues = context.getResources().getStringArray(stringArrayResId);
}
/**
@@ -235,7 +268,7 @@ public class RestrictionEntry implements Parcelable {
* @return the list of possible values.
*/
public String[] getChoiceValues() {
- return values;
+ return mChoiceValues;
}
/**
@@ -248,7 +281,7 @@ public class RestrictionEntry implements Parcelable {
* @see #setChoiceValues(String[])
*/
public void setChoiceEntries(String[] choiceEntries) {
- choices = choiceEntries;
+ mChoiceEntries = choiceEntries;
}
/** Sets a list of strings that will be presented as choices to the user. This is similar to
@@ -257,7 +290,7 @@ public class RestrictionEntry implements Parcelable {
* @param stringArrayResId the resource id of a string array containing the possible entries.
*/
public void setChoiceEntries(Context context, int stringArrayResId) {
- choices = context.getResources().getStringArray(stringArrayResId);
+ mChoiceEntries = context.getResources().getStringArray(stringArrayResId);
}
/**
@@ -265,7 +298,7 @@ public class RestrictionEntry implements Parcelable {
* @return the list of choices presented to the user.
*/
public String[] getChoiceEntries() {
- return choices;
+ return mChoiceEntries;
}
/**
@@ -273,7 +306,7 @@ public class RestrictionEntry implements Parcelable {
* @return the user-visible description, null if none was set earlier.
*/
public String getDescription() {
- return description;
+ return mDescription;
}
/**
@@ -283,7 +316,7 @@ public class RestrictionEntry implements Parcelable {
* @param description the user-visible description string.
*/
public void setDescription(String description) {
- this.description = description;
+ this.mDescription = description;
}
/**
@@ -291,7 +324,7 @@ public class RestrictionEntry implements Parcelable {
* @return the key for the restriction.
*/
public String getKey() {
- return key;
+ return mKey;
}
/**
@@ -299,7 +332,7 @@ public class RestrictionEntry implements Parcelable {
* @return the user-visible title for the entry, null if none was set earlier.
*/
public String getTitle() {
- return title;
+ return mTitle;
}
/**
@@ -307,7 +340,7 @@ public class RestrictionEntry implements Parcelable {
* @param title the user-visible title for the entry.
*/
public void setTitle(String title) {
- this.title = title;
+ this.mTitle = title;
}
private boolean equalArrays(String[] one, String[] other) {
@@ -324,23 +357,23 @@ public class RestrictionEntry implements Parcelable {
if (!(o instanceof RestrictionEntry)) return false;
final RestrictionEntry other = (RestrictionEntry) o;
// Make sure that either currentValue matches or currentValues matches.
- return type == other.type && key.equals(other.key)
+ return mType == other.mType && mKey.equals(other.mKey)
&&
- ((currentValues == null && other.currentValues == null
- && currentValue != null && currentValue.equals(other.currentValue))
+ ((mCurrentValues == null && other.mCurrentValues == null
+ && mCurrentValue != null && mCurrentValue.equals(other.mCurrentValue))
||
- (currentValue == null && other.currentValue == null
- && currentValues != null && equalArrays(currentValues, other.currentValues)));
+ (mCurrentValue == null && other.mCurrentValue == null
+ && mCurrentValues != null && equalArrays(mCurrentValues, other.mCurrentValues)));
}
@Override
public int hashCode() {
int result = 17;
- result = 31 * result + key.hashCode();
- if (currentValue != null) {
- result = 31 * result + currentValue.hashCode();
- } else if (currentValues != null) {
- for (String value : currentValues) {
+ result = 31 * result + mKey.hashCode();
+ if (mCurrentValue != null) {
+ result = 31 * result + mCurrentValue.hashCode();
+ } else if (mCurrentValues != null) {
+ for (String value : mCurrentValues) {
if (value != null) {
result = 31 * result + value.hashCode();
}
@@ -359,14 +392,14 @@ public class RestrictionEntry implements Parcelable {
}
public RestrictionEntry(Parcel in) {
- type = in.readInt();
- key = in.readString();
- title = in.readString();
- description = in.readString();
- choices = readArray(in);
- values = readArray(in);
- currentValue = in.readString();
- currentValues = readArray(in);
+ mType = in.readInt();
+ mKey = in.readString();
+ mTitle = in.readString();
+ mDescription = in.readString();
+ mChoiceEntries = readArray(in);
+ mChoiceValues = readArray(in);
+ mCurrentValue = in.readString();
+ mCurrentValues = readArray(in);
}
@Override
@@ -387,14 +420,14 @@ public class RestrictionEntry implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(type);
- dest.writeString(key);
- dest.writeString(title);
- dest.writeString(description);
- writeArray(dest, choices);
- writeArray(dest, values);
- dest.writeString(currentValue);
- writeArray(dest, currentValues);
+ dest.writeInt(mType);
+ dest.writeString(mKey);
+ dest.writeString(mTitle);
+ dest.writeString(mDescription);
+ writeArray(dest, mChoiceEntries);
+ writeArray(dest, mChoiceValues);
+ dest.writeString(mCurrentValue);
+ writeArray(dest, mCurrentValues);
}
public static final Creator<RestrictionEntry> CREATOR = new Creator<RestrictionEntry>() {
@@ -409,6 +442,6 @@ public class RestrictionEntry implements Parcelable {
@Override
public String toString() {
- return "RestrictionsEntry {type=" + type + ", key=" + key + ", value=" + currentValue + "}";
+ return "RestrictionsEntry {type=" + mType + ", key=" + mKey + ", value=" + mCurrentValue + "}";
}
}
diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java
new file mode 100644
index 0000000..0dd0edd
--- /dev/null
+++ b/core/java/android/content/RestrictionsManager.java
@@ -0,0 +1,344 @@
+/*
+ * 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.
+ */
+
+package android.content;
+
+import android.app.admin.DevicePolicyManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Provides a mechanism for apps to query restrictions imposed by an entity that
+ * manages the user. Apps can also send permission requests to a local or remote
+ * device administrator to override default app-specific restrictions or any other
+ * operation that needs explicit authorization from the administrator.
+ * <p>
+ * Apps can expose a set of restrictions via a runtime receiver mechanism or via
+ * static meta data in the manifest.
+ * <p>
+ * If the user has an active restrictions provider, dynamic requests can be made in
+ * addition to the statically imposed restrictions. Dynamic requests are app-specific
+ * and can be expressed via a predefined set of templates.
+ * <p>
+ * The RestrictionsManager forwards the dynamic requests to the active
+ * restrictions provider. The restrictions provider can respond back to requests by calling
+ * {@link #notifyPermissionResponse(String, Bundle)}, when
+ * a response is received from the administrator of the device or user
+ * The response is relayed back to the application via a protected broadcast,
+ * {@link #ACTION_PERMISSION_RESPONSE_RECEIVED}.
+ * <p>
+ * Static restrictions are specified by an XML file referenced by a meta-data attribute
+ * in the manifest. This enables applications as well as any web administration consoles
+ * to be able to read the template from the apk.
+ * <p>
+ * The syntax of the XML format is as follows:
+ * <pre>
+ * &lt;restrictions&gt;
+ * &lt;restriction
+ * android:key="&lt;key&gt;"
+ * android:restrictionType="boolean|string|integer|multi-select|null"
+ * ... /&gt;
+ * &lt;restriction ... /&gt;
+ * &lt;/restrictions&gt;
+ * </pre>
+ * <p>
+ * The attributes for each restriction depend on the restriction type.
+ *
+ * @see RestrictionEntry
+ */
+public class RestrictionsManager {
+
+ /**
+ * Broadcast intent delivered when a response is received for a permission
+ * request. The response is not available for later query, so the receiver
+ * must persist and/or immediately act upon the response. The application
+ * should not interrupt the user by coming to the foreground if it isn't
+ * currently in the foreground. It can post a notification instead, informing
+ * the user of a change in state.
+ * <p>
+ * For instance, if the user requested permission to make an in-app purchase,
+ * the app can post a notification that the request had been granted or denied,
+ * and allow the purchase to go through.
+ * <p>
+ * The broadcast Intent carries the following extra:
+ * {@link #EXTRA_RESPONSE_BUNDLE}.
+ */
+ public static final String ACTION_PERMISSION_RESPONSE_RECEIVED =
+ "android.intent.action.PERMISSION_RESPONSE_RECEIVED";
+
+ /**
+ * Protected broadcast intent sent to the active restrictions provider. The intent
+ * contains the following extras:<p>
+ * <ul>
+ * <li>{@link #EXTRA_PACKAGE_NAME} : String; the package name of the application requesting
+ * permission.</li>
+ * <li>{@link #EXTRA_TEMPLATE_ID} : String; the template of the request.</li>
+ * <li>{@link #EXTRA_REQUEST_BUNDLE} : Bundle; contains the template-specific keys and values
+ * for the request.
+ * </ul>
+ * @see DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName)
+ * @see #requestPermission(String, String, Bundle)
+ */
+ public static final String ACTION_REQUEST_PERMISSION =
+ "android.intent.action.REQUEST_PERMISSION";
+
+ /**
+ * The package name of the application making the request.
+ */
+ public static final String EXTRA_PACKAGE_NAME = "package_name";
+
+ /**
+ * The template id that specifies what kind of a request it is and may indicate
+ * how the request is to be presented to the administrator. Must be either one of
+ * the predefined templates or a custom one specified by the application that the
+ * restrictions provider is familiar with.
+ */
+ public static final String EXTRA_TEMPLATE_ID = "template_id";
+
+ /**
+ * A bundle containing the details about the request. The contents depend on the
+ * template id.
+ * @see #EXTRA_TEMPLATE_ID
+ */
+ public static final String EXTRA_REQUEST_BUNDLE = "request_bundle";
+
+ /**
+ * Contains a response from the administrator for specific request.
+ * The bundle contains the following information, at least:
+ * <ul>
+ * <li>{@link #REQUEST_KEY_ID}: The request id.</li>
+ * <li>{@link #REQUEST_KEY_DATA}: The request reference data.</li>
+ * </ul>
+ * <p>
+ * And depending on what the request template was, the bundle will contain the actual
+ * result of the request. For {@link #REQUEST_TEMPLATE_QUESTION}, the result will be in
+ * {@link #RESPONSE_KEY_BOOLEAN}, which is of type boolean; true if the administrator
+ * approved the request, false otherwise.
+ */
+ public static final String EXTRA_RESPONSE_BUNDLE = "response_bundle";
+
+
+ /**
+ * Request template that presents a simple question, with a possible title and icon.
+ * <p>
+ * Required keys are
+ * {@link #REQUEST_KEY_ID} and {@link #REQUEST_KEY_MESSAGE}.
+ * <p>
+ * Optional keys are
+ * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE},
+ * {@link #REQUEST_KEY_APPROVE_LABEL} and {@link #REQUEST_KEY_DENY_LABEL}.
+ */
+ public static final String REQUEST_TEMPLATE_QUESTION = "android.req_template.type.simple";
+
+ /**
+ * Key for request ID contained in the request bundle.
+ * <p>
+ * App-generated request id to identify the specific request when receiving
+ * a response. This value is returned in the {@link #EXTRA_RESPONSE_BUNDLE}.
+ * <p>
+ * Type: String
+ */
+ public static final String REQUEST_KEY_ID = "android.req_template.req_id";
+
+ /**
+ * Key for request data contained in the request bundle.
+ * <p>
+ * Optional, typically used to identify the specific data that is being referred to,
+ * such as the unique identifier for a movie or book. This is not used for display
+ * purposes and is more like a cookie. This value is returned in the
+ * {@link #EXTRA_RESPONSE_BUNDLE}.
+ * <p>
+ * Type: String
+ */
+ public static final String REQUEST_KEY_DATA = "android.req_template.data";
+
+ /**
+ * Key for request title contained in the request bundle.
+ * <p>
+ * Optional, typically used as the title of any notification or dialog presented
+ * to the administrator who approves the request.
+ * <p>
+ * Type: String
+ */
+ public static final String REQUEST_KEY_TITLE = "android.req_template.title";
+
+ /**
+ * Key for request message contained in the request bundle.
+ * <p>
+ * Required, shown as the actual message in a notification or dialog presented
+ * to the administrator who approves the request.
+ * <p>
+ * Type: String
+ */
+ public static final String REQUEST_KEY_MESSAGE = "android.req_template.mesg";
+
+ /**
+ * Key for request icon contained in the request bundle.
+ * <p>
+ * Optional, shown alongside the request message presented to the administrator
+ * who approves the request.
+ * <p>
+ * Type: Bitmap
+ */
+ public static final String REQUEST_KEY_ICON = "android.req_template.icon";
+
+ /**
+ * Key for request approval button label contained in the request bundle.
+ * <p>
+ * Optional, may be shown as a label on the positive button in a dialog or
+ * notification presented to the administrator who approves the request.
+ * <p>
+ * Type: String
+ */
+ public static final String REQUEST_KEY_APPROVE_LABEL = "android.req_template.accept";
+
+ /**
+ * Key for request rejection button label contained in the request bundle.
+ * <p>
+ * Optional, may be shown as a label on the negative button in a dialog or
+ * notification presented to the administrator who approves the request.
+ * <p>
+ * Type: String
+ */
+ public static final String REQUEST_KEY_DENY_LABEL = "android.req_template.reject";
+
+ /**
+ * Key for requestor's name contained in the request bundle. This value is not specified by
+ * the application. It is automatically inserted into the Bundle by the Restrictions Provider
+ * before it is sent to the administrator.
+ * <p>
+ * Type: String
+ */
+ public static final String REQUEST_KEY_REQUESTOR_NAME = "android.req_template.requestor";
+
+ /**
+ * Key for requestor's device name contained in the request bundle. This value is not specified
+ * by the application. It is automatically inserted into the Bundle by the Restrictions Provider
+ * before it is sent to the administrator.
+ * <p>
+ * Type: String
+ */
+ public static final String REQUEST_KEY_DEVICE_NAME = "android.req_template.device";
+
+ /**
+ * Key for the response in the response bundle sent to the application, for a permission
+ * request.
+ * <p>
+ * Type: boolean
+ */
+ public static final String RESPONSE_KEY_BOOLEAN = "android.req_template.response";
+
+ private static final String TAG = "RestrictionsManager";
+
+ private final Context mContext;
+ private final IRestrictionsManager mService;
+
+ /**
+ * @hide
+ */
+ public RestrictionsManager(Context context, IRestrictionsManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Returns any available set of application-specific restrictions applicable
+ * to this application.
+ * @return
+ */
+ public Bundle getApplicationRestrictions() {
+ try {
+ if (mService != null) {
+ return mService.getApplicationRestrictions(mContext.getPackageName());
+ }
+ } catch (RemoteException re) {
+ Log.w(TAG, "Couldn't reach service");
+ }
+ return null;
+ }
+
+ /**
+ * Called by an application to check if permission requests can be made. If false,
+ * there is no need to request permission for an operation, unless a static
+ * restriction applies to that operation.
+ * @return
+ */
+ public boolean hasRestrictionsProvider() {
+ try {
+ if (mService != null) {
+ return mService.hasRestrictionsProvider();
+ }
+ } catch (RemoteException re) {
+ Log.w(TAG, "Couldn't reach service");
+ }
+ return false;
+ }
+
+ /**
+ * Called by an application to request permission for an operation. The contents of the
+ * request are passed in a Bundle that contains several pieces of data depending on the
+ * chosen request template.
+ *
+ * @param requestTemplate The request template to use. The template could be one of the
+ * predefined templates specified in this class or a custom template that the specific
+ * Restrictions Provider might understand. For custom templates, the template name should be
+ * namespaced to avoid collisions with predefined templates and templates specified by
+ * other Restrictions Provider vendors.
+ * @param requestData A Bundle containing the data corresponding to the specified request
+ * template. The keys for the data in the bundle depend on the kind of template chosen.
+ */
+ public void requestPermission(String requestTemplate, Bundle requestData) {
+ try {
+ if (mService != null) {
+ mService.requestPermission(mContext.getPackageName(), requestTemplate, requestData);
+ }
+ } catch (RemoteException re) {
+ Log.w(TAG, "Couldn't reach service");
+ }
+ }
+
+ /**
+ * Called by the Restrictions Provider when a response is available to be
+ * delivered to an application.
+ * @param packageName
+ * @param response
+ */
+ public void notifyPermissionResponse(String packageName, Bundle response) {
+ try {
+ if (mService != null) {
+ mService.notifyPermissionResponse(packageName, response);
+ }
+ } catch (RemoteException re) {
+ Log.w(TAG, "Couldn't reach service");
+ }
+ }
+
+ /**
+ * Parse and return the list of restrictions defined in the manifest for the specified
+ * package, if any.
+ * @param packageName The application for which to fetch the restrictions list.
+ * @return The list of RestrictionEntry objects created from the XML file specified
+ * in the manifest, or null if none was specified.
+ */
+ public List<RestrictionEntry> getManifestRestrictions(String packageName) {
+ // TODO:
+ return null;
+ }
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 44a6a5d..70668e1 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -433,6 +433,13 @@ interface IPackageManager {
in VerificationParams verificationParams,
in ContainerEncryptionParams encryptionParams);
+ void installPackageWithVerificationEncryptionAndAbiOverrideEtc(in Uri packageURI,
+ in IPackageInstallObserver observer, in IPackageInstallObserver2 observer2,
+ int flags, in String installerPackageName,
+ in VerificationParams verificationParams,
+ in ContainerEncryptionParams encryptionParams,
+ in String packageAbiOverride);
+
int installExistingPackageAsUser(String packageName, int userId);
void verifyPendingInstall(int id, int verificationCode);
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 9087338..5d48868 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -30,6 +30,7 @@ import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.DisplayMetrics;
import android.util.Log;
/**
@@ -47,21 +48,22 @@ public class LauncherActivityInfo {
private ActivityInfo mActivityInfo;
private ComponentName mComponentName;
private UserHandle mUser;
- // TODO: Fetch this value from PM
private long mFirstInstallTime;
/**
* Create a launchable activity object for a given ResolveInfo and user.
- *
+ *
* @param context The context for fetching resources.
* @param info ResolveInfo from which to create the LauncherActivityInfo.
* @param user The UserHandle of the profile to which this activity belongs.
*/
- LauncherActivityInfo(Context context, ResolveInfo info, UserHandle user) {
+ LauncherActivityInfo(Context context, ResolveInfo info, UserHandle user,
+ long firstInstallTime) {
this(context);
- this.mActivityInfo = info.activityInfo;
- this.mComponentName = LauncherApps.getComponentName(info);
- this.mUser = user;
+ mActivityInfo = info.activityInfo;
+ mComponentName = LauncherApps.getComponentName(info);
+ mUser = user;
+ mFirstInstallTime = firstInstallTime;
}
LauncherActivityInfo(Context context) {
@@ -79,7 +81,13 @@ public class LauncherActivityInfo {
}
/**
- * Returns the user handle of the user profile that this activity belongs to.
+ * Returns the user handle of the user profile that this activity belongs to. In order to
+ * persist the identity of the profile, do not store the UserHandle. Instead retrieve its
+ * serial number from UserManager. You can convert the serial number back to a UserHandle
+ * for later use.
+ *
+ * @see UserManager#getSerialNumberForUser(UserHandle)
+ * @see UserManager#getUserForSerialNumber(long)
*
* @return The UserHandle of the profile.
*/
@@ -89,7 +97,7 @@ public class LauncherActivityInfo {
/**
* Retrieves the label for the activity.
- *
+ *
* @return The label for the activity.
*/
public CharSequence getLabel() {
@@ -98,8 +106,10 @@ public class LauncherActivityInfo {
/**
* Returns the icon for this activity, without any badging for the profile.
- * @param density The preferred density of the icon, zero for default density.
+ * @param density The preferred density of the icon, zero for default density. Use
+ * density DPI values from {@link DisplayMetrics}.
* @see #getBadgedIcon(int)
+ * @see DisplayMetrics
* @return The drawable associated with the activity
*/
public Drawable getIcon(int density) {
@@ -109,15 +119,25 @@ public class LauncherActivityInfo {
/**
* Returns the application flags from the ApplicationInfo of the activity.
- *
+ *
* @return Application flags
+ * @hide remove before shipping
*/
public int getApplicationFlags() {
return mActivityInfo.applicationInfo.flags;
}
/**
+ * Returns the application info for the appliction this activity belongs to.
+ * @return
+ */
+ public ApplicationInfo getApplicationInfo() {
+ return mActivityInfo.applicationInfo;
+ }
+
+ /**
* Returns the time at which the package was first installed.
+ *
* @return The time of installation of the package, in milliseconds.
*/
public long getFirstInstallTime() {
@@ -134,7 +154,9 @@ public class LauncherActivityInfo {
/**
* Returns the activity icon with badging appropriate for the profile.
- * @param density Optional density for the icon, or 0 to use the default density.
+ * @param density Optional density for the icon, or 0 to use the default density. Use
+ * {@link DisplayMetrics} for DPI values.
+ * @see DisplayMetrics
* @return A badged icon for the activity.
*/
public Drawable getBadgedIcon(int density) {
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 8025b60..04c0b9f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -16,15 +16,18 @@
package android.content.pm;
+import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ILauncherApps;
import android.content.pm.IOnAppsChangedListener;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Log;
import java.util.ArrayList;
@@ -36,6 +39,12 @@ import java.util.List;
* managed profiles. This is mainly for use by launchers. Apps can be queried for each user profile.
* Since the PackageManager will not deliver package broadcasts for other profiles, you can register
* for package changes here.
+ * <p>
+ * To watch for managed profiles being added or removed, register for the following broadcasts:
+ * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
+ * <p>
+ * You can retrieve the list of profiles associated with this user with
+ * {@link UserManager#getUserProfiles()}.
*/
public class LauncherApps {
@@ -44,12 +53,13 @@ public class LauncherApps {
private Context mContext;
private ILauncherApps mService;
+ private PackageManager mPm;
private List<OnAppsChangedListener> mListeners
= new ArrayList<OnAppsChangedListener>();
/**
- * Callbacks for changes to this and related managed profiles.
+ * Callbacks for package changes to this and related managed profiles.
*/
public interface OnAppsChangedListener {
/**
@@ -57,6 +67,7 @@ public class LauncherApps {
*
* @param user The UserHandle of the profile that generated the change.
* @param packageName The name of the package that was removed.
+ * @hide remove before ship
*/
void onPackageRemoved(UserHandle user, String packageName);
@@ -65,6 +76,7 @@ public class LauncherApps {
*
* @param user The UserHandle of the profile that generated the change.
* @param packageName The name of the package that was added.
+ * @hide remove before ship
*/
void onPackageAdded(UserHandle user, String packageName);
@@ -73,6 +85,7 @@ public class LauncherApps {
*
* @param user The UserHandle of the profile that generated the change.
* @param packageName The name of the package that has changed.
+ * @hide remove before ship
*/
void onPackageChanged(UserHandle user, String packageName);
@@ -86,6 +99,7 @@ public class LauncherApps {
* available.
* @param replacing Indicates whether these packages are replacing
* existing ones.
+ * @hide remove before ship
*/
void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing);
@@ -99,14 +113,66 @@ public class LauncherApps {
* unavailable.
* @param replacing Indicates whether the packages are about to be
* replaced with new versions.
+ * @hide remove before ship
*/
void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing);
+
+ /**
+ * Indicates that a package was removed from the specified profile.
+ *
+ * @param packageName The name of the package that was removed.
+ * @param user The UserHandle of the profile that generated the change.
+ */
+ void onPackageRemoved(String packageName, UserHandle user);
+
+ /**
+ * Indicates that a package was added to the specified profile.
+ *
+ * @param packageName The name of the package that was added.
+ * @param user The UserHandle of the profile that generated the change.
+ */
+ void onPackageAdded(String packageName, UserHandle user);
+
+ /**
+ * Indicates that a package was modified in the specified profile.
+ *
+ * @param packageName The name of the package that has changed.
+ * @param user The UserHandle of the profile that generated the change.
+ */
+ void onPackageChanged(String packageName, UserHandle user);
+
+ /**
+ * Indicates that one or more packages have become available. For
+ * example, this can happen when a removable storage card has
+ * reappeared.
+ *
+ * @param packageNames The names of the packages that have become
+ * available.
+ * @param user The UserHandle of the profile that generated the change.
+ * @param replacing Indicates whether these packages are replacing
+ * existing ones.
+ */
+ void onPackagesAvailable(String [] packageNames, UserHandle user, boolean replacing);
+
+ /**
+ * Indicates that one or more packages have become unavailable. For
+ * example, this can happen when a removable storage card has been
+ * removed.
+ *
+ * @param packageNames The names of the packages that have become
+ * unavailable.
+ * @param user The UserHandle of the profile that generated the change.
+ * @param replacing Indicates whether the packages are about to be
+ * replaced with new versions.
+ */
+ void onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing);
}
/** @hide */
public LauncherApps(Context context, ILauncherApps service) {
mContext = context;
mService = service;
+ mPm = context.getPackageManager();
}
/**
@@ -131,7 +197,15 @@ public class LauncherApps {
final int count = activities.size();
for (int i = 0; i < count; i++) {
ResolveInfo ri = activities.get(i);
- LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user);
+ long firstInstallTime = 0;
+ try {
+ firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
+ } catch (NameNotFoundException nnfe) {
+ // Sorry, can't find package
+ }
+ LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user,
+ firstInstallTime);
if (DEBUG) {
Log.v(TAG, "Returning activity for profile " + user + " : "
+ lai.getComponentName());
@@ -157,7 +231,15 @@ public class LauncherApps {
try {
ResolveInfo ri = mService.resolveActivity(intent, user);
if (ri != null) {
- LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user);
+ long firstInstallTime = 0;
+ try {
+ firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
+ } catch (NameNotFoundException nnfe) {
+ // Sorry, can't find package
+ }
+ LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user,
+ firstInstallTime);
return info;
}
} catch (RemoteException re) {
@@ -173,9 +255,23 @@ public class LauncherApps {
* @param sourceBounds The Rect containing the source bounds of the clicked icon
* @param opts Options to pass to startActivity
* @param user The UserHandle of the profile
+ * @hide remove before ship
*/
public void startActivityForProfile(ComponentName component, Rect sourceBounds,
Bundle opts, UserHandle user) {
+ startActivityForProfile(component, user, sourceBounds, opts);
+ }
+
+ /**
+ * Starts an activity in the specified profile.
+ *
+ * @param component The ComponentName of the activity to launch
+ * @param user The UserHandle of the profile
+ * @param sourceBounds The Rect containing the source bounds of the clicked icon
+ * @param opts Options to pass to startActivity
+ */
+ public void startActivityForProfile(ComponentName component, UserHandle user, Rect sourceBounds,
+ Bundle opts) {
if (DEBUG) {
Log.i(TAG, "StartActivityForProfile " + component + " " + user.getIdentifier());
}
@@ -224,13 +320,15 @@ public class LauncherApps {
*
* @param listener The listener to add.
*/
- public synchronized void addOnAppsChangedListener(OnAppsChangedListener listener) {
- if (listener != null && !mListeners.contains(listener)) {
- mListeners.add(listener);
- if (mListeners.size() == 1) {
- try {
- mService.addOnAppsChangedListener(mAppsChangedListener);
- } catch (RemoteException re) {
+ public void addOnAppsChangedListener(OnAppsChangedListener listener) {
+ synchronized (this) {
+ if (listener != null && !mListeners.contains(listener)) {
+ mListeners.add(listener);
+ if (mListeners.size() == 1) {
+ try {
+ mService.addOnAppsChangedListener(mAppsChangedListener);
+ } catch (RemoteException re) {
+ }
}
}
}
@@ -242,12 +340,14 @@ public class LauncherApps {
* @param listener The listener to remove.
* @see #addOnAppsChangedListener(OnAppsChangedListener)
*/
- public synchronized void removeOnAppsChangedListener(OnAppsChangedListener listener) {
- mListeners.remove(listener);
- if (mListeners.size() == 0) {
- try {
- mService.removeOnAppsChangedListener(mAppsChangedListener);
- } catch (RemoteException re) {
+ public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
+ synchronized (this) {
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ try {
+ mService.removeOnAppsChangedListener(mAppsChangedListener);
+ } catch (RemoteException re) {
+ }
}
}
}
@@ -261,7 +361,8 @@ public class LauncherApps {
}
synchronized (LauncherApps.this) {
for (OnAppsChangedListener listener : mListeners) {
- listener.onPackageRemoved(user, packageName);
+ listener.onPackageRemoved(user, packageName); // TODO: Remove before ship
+ listener.onPackageRemoved(packageName, user);
}
}
}
@@ -273,7 +374,8 @@ public class LauncherApps {
}
synchronized (LauncherApps.this) {
for (OnAppsChangedListener listener : mListeners) {
- listener.onPackageChanged(user, packageName);
+ listener.onPackageChanged(user, packageName); // TODO: Remove before ship
+ listener.onPackageChanged(packageName, user);
}
}
}
@@ -285,7 +387,8 @@ public class LauncherApps {
}
synchronized (LauncherApps.this) {
for (OnAppsChangedListener listener : mListeners) {
- listener.onPackageAdded(user, packageName);
+ listener.onPackageAdded(user, packageName); // TODO: Remove before ship
+ listener.onPackageAdded(packageName, user);
}
}
}
@@ -298,7 +401,8 @@ public class LauncherApps {
}
synchronized (LauncherApps.this) {
for (OnAppsChangedListener listener : mListeners) {
- listener.onPackagesAvailable(user, packageNames, replacing);
+ listener.onPackagesAvailable(user, packageNames, replacing); // TODO: Remove
+ listener.onPackagesAvailable(packageNames, user, replacing);
}
}
}
@@ -311,7 +415,8 @@ public class LauncherApps {
}
synchronized (LauncherApps.this) {
for (OnAppsChangedListener listener : mListeners) {
- listener.onPackagesUnavailable(user, packageNames, replacing);
+ listener.onPackagesUnavailable(user, packageNames, replacing); // TODO: Remove
+ listener.onPackagesUnavailable(packageNames, user, replacing);
}
}
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 6aa24e6..d4dfdd5 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -691,8 +691,12 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* with (0,0) being the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array. The weight
- * should be nonnegative.</p>
+ * bottom-right pixel in the active pixel array.</p>
+ * <p>The weight must range from 0 to 1000, and represents a weight
+ * for every pixel in the area. This means that a large metering area
+ * with the same weight as a smaller area will have more effect in
+ * the metering result. Metering areas can partially overlap and the
+ * camera device will add the weights in the overlap region.</p>
* <p>If all regions have 0 weight, then no specific metering area
* needs to be used by the camera device. If the metering region is
* outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
@@ -763,8 +767,12 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* with (0,0) being the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array. The weight
- * should be nonnegative.</p>
+ * bottom-right pixel in the active pixel array.</p>
+ * <p>The weight must range from 0 to 1000, and represents a weight
+ * for every pixel in the area. This means that a large metering area
+ * with the same weight as a smaller area will have more effect in
+ * the metering result. Metering areas can partially overlap and the
+ * camera device will add the weights in the overlap region.</p>
* <p>If all regions have 0 weight, then no specific metering area
* needs to be used by the camera device. If the metering region is
* outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
@@ -846,8 +854,12 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* with (0,0) being the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array. The weight
- * should be nonnegative.</p>
+ * bottom-right pixel in the active pixel array.</p>
+ * <p>The weight must range from 0 to 1000, and represents a weight
+ * for every pixel in the area. This means that a large metering area
+ * with the same weight as a smaller area will have more effect in
+ * the metering result. Metering areas can partially overlap and the
+ * camera device will add the weights in the overlap region.</p>
* <p>If all regions have 0 weight, then no specific metering area
* needs to be used by the camera device. If the metering region is
* outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 42020eb..7d07c92 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -537,8 +537,12 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* with (0,0) being the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array. The weight
- * should be nonnegative.</p>
+ * bottom-right pixel in the active pixel array.</p>
+ * <p>The weight must range from 0 to 1000, and represents a weight
+ * for every pixel in the area. This means that a large metering area
+ * with the same weight as a smaller area will have more effect in
+ * the metering result. Metering areas can partially overlap and the
+ * camera device will add the weights in the overlap region.</p>
* <p>If all regions have 0 weight, then no specific metering area
* needs to be used by the camera device. If the metering region is
* outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
@@ -807,8 +811,12 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* with (0,0) being the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array. The weight
- * should be nonnegative.</p>
+ * bottom-right pixel in the active pixel array.</p>
+ * <p>The weight must range from 0 to 1000, and represents a weight
+ * for every pixel in the area. This means that a large metering area
+ * with the same weight as a smaller area will have more effect in
+ * the metering result. Metering areas can partially overlap and the
+ * camera device will add the weights in the overlap region.</p>
* <p>If all regions have 0 weight, then no specific metering area
* needs to be used by the camera device. If the metering region is
* outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
@@ -1287,8 +1295,12 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* with (0,0) being the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array. The weight
- * should be nonnegative.</p>
+ * bottom-right pixel in the active pixel array.</p>
+ * <p>The weight must range from 0 to 1000, and represents a weight
+ * for every pixel in the area. This means that a large metering area
+ * with the same weight as a smaller area will have more effect in
+ * the metering result. Metering areas can partially overlap and the
+ * camera device will add the weights in the overlap region.</p>
* <p>If all regions have 0 weight, then no specific metering area
* needs to be used by the camera device. If the metering region is
* outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
@@ -1792,8 +1804,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p>If variable focus not supported, can still report
* fixed depth of field range</p>
*/
- public static final Key<android.util.Range<Float>> LENS_FOCUS_RANGE =
- new Key<android.util.Range<Float>>("android.lens.focusRange", new TypeReference<android.util.Range<Float>>() {{ }});
+ public static final Key<android.util.Pair<Float,Float>> LENS_FOCUS_RANGE =
+ new Key<android.util.Pair<Float,Float>>("android.lens.focusRange", new TypeReference<android.util.Pair<Float,Float>>() {{ }});
/**
* <p>Sets whether the camera device uses optical image stabilization (OIS)
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index dc0c652..83aee5d 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -31,6 +31,7 @@ import android.hardware.camera2.marshal.impl.MarshalQueryableColorSpaceTransform
import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
import android.hardware.camera2.marshal.impl.MarshalQueryableMeteringRectangle;
import android.hardware.camera2.marshal.impl.MarshalQueryableNativeByteToInteger;
+import android.hardware.camera2.marshal.impl.MarshalQueryablePair;
import android.hardware.camera2.marshal.impl.MarshalQueryableParcelable;
import android.hardware.camera2.marshal.impl.MarshalQueryablePrimitive;
import android.hardware.camera2.marshal.impl.MarshalQueryableRange;
@@ -1006,6 +1007,7 @@ public class CameraMetadataNative implements Parcelable {
new MarshalQueryableString(),
new MarshalQueryableReprocessFormatsMap(),
new MarshalQueryableRange(),
+ new MarshalQueryablePair(),
new MarshalQueryableMeteringRectangle(),
new MarshalQueryableColorSpaceTransform(),
new MarshalQueryableStreamConfiguration(),
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePair.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePair.java
new file mode 100644
index 0000000..0a9935d
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePair.java
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+package android.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.marshal.MarshalRegistry;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Pair;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
+
+/**
+ * Marshal {@link Pair} to/from any native type
+ */
+public class MarshalQueryablePair<T1, T2>
+ implements MarshalQueryable<Pair<T1, T2>> {
+
+ private class MarshalerPair extends Marshaler<Pair<T1, T2>> {
+ private final Class<? super Pair<T1, T2>> mClass;
+ private final Constructor<Pair<T1, T2>> mConstructor;
+ /** Marshal the {@code T1} inside of {@code Pair<T1, T2>} */
+ private final Marshaler<T1> mNestedTypeMarshalerFirst;
+ /** Marshal the {@code T1} inside of {@code Pair<T1, T2>} */
+ private final Marshaler<T2> mNestedTypeMarshalerSecond;
+
+ @SuppressWarnings("unchecked")
+ protected MarshalerPair(TypeReference<Pair<T1, T2>> typeReference,
+ int nativeType) {
+ super(MarshalQueryablePair.this, typeReference, nativeType);
+
+ mClass = typeReference.getRawType();
+
+ /*
+ * Lookup the actual type arguments, e.g. Pair<Integer, Float> --> [Integer, Float]
+ * and then get the marshalers for that managed type.
+ */
+ ParameterizedType paramType;
+ try {
+ paramType = (ParameterizedType) typeReference.getType();
+ } catch (ClassCastException e) {
+ throw new AssertionError("Raw use of Pair is not supported", e);
+ }
+
+ // Get type marshaler for T1
+ {
+ Type actualTypeArgument = paramType.getActualTypeArguments()[0];
+
+ TypeReference<?> actualTypeArgToken =
+ TypeReference.createSpecializedTypeReference(actualTypeArgument);
+
+ mNestedTypeMarshalerFirst = (Marshaler<T1>)MarshalRegistry.getMarshaler(
+ actualTypeArgToken, mNativeType);
+ }
+ // Get type marshaler for T2
+ {
+ Type actualTypeArgument = paramType.getActualTypeArguments()[1];
+
+ TypeReference<?> actualTypeArgToken =
+ TypeReference.createSpecializedTypeReference(actualTypeArgument);
+
+ mNestedTypeMarshalerSecond = (Marshaler<T2>)MarshalRegistry.getMarshaler(
+ actualTypeArgToken, mNativeType);
+ }
+ try {
+ mConstructor = (Constructor<Pair<T1, T2>>)mClass.getConstructor(
+ Object.class, Object.class);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public void marshal(Pair<T1, T2> value, ByteBuffer buffer) {
+ if (value.first == null) {
+ throw new UnsupportedOperationException("Pair#first must not be null");
+ } else if (value.second == null) {
+ throw new UnsupportedOperationException("Pair#second must not be null");
+ }
+
+ mNestedTypeMarshalerFirst.marshal(value.first, buffer);
+ mNestedTypeMarshalerSecond.marshal(value.second, buffer);
+ }
+
+ @Override
+ public Pair<T1, T2> unmarshal(ByteBuffer buffer) {
+ T1 first = mNestedTypeMarshalerFirst.unmarshal(buffer);
+ T2 second = mNestedTypeMarshalerSecond.unmarshal(buffer);
+
+ try {
+ return mConstructor.newInstance(first, second);
+ } catch (InstantiationException e) {
+ throw new AssertionError(e);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ } catch (IllegalArgumentException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public int getNativeSize() {
+ int firstSize = mNestedTypeMarshalerFirst.getNativeSize();
+ int secondSize = mNestedTypeMarshalerSecond.getNativeSize();
+
+ if (firstSize != NATIVE_SIZE_DYNAMIC && secondSize != NATIVE_SIZE_DYNAMIC) {
+ return firstSize + secondSize;
+ } else {
+ return NATIVE_SIZE_DYNAMIC;
+ }
+ }
+
+ @Override
+ public int calculateMarshalSize(Pair<T1, T2> value) {
+ int nativeSize = getNativeSize();
+
+ if (nativeSize != NATIVE_SIZE_DYNAMIC) {
+ return nativeSize;
+ } else {
+ int firstSize = mNestedTypeMarshalerFirst.calculateMarshalSize(value.first);
+ int secondSize = mNestedTypeMarshalerSecond.calculateMarshalSize(value.second);
+
+ return firstSize + secondSize;
+ }
+ }
+ }
+
+ @Override
+ public Marshaler<Pair<T1, T2>> createMarshaler(TypeReference<Pair<T1, T2>> managedType,
+ int nativeType) {
+ return new MarshalerPair(managedType, nativeType);
+ }
+
+ @Override
+ public boolean isTypeMappingSupported(TypeReference<Pair<T1, T2>> managedType, int nativeType) {
+ return (Pair.class.equals(managedType.getRawType()));
+ }
+
+}
diff --git a/core/java/android/hardware/camera2/params/MeteringRectangle.java b/core/java/android/hardware/camera2/params/MeteringRectangle.java
index a7a3b59..93fd053 100644
--- a/core/java/android/hardware/camera2/params/MeteringRectangle.java
+++ b/core/java/android/hardware/camera2/params/MeteringRectangle.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.hardware.camera2.params;
import android.util.Size;
@@ -25,22 +26,50 @@ import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.utils.HashCodeHelpers;
/**
- * An immutable class to represent a rectangle {@code (x,y, width, height)} with an
- * additional weight component.
- *
- * </p>The rectangle is defined to be inclusive of the specified coordinates.</p>
- *
- * <p>When used with a {@link CaptureRequest}, the coordinate system is based on the active pixel
+ * An immutable class to represent a rectangle {@code (x, y, width, height)} with an additional
+ * weight component.
+ * <p>
+ * The rectangle is defined to be inclusive of the specified coordinates.
+ * </p>
+ * <p>
+ * When used with a {@link CaptureRequest}, the coordinate system is based on the active pixel
* array, with {@code (0,0)} being the top-left pixel in the
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE active pixel array}, and
* {@code (android.sensor.info.activeArraySize.width - 1,
- * android.sensor.info.activeArraySize.height - 1)}
- * being the bottom-right pixel in the active pixel array.
+ * android.sensor.info.activeArraySize.height - 1)} being the bottom-right pixel in the active pixel
+ * array.
+ * </p>
+ * <p>
+ * The weight must range from {@value #METERING_WEIGHT_MIN} to {@value #METERING_WEIGHT_MAX}
+ * inclusively, and represents a weight for every pixel in the area. This means that a large
+ * metering area with the same weight as a smaller area will have more effect in the metering
+ * result. Metering areas can partially overlap and the camera device will add the weights in the
+ * overlap rectangle.
+ * </p>
+ * <p>
+ * If all rectangles have 0 weight, then no specific metering area needs to be used by the camera
+ * device. If the metering rectangle is outside the used android.scaler.cropRegion returned in
+ * capture result metadata, the camera device will ignore the sections outside the rectangle and
+ * output the used sections in the result metadata.
* </p>
- *
- * <p>The metering weight is nonnegative.</p>
*/
public final class MeteringRectangle {
+ /**
+ * The minimum value of valid metering weight.
+ */
+ public static final int METERING_WEIGHT_MIN = 0;
+
+ /**
+ * The maximum value of valid metering weight.
+ */
+ public static final int METERING_WEIGHT_MAX = 1000;
+
+ /**
+ * Weights set to this value will cause the camera device to ignore this rectangle.
+ * If all metering rectangles are weighed with 0, the camera device will choose its own metering
+ * rectangles.
+ */
+ public static final int METERING_WEIGHT_DONT_CARE = 0;
private final int mX;
private final int mY;
@@ -55,8 +84,8 @@ public final class MeteringRectangle {
* @param y coordinate >= 0
* @param width width >= 0
* @param height height >= 0
- * @param meteringWeight weight >= 0
- *
+ * @param meteringWeight weight between {@value #METERING_WEIGHT_MIN} and
+ * {@value #METERING_WEIGHT_MAX} inclusively
* @throws IllegalArgumentException if any of the parameters were negative
*/
public MeteringRectangle(int x, int y, int width, int height, int meteringWeight) {
@@ -64,7 +93,8 @@ public final class MeteringRectangle {
mY = checkArgumentNonnegative(y, "y must be nonnegative");
mWidth = checkArgumentNonnegative(width, "width must be nonnegative");
mHeight = checkArgumentNonnegative(height, "height must be nonnegative");
- mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
+ mWeight = checkArgumentInRange(
+ meteringWeight, METERING_WEIGHT_MIN, METERING_WEIGHT_MAX, "meteringWeight");
}
/**
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index a71a74d..723eda1 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -194,6 +194,8 @@ public final class HdmiCec {
DEVICE_RECORDER, // ADDR_RECORDER_3
DEVICE_TUNER, // ADDR_TUNER_4
DEVICE_PLAYBACK, // ADDR_PLAYBACK_3
+ DEVICE_RESERVED,
+ DEVICE_RESERVED,
DEVICE_TV, // ADDR_SPECIFIC_USE
};
@@ -210,6 +212,8 @@ public final class HdmiCec {
"Recorder_3",
"Tuner_4",
"Playback_3",
+ "Reserved_1",
+ "Reserved_2",
"Secondary_TV",
};
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 06d8e4a..857e335 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -69,6 +69,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
final WeakReference<AbstractInputMethodService> mTarget;
+ final Context mContext;
final HandlerCaller mCaller;
final WeakReference<InputMethod> mInputMethod;
final int mTargetSdkVersion;
@@ -111,8 +112,8 @@ class IInputMethodWrapper extends IInputMethod.Stub
public IInputMethodWrapper(AbstractInputMethodService context,
InputMethod inputMethod) {
mTarget = new WeakReference<AbstractInputMethodService>(context);
- mCaller = new HandlerCaller(context.getApplicationContext(), null,
- this, true /*asyncHandler*/);
+ mContext = context.getApplicationContext();
+ mCaller = new HandlerCaller(mContext, null, this, true /*asyncHandler*/);
mInputMethod = new WeakReference<InputMethod>(inputMethod);
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
}
@@ -186,7 +187,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
- mCaller.mContext, (InputChannel)args.arg1,
+ mContext, (InputChannel)args.arg1,
(IInputSessionCallback)args.arg2));
args.recycle();
return;
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 38a65c5..795117e 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -115,6 +115,10 @@ public class SoftInputWindow extends Dialog {
getWindow().setAttributes(lp);
}
+ public int getGravity() {
+ return getWindow().getAttributes().gravity;
+ }
+
private void updateWidthHeight(WindowManager.LayoutParams lp) {
if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) {
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 2f2aba3..a48a388 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -40,6 +40,7 @@ import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.Protocol;
import java.net.InetAddress;
@@ -807,11 +808,34 @@ public class ConnectivityManager {
* @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
*/
public int startUsingNetworkFeature(int networkType, String feature) {
- try {
- return mService.startUsingNetworkFeature(networkType, feature,
- new Binder());
- } catch (RemoteException e) {
- return -1;
+ NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
+ if (netCap == null) {
+ Log.d(TAG, "Can't satisfy startUsingNetworkFeature for " + networkType + ", " +
+ feature);
+ return PhoneConstants.APN_REQUEST_FAILED;
+ }
+
+ NetworkRequest request = null;
+ synchronized (sLegacyRequests) {
+ LegacyRequest l = sLegacyRequests.get(netCap);
+ if (l != null) {
+ Log.d(TAG, "renewing startUsingNetworkFeature request " + l.networkRequest);
+ renewRequestLocked(l);
+ if (l.currentNetwork != null) {
+ return PhoneConstants.APN_ALREADY_ACTIVE;
+ } else {
+ return PhoneConstants.APN_REQUEST_STARTED;
+ }
+ }
+
+ request = requestNetworkForFeatureLocked(netCap);
+ }
+ if (request != null) {
+ Log.d(TAG, "starting startUsingNeworkFeature for request " + request);
+ return PhoneConstants.APN_REQUEST_STARTED;
+ } else {
+ Log.d(TAG, " request Failed");
+ return PhoneConstants.APN_REQUEST_FAILED;
}
}
@@ -831,11 +855,172 @@ public class ConnectivityManager {
* @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
*/
public int stopUsingNetworkFeature(int networkType, String feature) {
- try {
- return mService.stopUsingNetworkFeature(networkType, feature);
- } catch (RemoteException e) {
+ NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
+ if (netCap == null) {
+ Log.d(TAG, "Can't satisfy stopUsingNetworkFeature for " + networkType + ", " +
+ feature);
return -1;
}
+
+ NetworkRequest request = removeRequestForFeature(netCap);
+ if (request != null) {
+ Log.d(TAG, "stopUsingNetworkFeature for " + networkType + ", " + feature);
+ releaseNetworkRequest(request);
+ }
+ return 1;
+ }
+
+ private NetworkCapabilities networkCapabilitiesForFeature(int networkType, String feature) {
+ if (networkType == TYPE_MOBILE) {
+ int cap = -1;
+ if ("enableMMS".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_MMS;
+ } else if ("enableSUPL".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_SUPL;
+ } else if ("enableDUN".equals(feature) || "enableDUNAlways".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_DUN;
+ } else if ("enableHIPRI".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_INTERNET;
+ } else if ("enableFOTA".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_FOTA;
+ } else if ("enableIMS".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_IMS;
+ } else if ("enableCBS".equals(feature)) {
+ cap = NetworkCapabilities.NET_CAPABILITY_CBS;
+ } else {
+ return null;
+ }
+ NetworkCapabilities netCap = new NetworkCapabilities();
+ netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ netCap.addNetworkCapability(cap);
+ return netCap;
+ } else if (networkType == TYPE_WIFI) {
+ if ("p2p".equals(feature)) {
+ NetworkCapabilities netCap = new NetworkCapabilities();
+ netCap.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P);
+ return netCap;
+ }
+ }
+ return null;
+ }
+
+ private int legacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
+ if (netCap == null) return TYPE_NONE;
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
+ return TYPE_MOBILE_CBS;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
+ return TYPE_MOBILE_IMS;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
+ return TYPE_MOBILE_FOTA;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
+ return TYPE_MOBILE_DUN;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
+ return TYPE_MOBILE_SUPL;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
+ return TYPE_MOBILE_MMS;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ return TYPE_MOBILE_HIPRI;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P)) {
+ return TYPE_WIFI_P2P;
+ }
+ return TYPE_NONE;
+ }
+
+ private static class LegacyRequest {
+ NetworkCapabilities networkCapabilities;
+ NetworkRequest networkRequest;
+ int expireSequenceNumber;
+ Network currentNetwork;
+ int delay = -1;
+ NetworkCallbackListener networkCallbackListener = new NetworkCallbackListener() {
+ @Override
+ public void onAvailable(NetworkRequest request, Network network) {
+ currentNetwork = network;
+ Log.d(TAG, "startUsingNetworkFeature got Network:" + network);
+ network.bindProcessForHostResolution();
+ }
+ @Override
+ public void onLost(NetworkRequest request, Network network) {
+ if (network.equals(currentNetwork)) {
+ currentNetwork = null;
+ network.unbindProcessForHostResolution();
+ }
+ Log.d(TAG, "startUsingNetworkFeature lost Network:" + network);
+ }
+ };
+ }
+
+ private HashMap<NetworkCapabilities, LegacyRequest> sLegacyRequests =
+ new HashMap<NetworkCapabilities, LegacyRequest>();
+
+ private NetworkRequest findRequestForFeature(NetworkCapabilities netCap) {
+ synchronized (sLegacyRequests) {
+ LegacyRequest l = sLegacyRequests.get(netCap);
+ if (l != null) return l.networkRequest;
+ }
+ return null;
+ }
+
+ private void renewRequestLocked(LegacyRequest l) {
+ l.expireSequenceNumber++;
+ Log.d(TAG, "renewing request to seqNum " + l.expireSequenceNumber);
+ sendExpireMsgForFeature(l.networkCapabilities, l.expireSequenceNumber, l.delay);
+ }
+
+ private void expireRequest(NetworkCapabilities netCap, int sequenceNum) {
+ int ourSeqNum = -1;
+ synchronized (sLegacyRequests) {
+ LegacyRequest l = sLegacyRequests.get(netCap);
+ if (l == null) return;
+ ourSeqNum = l.expireSequenceNumber;
+ if (l.expireSequenceNumber == sequenceNum) {
+ releaseNetworkRequest(l.networkRequest);
+ sLegacyRequests.remove(netCap);
+ }
+ }
+ Log.d(TAG, "expireRequest with " + ourSeqNum + ", " + sequenceNum);
+ }
+
+ private NetworkRequest requestNetworkForFeatureLocked(NetworkCapabilities netCap) {
+ int delay = -1;
+ int type = legacyTypeForNetworkCapabilities(netCap);
+ try {
+ delay = mService.getRestoreDefaultNetworkDelay(type);
+ } catch (RemoteException e) {}
+ LegacyRequest l = new LegacyRequest();
+ l.networkCapabilities = netCap;
+ l.delay = delay;
+ l.expireSequenceNumber = 0;
+ l.networkRequest = sendRequestForNetwork(netCap, l.networkCallbackListener, 0,
+ REQUEST, type);
+ if (l.networkRequest == null) return null;
+ sLegacyRequests.put(netCap, l);
+ sendExpireMsgForFeature(netCap, l.expireSequenceNumber, delay);
+ return l.networkRequest;
+ }
+
+ private void sendExpireMsgForFeature(NetworkCapabilities netCap, int seqNum, int delay) {
+ if (delay >= 0) {
+ Log.d(TAG, "sending expire msg with seqNum " + seqNum + " and delay " + delay);
+ Message msg = sCallbackHandler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap);
+ sCallbackHandler.sendMessageDelayed(msg, delay);
+ }
+ }
+
+ private NetworkRequest removeRequestForFeature(NetworkCapabilities netCap) {
+ synchronized (sLegacyRequests) {
+ LegacyRequest l = sLegacyRequests.remove(netCap);
+ if (l == null) return null;
+ return l.networkRequest;
+ }
}
/**
@@ -1782,8 +1967,10 @@ public class ConnectivityManager {
public static final int CALLBACK_RELEASED = BASE + 8;
/** @hide */
public static final int CALLBACK_EXIT = BASE + 9;
+ /** @hide obj = NetworkCapabilities, arg1 = seq number */
+ private static final int EXPIRE_LEGACY_REQUEST = BASE + 10;
- private static class CallbackHandler extends Handler {
+ private class CallbackHandler extends Handler {
private final HashMap<NetworkRequest, NetworkCallbackListener>mCallbackMap;
private final AtomicInteger mRefCount;
private static final String TAG = "ConnectivityManager.CallbackHandler";
@@ -1903,6 +2090,10 @@ public class ConnectivityManager {
getLooper().quit();
break;
}
+ case EXPIRE_LEGACY_REQUEST: {
+ expireRequest((NetworkCapabilities)message.obj, message.arg1);
+ break;
+ }
}
}
@@ -1954,8 +2145,9 @@ public class ConnectivityManager {
private final static int LISTEN = 1;
private final static int REQUEST = 2;
- private NetworkRequest somethingForNetwork(NetworkCapabilities need,
- NetworkCallbackListener networkCallbackListener, int timeoutSec, int action) {
+ private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
+ NetworkCallbackListener networkCallbackListener, int timeoutSec, int action,
+ int legacyType) {
NetworkRequest networkRequest = null;
if (networkCallbackListener == null) {
throw new IllegalArgumentException("null NetworkCallbackListener");
@@ -1968,7 +2160,7 @@ public class ConnectivityManager {
new Binder());
} else {
networkRequest = mService.requestNetwork(need, new Messenger(sCallbackHandler),
- timeoutSec, new Binder());
+ timeoutSec, new Binder(), legacyType);
}
if (networkRequest != null) {
synchronized(sNetworkCallbackListener) {
@@ -1998,7 +2190,7 @@ public class ConnectivityManager {
*/
public NetworkRequest requestNetwork(NetworkCapabilities need,
NetworkCallbackListener networkCallbackListener) {
- return somethingForNetwork(need, networkCallbackListener, 0, REQUEST);
+ return sendRequestForNetwork(need, networkCallbackListener, 0, REQUEST, TYPE_NONE);
}
/**
@@ -2021,7 +2213,8 @@ public class ConnectivityManager {
*/
public NetworkRequest requestNetwork(NetworkCapabilities need,
NetworkCallbackListener networkCallbackListener, int timeoutSec) {
- return somethingForNetwork(need, networkCallbackListener, timeoutSec, REQUEST);
+ return sendRequestForNetwork(need, networkCallbackListener, timeoutSec, REQUEST,
+ TYPE_NONE);
}
/**
@@ -2099,7 +2292,7 @@ public class ConnectivityManager {
*/
public NetworkRequest listenForNetwork(NetworkCapabilities need,
NetworkCallbackListener networkCallbackListener) {
- return somethingForNetwork(need, networkCallbackListener, 0, LISTEN);
+ return sendRequestForNetwork(need, networkCallbackListener, 0, LISTEN, TYPE_NONE);
}
/**
diff --git a/core/java/android/net/ConnectivityServiceProtocol.java b/core/java/android/net/ConnectivityServiceProtocol.java
deleted file mode 100644
index 74096b4..0000000
--- a/core/java/android/net/ConnectivityServiceProtocol.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-
-package android.net;
-
-import static com.android.internal.util.Protocol.BASE_CONNECTIVITY_SERVICE;
-
-/**
- * Describes the Internal protocols used to communicate with ConnectivityService.
- * @hide
- */
-public class ConnectivityServiceProtocol {
-
- private static final int BASE = BASE_CONNECTIVITY_SERVICE;
-
- private ConnectivityServiceProtocol() {}
-
- /**
- * This is a contract between ConnectivityService and various bearers.
- * A NetworkFactory is an abstract entity that creates NetworkAgent objects.
- * The bearers register with ConnectivityService using
- * ConnectivityManager.registerNetworkFactory, where they pass in a Messenger
- * to be used to deliver the following Messages.
- */
- public static class NetworkFactoryProtocol {
- private NetworkFactoryProtocol() {}
- /**
- * Pass a network request to the bearer. If the bearer believes it can
- * satisfy the request it should connect to the network and create a
- * NetworkAgent. Once the NetworkAgent is fully functional it will
- * register itself with ConnectivityService using registerNetworkAgent.
- * If the bearer cannot immediately satisfy the request (no network,
- * user disabled the radio, lower-scored network) it should remember
- * any NetworkRequests it may be able to satisfy in the future. It may
- * disregard any that it will never be able to service, for example
- * those requiring a different bearer.
- * msg.obj = NetworkRequest
- * msg.arg1 = score - the score of the any network currently satisfying this
- * request. If this bearer knows in advance it cannot
- * exceed this score it should not try to connect, holding the request
- * for the future.
- * Note that subsequent events may give a different (lower
- * or higher) score for this request, transmitted to each
- * NetworkFactory through additional CMD_REQUEST_NETWORK msgs
- * with the same NetworkRequest but an updated score.
- * Also, network conditions may change for this bearer
- * allowing for a better score in the future.
- */
- public static final int CMD_REQUEST_NETWORK = BASE;
-
- /**
- * Cancel a network request
- * msg.obj = NetworkRequest
- */
- public static final int CMD_CANCEL_REQUEST = BASE + 1;
- }
-}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index baec36a..5f1ff3e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -158,7 +158,7 @@ interface IConnectivityManager
in NetworkCapabilities nc, int score);
NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
- in Messenger messenger, int timeoutSec, in IBinder binder);
+ in Messenger messenger, int timeoutSec, in IBinder binder, int legacy);
NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
in PendingIntent operation);
@@ -170,4 +170,6 @@ interface IConnectivityManager
in PendingIntent operation);
void releaseNetworkRequest(in NetworkRequest networkRequest);
+
+ int getRestoreDefaultNetworkDelay(int networkType);
}
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 1c18ba5..7e8b1f1 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -24,85 +24,39 @@ import android.os.Messenger;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
-import android.util.SparseArray;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
+import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * A Utility class for handling NetworkRequests.
- *
- * Created by bearer-specific code to handle tracking requests, scores,
- * network data and handle communicating with ConnectivityService. Two
- * abstract methods: connect and disconnect are used to act on the
- * underlying bearer code. Connect is called when we have a NetworkRequest
- * and our score is better than the current handling network's score, while
- * disconnect is used when ConnectivityService requests a disconnect.
+ * A Utility class for handling for communicating between bearer-specific
+ * code and ConnectivityService.
*
* A bearer may have more than one NetworkAgent if it can simultaneously
* support separate networks (IMS / Internet / MMS Apns on cellular, or
- * perhaps connections with different SSID or P2P for Wi-Fi). The bearer
- * code should pass its NetworkAgents the NetworkRequests each NetworkAgent
- * can handle, demultiplexing for different network types. The bearer code
- * can also filter out requests it can never handle.
+ * perhaps connections with different SSID or P2P for Wi-Fi).
*
- * Each NetworkAgent needs to be given a score and NetworkCapabilities for
- * their potential network. While disconnected, the NetworkAgent will check
- * each time its score changes or a NetworkRequest changes to see if
- * the NetworkAgent can provide a higher scored network for a NetworkRequest
- * that the NetworkAgent's NetworkCapabilties can satisfy. This condition will
- * trigger a connect request via connect(). After connection, connection data
- * should be given to the NetworkAgent by the bearer, including LinkProperties
- * NetworkCapabilties and NetworkInfo. After that the NetworkAgent will register
- * with ConnectivityService and forward the data on.
* @hide
*/
public abstract class NetworkAgent extends Handler {
- private final SparseArray<NetworkRequestAndScore> mNetworkRequests = new SparseArray<>();
- private boolean mConnectionRequested = false;
-
- private AsyncChannel mAsyncChannel;
+ private volatile AsyncChannel mAsyncChannel;
private final String LOG_TAG;
private static final boolean DBG = true;
private static final boolean VDBG = true;
- // TODO - this class shouldn't cache data or it runs the risk of getting out of sync
- // Make the API require each of these when any is updated so we have the data we need,
- // without caching.
- private LinkProperties mLinkProperties;
- private NetworkInfo mNetworkInfo;
- private NetworkCapabilities mNetworkCapabilities;
- private int mNetworkScore;
- private boolean mRegistered = false;
private final Context mContext;
- private AtomicBoolean mHasRequests = new AtomicBoolean(false);
-
- // TODO - add a name member for logging purposes.
-
- protected final Object mLockObj = new Object();
-
+ private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
private static final int BASE = Protocol.BASE_NETWORK_AGENT;
/**
- * Sent by self to queue up a new/modified request.
- * obj = NetworkRequestAndScore
- */
- private static final int CMD_ADD_REQUEST = BASE + 1;
-
- /**
- * Sent by self to queue up the removal of a request.
- * obj = NetworkRequest
- */
- private static final int CMD_REMOVE_REQUEST = BASE + 2;
-
- /**
* Sent by ConnectivityService to the NetworkAgent to inform it of
* suspected connectivity problems on its network. The NetworkAgent
* should take steps to verify and correct connectivity.
*/
- public static final int CMD_SUSPECT_BAD = BASE + 3;
+ public static final int CMD_SUSPECT_BAD = BASE;
/**
* Sent by the NetworkAgent (note the EVENT vs CMD prefix) to
@@ -110,84 +64,63 @@ public abstract class NetworkAgent extends Handler {
* Sent when the NetworkInfo changes, mainly due to change of state.
* obj = NetworkInfo
*/
- public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 4;
+ public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1;
/**
* Sent by the NetworkAgent to ConnectivityService to pass the current
* NetworkCapabilties.
* obj = NetworkCapabilities
*/
- public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 5;
+ public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2;
/**
* Sent by the NetworkAgent to ConnectivityService to pass the current
* NetworkProperties.
* obj = NetworkProperties
*/
- public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 6;
+ public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;
/**
* Sent by the NetworkAgent to ConnectivityService to pass the current
* network score.
- * arg1 = network score int
+ * obj = network score Integer
*/
- public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 7;
+ public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
- public NetworkAgent(Looper looper, Context context, String logTag) {
+ public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
+ NetworkCapabilities nc, LinkProperties lp, int score) {
super(looper);
LOG_TAG = logTag;
mContext = context;
- }
-
- /**
- * When conditions are right, register with ConnectivityService.
- * Connditions include having a well defined network and a request
- * that justifies it. The NetworkAgent will remain registered until
- * disconnected.
- * TODO - this should have all data passed in rather than caching
- */
- private void registerSelf() {
- synchronized(mLockObj) {
- if (!mRegistered && mConnectionRequested &&
- mNetworkInfo != null && mNetworkInfo.isConnected() &&
- mNetworkCapabilities != null &&
- mLinkProperties != null &&
- mNetworkScore != 0) {
- if (DBG) log("Registering NetworkAgent");
- mRegistered = true;
- ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(mNetworkInfo),
- new LinkProperties(mLinkProperties),
- new NetworkCapabilities(mNetworkCapabilities), mNetworkScore);
- } else if (DBG && !mRegistered) {
- String err = "Not registering due to ";
- if (mConnectionRequested == false) err += "no Connect requested ";
- if (mNetworkInfo == null) err += "null NetworkInfo ";
- if (mNetworkInfo != null && mNetworkInfo.isConnected() == false) {
- err += "NetworkInfo disconnected ";
- }
- if (mLinkProperties == null) err += "null LinkProperties ";
- if (mNetworkCapabilities == null) err += "null NetworkCapabilities ";
- if (mNetworkScore == 0) err += "null NetworkScore";
- log(err);
- }
+ if (ni == null || nc == null || lp == null) {
+ throw new IllegalArgumentException();
}
+
+ if (DBG) log("Registering NetworkAgent");
+ ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
+ new LinkProperties(lp), new NetworkCapabilities(nc), score);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
- synchronized (mLockObj) {
- if (mAsyncChannel != null) {
- log("Received new connection while already connected!");
- } else {
- if (DBG) log("NetworkAgent fully connected");
- mAsyncChannel = new AsyncChannel();
- mAsyncChannel.connected(null, this, msg.replyTo);
- mAsyncChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
- AsyncChannel.STATUS_SUCCESSFUL);
+ if (mAsyncChannel != null) {
+ log("Received new connection while already connected!");
+ } else {
+ if (DBG) log("NetworkAgent fully connected");
+ AsyncChannel ac = new AsyncChannel();
+ ac.connected(null, this, msg.replyTo);
+ ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_SUCCESSFUL);
+ synchronized (mPreConnectedQueue) {
+ mAsyncChannel = ac;
+ for (Message m : mPreConnectedQueue) {
+ ac.sendMessage(m);
+ }
+ mPreConnectedQueue.clear();
}
}
break;
@@ -199,213 +132,69 @@ public abstract class NetworkAgent extends Handler {
}
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
if (DBG) log("NetworkAgent channel lost");
- disconnect();
- clear();
+ // let the client know CS is done with us.
+ unwanted();
+ synchronized (mPreConnectedQueue) {
+ mAsyncChannel = null;
+ }
break;
}
case CMD_SUSPECT_BAD: {
log("Unhandled Message " + msg);
break;
}
- case CMD_ADD_REQUEST: {
- handleAddRequest(msg);
- break;
- }
- case CMD_REMOVE_REQUEST: {
- handleRemoveRequest(msg);
- break;
- }
- }
- }
-
- private void clear() {
- synchronized(mLockObj) {
- mNetworkRequests.clear();
- mHasRequests.set(false);
- mConnectionRequested = false;
- mAsyncChannel = null;
- mRegistered = false;
- }
- }
-
- private static class NetworkRequestAndScore {
- NetworkRequest req;
- int score;
-
- NetworkRequestAndScore(NetworkRequest networkRequest, int score) {
- req = networkRequest;
- this.score = score;
}
}
- private void handleAddRequest(Message msg) {
- NetworkRequestAndScore n = (NetworkRequestAndScore)msg.obj;
- // replaces old request, updating score
- mNetworkRequests.put(n.req.requestId, n);
- mHasRequests.set(true);
- evalScores();
- }
-
- private void handleRemoveRequest(Message msg) {
- NetworkRequest networkRequest = (NetworkRequest)msg.obj;
-
- if (mNetworkRequests.get(networkRequest.requestId) != null) {
- mNetworkRequests.remove(networkRequest.requestId);
- if (mNetworkRequests.size() == 0) mHasRequests.set(false);
- evalScores();
- }
- }
-
- /**
- * Called to go through our list of requests and see if we're
- * good enough to try connecting, or if we have gotten worse and
- * need to disconnect.
- *
- * Once we are registered, does nothing: we disconnect when requested via
- * CMD_CHANNEL_DISCONNECTED, generated by either a loss of connection
- * between modules (bearer or ConnectivityService dies) or more commonly
- * when the NetworkInfo reports to ConnectivityService it is disconnected.
- */
- private void evalScores() {
- synchronized(mLockObj) {
- if (mRegistered) {
- if (VDBG) log("evalScores - already connected - size=" + mNetworkRequests.size());
- // already trying
- return;
- }
- if (VDBG) log("evalScores!");
- for (int i=0; i < mNetworkRequests.size(); i++) {
- int score = mNetworkRequests.valueAt(i).score;
- if (VDBG) log(" checking request Min " + score + " vs my score " + mNetworkScore);
- if (score < mNetworkScore) {
- // have a request that has a lower scored network servicing it
- // (or no network) than we could provide, so let's connect!
- mConnectionRequested = true;
- connect();
- return;
- }
- }
- // Our score is not high enough to satisfy any current request.
- // This can happen if our score goes down after a connection is
- // requested but before we actually connect. In this case, disconnect
- // rather than continue trying - there's no point connecting if we know
- // we'll just be torn down as soon as we do.
- if (mConnectionRequested) {
- mConnectionRequested = false;
- disconnect();
+ private void queueOrSendMessage(int what, Object obj) {
+ synchronized (mPreConnectedQueue) {
+ if (mAsyncChannel != null) {
+ mAsyncChannel.sendMessage(what, obj);
+ } else {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.obj = obj;
+ mPreConnectedQueue.add(msg);
}
}
}
- public void addNetworkRequest(NetworkRequest networkRequest, int score) {
- if (DBG) log("adding NetworkRequest " + networkRequest + " with score " + score);
- sendMessage(obtainMessage(CMD_ADD_REQUEST,
- new NetworkRequestAndScore(networkRequest, score)));
- }
-
- public void removeNetworkRequest(NetworkRequest networkRequest) {
- if (DBG) log("removing NetworkRequest " + networkRequest);
- sendMessage(obtainMessage(CMD_REMOVE_REQUEST, networkRequest));
- }
-
/**
* Called by the bearer code when it has new LinkProperties data.
- * If we're a registered NetworkAgent, this new data will get forwarded on,
- * otherwise we store a copy in anticipation of registering. This call
- * may also prompt registration if it causes the NetworkAgent to meet
- * the conditions (fully configured, connected, satisfys a request and
- * has sufficient score).
*/
public void sendLinkProperties(LinkProperties linkProperties) {
- linkProperties = new LinkProperties(linkProperties);
- synchronized(mLockObj) {
- mLinkProperties = linkProperties;
- if (mAsyncChannel != null) {
- mAsyncChannel.sendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, linkProperties);
- } else {
- registerSelf();
- }
- }
+ queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
}
/**
* Called by the bearer code when it has new NetworkInfo data.
- * If we're a registered NetworkAgent, this new data will get forwarded on,
- * otherwise we store a copy in anticipation of registering. This call
- * may also prompt registration if it causes the NetworkAgent to meet
- * the conditions (fully configured, connected, satisfys a request and
- * has sufficient score).
*/
public void sendNetworkInfo(NetworkInfo networkInfo) {
- networkInfo = new NetworkInfo(networkInfo);
- synchronized(mLockObj) {
- mNetworkInfo = networkInfo;
- if (mAsyncChannel != null) {
- mAsyncChannel.sendMessage(EVENT_NETWORK_INFO_CHANGED, networkInfo);
- } else {
- registerSelf();
- }
- }
+ queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
}
/**
* Called by the bearer code when it has new NetworkCapabilities data.
- * If we're a registered NetworkAgent, this new data will get forwarded on,
- * otherwise we store a copy in anticipation of registering. This call
- * may also prompt registration if it causes the NetworkAgent to meet
- * the conditions (fully configured, connected, satisfys a request and
- * has sufficient score).
- * Note that if these capabilities make the network non-useful,
- * ConnectivityServce will tear this network down.
*/
public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
- networkCapabilities = new NetworkCapabilities(networkCapabilities);
- synchronized(mLockObj) {
- mNetworkCapabilities = networkCapabilities;
- if (mAsyncChannel != null) {
- mAsyncChannel.sendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, networkCapabilities);
- } else {
- registerSelf();
- }
- }
- }
-
- public NetworkCapabilities getNetworkCapabilities() {
- synchronized(mLockObj) {
- return new NetworkCapabilities(mNetworkCapabilities);
- }
+ queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
+ new NetworkCapabilities(networkCapabilities));
}
/**
* Called by the bearer code when it has a new score for this network.
- * If we're a registered NetworkAgent, this new data will get forwarded on,
- * otherwise we store a copy.
*/
- public synchronized void sendNetworkScore(int score) {
- synchronized(mLockObj) {
- mNetworkScore = score;
- evalScores();
- if (mAsyncChannel != null) {
- mAsyncChannel.sendMessage(EVENT_NETWORK_SCORE_CHANGED, mNetworkScore);
- } else {
- registerSelf();
- }
- }
- }
-
- public boolean hasRequests() {
- return mHasRequests.get();
+ public void sendNetworkScore(int score) {
+ queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new Integer(score));
}
- public boolean isConnectionRequested() {
- synchronized(mLockObj) {
- return mConnectionRequested;
- }
- }
-
-
- abstract protected void connect();
- abstract protected void disconnect();
+ /**
+ * Called when ConnectivityService has indicated they no longer want this network.
+ * The parent factory should (previously) have received indication of the change
+ * as well, either canceling NetworkRequests or altering their score such that this
+ * network won't be immediately requested again.
+ */
+ abstract protected void unwanted();
protected void log(String s) {
Log.d(LOG_TAG, "NetworkAgent: " + s);
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
new file mode 100644
index 0000000..a20e8e7
--- /dev/null
+++ b/core/java/android/net/NetworkFactory.java
@@ -0,0 +1,275 @@
+/*
+ * 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.
+ */
+
+package android.net;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+/**
+ * A NetworkFactory is an entity that creates NetworkAgent objects.
+ * The bearers register with ConnectivityService using {@link #register} and
+ * their factory will start receiving scored NetworkRequests. NetworkRequests
+ * can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by
+ * overridden function. All of these can be dynamic - changing NetworkCapabilities
+ * or score forces re-evaluation of all current requests.
+ *
+ * If any requests pass the filter some overrideable functions will be called.
+ * If the bearer only cares about very simple start/stopNetwork callbacks, those
+ * functions can be overridden. If the bearer needs more interaction, it can
+ * override addNetworkRequest and removeNetworkRequest which will give it each
+ * request that passes their current filters.
+ * @hide
+ **/
+public class NetworkFactory extends Handler {
+ private static final boolean DBG = true;
+
+ private static final int BASE = Protocol.BASE_NETWORK_FACTORY;
+ /**
+ * Pass a network request to the bearer. If the bearer believes it can
+ * satisfy the request it should connect to the network and create a
+ * NetworkAgent. Once the NetworkAgent is fully functional it will
+ * register itself with ConnectivityService using registerNetworkAgent.
+ * If the bearer cannot immediately satisfy the request (no network,
+ * user disabled the radio, lower-scored network) it should remember
+ * any NetworkRequests it may be able to satisfy in the future. It may
+ * disregard any that it will never be able to service, for example
+ * those requiring a different bearer.
+ * msg.obj = NetworkRequest
+ * msg.arg1 = score - the score of the any network currently satisfying this
+ * request. If this bearer knows in advance it cannot
+ * exceed this score it should not try to connect, holding the request
+ * for the future.
+ * Note that subsequent events may give a different (lower
+ * or higher) score for this request, transmitted to each
+ * NetworkFactory through additional CMD_REQUEST_NETWORK msgs
+ * with the same NetworkRequest but an updated score.
+ * Also, network conditions may change for this bearer
+ * allowing for a better score in the future.
+ */
+ public static final int CMD_REQUEST_NETWORK = BASE;
+
+ /**
+ * Cancel a network request
+ * msg.obj = NetworkRequest
+ */
+ public static final int CMD_CANCEL_REQUEST = BASE + 1;
+
+ /**
+ * Internally used to set our best-guess score.
+ * msg.arg1 = new score
+ */
+ private static final int CMD_SET_SCORE = BASE + 2;
+
+ /**
+ * Internally used to set our current filter for coarse bandwidth changes with
+ * technology changes.
+ * msg.obj = new filter
+ */
+ private static final int CMD_SET_FILTER = BASE + 3;
+
+ private final Context mContext;
+ private final String LOG_TAG;
+
+ private final SparseArray<NetworkRequestInfo> mNetworkRequests =
+ new SparseArray<NetworkRequestInfo>();
+
+ private int mScore;
+ private NetworkCapabilities mCapabilityFilter;
+
+ private int mRefCount = 0;
+ private Messenger mMessenger = null;
+
+ public NetworkFactory(Looper looper, Context context, String logTag,
+ NetworkCapabilities filter) {
+ super(looper);
+ LOG_TAG = logTag;
+ mContext = context;
+ mCapabilityFilter = filter;
+ }
+
+ public void register() {
+ if (DBG) log("Registering NetworkFactory");
+ if (mMessenger == null) {
+ mMessenger = new Messenger(this);
+ ConnectivityManager.from(mContext).registerNetworkFactory(mMessenger, LOG_TAG);
+ }
+ }
+
+ public void unregister() {
+ if (DBG) log("Unregistering NetworkFactory");
+ if (mMessenger != null) {
+ ConnectivityManager.from(mContext).unregisterNetworkFactory(mMessenger);
+ mMessenger = null;
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case CMD_REQUEST_NETWORK: {
+ handleAddRequest((NetworkRequest)msg.obj, msg.arg1);
+ break;
+ }
+ case CMD_CANCEL_REQUEST: {
+ handleRemoveRequest((NetworkRequest) msg.obj);
+ break;
+ }
+ case CMD_SET_SCORE: {
+ handleSetScore(msg.arg1);
+ break;
+ }
+ case CMD_SET_FILTER: {
+ handleSetFilter((NetworkCapabilities) msg.obj);
+ break;
+ }
+ }
+ }
+
+ private class NetworkRequestInfo {
+ public final NetworkRequest request;
+ public int score;
+ public boolean requested; // do we have a request outstanding, limited by score
+
+ public NetworkRequestInfo(NetworkRequest request, int score) {
+ this.request = request;
+ this.score = score;
+ this.requested = false;
+ }
+ }
+
+ private void handleAddRequest(NetworkRequest request, int score) {
+ NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
+ if (n == null) {
+ n = new NetworkRequestInfo(request, score);
+ mNetworkRequests.put(n.request.requestId, n);
+ } else {
+ n.score = score;
+ }
+ if (DBG) log("got request " + request + " with score " + score);
+ if (DBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter);
+
+ evalRequest(n);
+ }
+
+ private void handleRemoveRequest(NetworkRequest request) {
+ NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
+ if (n != null && n.requested) {
+ mNetworkRequests.remove(request.requestId);
+ releaseNetworkFor(n.request);
+ }
+ }
+
+ private void handleSetScore(int score) {
+ mScore = score;
+ evalRequests();
+ }
+
+ private void handleSetFilter(NetworkCapabilities netCap) {
+ mCapabilityFilter = netCap;
+ evalRequests();
+ }
+
+ /**
+ * Overridable function to provide complex filtering.
+ * Called for every request every time a new NetworkRequest is seen
+ * and whenever the filterScore or filterNetworkCapabilities change.
+ *
+ * acceptRequest can be overriden to provide complex filter behavior
+ * for the incoming requests
+ *
+ * For output, this class will call {@link #needNetworkFor} and
+ * {@link #releaseNetworkFor} for every request that passes the filters.
+ * If you don't need to see every request, you can leave the base
+ * implementations of those two functions and instead override
+ * {@link #startNetwork} and {@link #stopNetwork}.
+ *
+ * If you want to see every score fluctuation on every request, set
+ * your score filter to a very high number and watch {@link #needNetworkFor}.
+ *
+ * @return {@code true} to accept the request.
+ */
+ public boolean acceptRequest(NetworkRequest request, int score) {
+ return true;
+ }
+
+ private void evalRequest(NetworkRequestInfo n) {
+ if (n.requested == false && n.score < mScore &&
+ n.request.networkCapabilities.satisfiedByNetworkCapabilities(
+ mCapabilityFilter) && acceptRequest(n.request, n.score)) {
+ needNetworkFor(n.request, n.score);
+ n.requested = true;
+ } else if (n.requested == true &&
+ (n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities(
+ mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) {
+ releaseNetworkFor(n.request);
+ n.requested = false;
+ }
+ }
+
+ private void evalRequests() {
+ for (int i = 0; i < mNetworkRequests.size(); i++) {
+ NetworkRequestInfo n = mNetworkRequests.valueAt(i);
+
+ evalRequest(n);
+ }
+ }
+
+ // override to do simple mode (request independent)
+ protected void startNetwork() { }
+ protected void stopNetwork() { }
+
+ // override to do fancier stuff
+ protected void needNetworkFor(NetworkRequest networkRequest, int score) {
+ if (++mRefCount == 1) startNetwork();
+ }
+
+ protected void releaseNetworkFor(NetworkRequest networkRequest) {
+ if (--mRefCount == 0) stopNetwork();
+ }
+
+
+ public void addNetworkRequest(NetworkRequest networkRequest, int score) {
+ sendMessage(obtainMessage(CMD_REQUEST_NETWORK,
+ new NetworkRequestInfo(networkRequest, score)));
+ }
+
+ public void removeNetworkRequest(NetworkRequest networkRequest) {
+ sendMessage(obtainMessage(CMD_CANCEL_REQUEST, networkRequest));
+ }
+
+ public void setScoreFilter(int score) {
+ sendMessage(obtainMessage(CMD_SET_SCORE, score, 0));
+ }
+
+ public void setCapabilityFilter(NetworkCapabilities netCap) {
+ sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap)));
+ }
+
+ protected void log(String s) {
+ Log.d(LOG_TAG, s);
+ }
+}
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 9e656ee..d279412 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -188,6 +188,15 @@ public class NetworkInfo implements Parcelable {
}
/**
+ * @hide
+ */
+ public void setType(int type) {
+ synchronized (this) {
+ mNetworkType = type;
+ }
+ }
+
+ /**
* Return a network-type-specific integer describing the subtype
* of the network.
* @return the network subtype
@@ -198,7 +207,10 @@ public class NetworkInfo implements Parcelable {
}
}
- void setSubtype(int subtype, String subtypeName) {
+ /**
+ * @hide
+ */
+ public void setSubtype(int subtype, String subtypeName) {
synchronized (this) {
mSubtype = subtype;
mSubtypeName = subtypeName;
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 480cb05..47377e9 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -47,19 +47,19 @@ public class NetworkRequest implements Parcelable {
public final int requestId;
/**
- * Set for legacy requests and the default.
+ * Set for legacy requests and the default. Set to TYPE_NONE for none.
* Causes CONNECTIVITY_ACTION broadcasts to be sent.
* @hide
*/
- public final boolean needsBroadcasts;
+ public final int legacyType;
/**
* @hide
*/
- public NetworkRequest(NetworkCapabilities nc, boolean needsBroadcasts, int rId) {
+ public NetworkRequest(NetworkCapabilities nc, int legacyType, int rId) {
requestId = rId;
networkCapabilities = nc;
- this.needsBroadcasts = needsBroadcasts;
+ this.legacyType = legacyType;
}
/**
@@ -68,7 +68,7 @@ public class NetworkRequest implements Parcelable {
public NetworkRequest(NetworkRequest that) {
networkCapabilities = new NetworkCapabilities(that.networkCapabilities);
requestId = that.requestId;
- needsBroadcasts = that.needsBroadcasts;
+ this.legacyType = that.legacyType;
}
// implement the Parcelable interface
@@ -77,16 +77,16 @@ public class NetworkRequest implements Parcelable {
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(networkCapabilities, flags);
- dest.writeInt(needsBroadcasts ? 1 : 0);
+ dest.writeInt(legacyType);
dest.writeInt(requestId);
}
public static final Creator<NetworkRequest> CREATOR =
new Creator<NetworkRequest>() {
public NetworkRequest createFromParcel(Parcel in) {
NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null);
- boolean needsBroadcasts = (in.readInt() == 1);
+ int legacyType = in.readInt();
int requestId = in.readInt();
- NetworkRequest result = new NetworkRequest(nc, needsBroadcasts, requestId);
+ NetworkRequest result = new NetworkRequest(nc, legacyType, requestId);
return result;
}
public NetworkRequest[] newArray(int size) {
@@ -95,14 +95,14 @@ public class NetworkRequest implements Parcelable {
};
public String toString() {
- return "NetworkRequest [ id=" + requestId + ", needsBroadcasts=" + needsBroadcasts +
+ return "NetworkRequest [ id=" + requestId + ", legacyType=" + legacyType +
", " + networkCapabilities.toString() + " ]";
}
public boolean equals(Object obj) {
if (obj instanceof NetworkRequest == false) return false;
NetworkRequest that = (NetworkRequest)obj;
- return (that.needsBroadcasts == this.needsBroadcasts &&
+ return (that.legacyType == this.legacyType &&
that.requestId == this.requestId &&
((that.networkCapabilities == null && this.networkCapabilities == null) ||
(that.networkCapabilities != null &&
@@ -110,7 +110,7 @@ public class NetworkRequest implements Parcelable {
}
public int hashCode() {
- return requestId + (needsBroadcasts ? 1013 : 2026) +
+ return requestId + (legacyType * 1013) +
(networkCapabilities.hashCode() * 1051);
}
}
diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java
index b0449224..cabda5d 100644
--- a/core/java/android/nfc/cardemulation/AidGroup.java
+++ b/core/java/android/nfc/cardemulation/AidGroup.java
@@ -2,6 +2,7 @@ package android.nfc.cardemulation;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -21,6 +22,8 @@ import android.util.Log;
* <p>The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class
* requires the AIDs to be input as a hexadecimal string, with an even amount of
* hexadecimal characters, e.g. "F014811481".
+ *
+ * @hide
*/
public final class AidGroup implements Parcelable {
/**
@@ -30,7 +33,7 @@ public final class AidGroup implements Parcelable {
static final String TAG = "AidGroup";
- final ArrayList<String> aids;
+ final List<String> aids;
final String category;
final String description;
@@ -40,7 +43,7 @@ public final class AidGroup implements Parcelable {
* @param aids The list of AIDs present in the group
* @param category The category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT}
*/
- public AidGroup(ArrayList<String> aids, String category) {
+ public AidGroup(List<String> aids, String category) {
if (aids == null || aids.size() == 0) {
throw new IllegalArgumentException("No AIDS in AID group.");
}
@@ -72,7 +75,7 @@ public final class AidGroup implements Parcelable {
/**
* @return the list of AIDs in this group
*/
- public ArrayList<String> getAids() {
+ public List<String> getAids() {
return aids;
}
@@ -121,11 +124,6 @@ public final class AidGroup implements Parcelable {
}
};
- /**
- * @hide
- * Note: description is not serialized, since it's not localized
- * and resource identifiers don't make sense to persist.
- */
static public AidGroup createFromXml(XmlPullParser parser) throws XmlPullParserException, IOException {
String category = parser.getAttributeValue(null, "category");
ArrayList<String> aids = new ArrayList<String>();
@@ -152,9 +150,6 @@ public final class AidGroup implements Parcelable {
}
}
- /**
- * @hide
- */
public void writeAsXml(XmlSerializer out) throws IOException {
out.attribute(null, "category", category);
for (String aid : aids) {
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index e24a22a..4b9e890 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -303,12 +303,13 @@ public final class CardEmulation {
}
/**
- * Registers a group of AIDs for the specified service.
+ * Registers a list of AIDs for a specific category for the
+ * specified service.
*
- * <p>If an AID group for that category was previously
+ * <p>If a list of AIDs for that category was previously
* registered for this service (either statically
* through the manifest, or dynamically by using this API),
- * that AID group will be replaced with this one.
+ * that list of AIDs will be replaced with this one.
*
* <p>Note that you can only register AIDs for a service that
* is running under the same UID as the caller of this API. Typically
@@ -317,10 +318,13 @@ public final class CardEmulation {
* be shared between packages using shared UIDs.
*
* @param service The component name of the service
- * @param aidGroup The group of AIDs to be registered
+ * @param category The category of AIDs to be registered
+ * @param aids A list containing the AIDs to be registered
* @return whether the registration was successful.
*/
- public boolean registerAidGroupForService(ComponentName service, AidGroup aidGroup) {
+ public boolean registerAidsForService(ComponentName service, String category,
+ List<String> aids) {
+ AidGroup aidGroup = new AidGroup(aids, category);
try {
return sService.registerAidGroupForService(UserHandle.myUserId(), service, aidGroup);
} catch (RemoteException e) {
@@ -341,21 +345,24 @@ public final class CardEmulation {
}
/**
- * Retrieves the currently registered AID group for the specified
+ * Retrieves the currently registered AIDs for the specified
* category for a service.
*
- * <p>Note that this will only return AID groups that were dynamically
- * registered using {@link #registerAidGroupForService(ComponentName, AidGroup)}
- * method. It will *not* return AID groups that were statically registered
+ * <p>Note that this will only return AIDs that were dynamically
+ * registered using {@link #registerAidsForService(ComponentName, String, List)}
+ * method. It will *not* return AIDs that were statically registered
* in the manifest.
*
* @param service The component name of the service
- * @param category The category of the AID group to be returned, e.g. {@link #CATEGORY_PAYMENT}
- * @return The AID group, or null if it couldn't be found
+ * @param category The category for which the AIDs were registered,
+ * e.g. {@link #CATEGORY_PAYMENT}
+ * @return The list of AIDs registered for this category, or null if it couldn't be found.
*/
- public AidGroup getAidGroupForService(ComponentName service, String category) {
+ public List<String> getAidsForService(ComponentName service, String category) {
try {
- return sService.getAidGroupForService(UserHandle.myUserId(), service, category);
+ AidGroup group = sService.getAidGroupForService(UserHandle.myUserId(), service,
+ category);
+ return (group != null ? group.getAids() : null);
} catch (RemoteException e) {
recoverService();
if (sService == null) {
@@ -363,7 +370,9 @@ public final class CardEmulation {
return null;
}
try {
- return sService.getAidGroupForService(UserHandle.myUserId(), service, category);
+ AidGroup group = sService.getAidGroupForService(UserHandle.myUserId(), service,
+ category);
+ return (group != null ? group.getAids() : null);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return null;
@@ -372,21 +381,21 @@ public final class CardEmulation {
}
/**
- * Removes a registered AID group for the specified category for the
+ * Removes a previously registered list of AIDs for the specified category for the
* service provided.
*
- * <p>Note that this will only remove AID groups that were dynamically
- * registered using the {@link #registerAidGroupForService(ComponentName, AidGroup)}
- * method. It will *not* remove AID groups that were statically registered in
- * the manifest. If a dynamically registered AID group is removed using
+ * <p>Note that this will only remove AIDs that were dynamically
+ * registered using the {@link #registerAidsForService(ComponentName, String, List)}
+ * method. It will *not* remove AIDs that were statically registered in
+ * the manifest. If dynamically registered AIDs are removed using
* this method, and a statically registered AID group for the same category
* exists in the manifest, the static AID group will become active again.
*
* @param service The component name of the service
- * @param category The category of the AID group to be removed, e.g. {@link #CATEGORY_PAYMENT}
+ * @param category The category of the AIDs to be removed, e.g. {@link #CATEGORY_PAYMENT}
* @return whether the group was successfully removed.
*/
- public boolean removeAidGroupForService(ComponentName service, String category) {
+ public boolean removeAidsForService(ComponentName service, String category) {
try {
return sService.removeAidGroupForService(UserHandle.myUserId(), service, category);
} catch (RemoteException e) {
diff --git a/core/java/android/os/CommonBundle.java b/core/java/android/os/BaseBundle.java
index c1b202c..c2a45ba 100644
--- a/core/java/android/os/CommonBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -27,7 +27,7 @@ import java.util.Set;
/**
* A mapping from String values to various types.
*/
-abstract class CommonBundle implements Parcelable, Cloneable {
+public class BaseBundle {
private static final String TAG = "Bundle";
static final boolean DEBUG = false;
@@ -63,7 +63,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* inside of the Bundle.
* @param capacity Initial size of the ArrayMap.
*/
- CommonBundle(ClassLoader loader, int capacity) {
+ BaseBundle(ClassLoader loader, int capacity) {
mMap = capacity > 0 ?
new ArrayMap<String, Object>(capacity) : new ArrayMap<String, Object>();
mClassLoader = loader == null ? getClass().getClassLoader() : loader;
@@ -72,7 +72,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
/**
* Constructs a new, empty Bundle.
*/
- CommonBundle() {
+ BaseBundle() {
this((ClassLoader) null, 0);
}
@@ -82,11 +82,11 @@ abstract class CommonBundle implements Parcelable, Cloneable {
*
* @param parcelledData a Parcel containing a Bundle
*/
- CommonBundle(Parcel parcelledData) {
+ BaseBundle(Parcel parcelledData) {
readFromParcelInner(parcelledData);
}
- CommonBundle(Parcel parcelledData, int length) {
+ BaseBundle(Parcel parcelledData, int length) {
readFromParcelInner(parcelledData, length);
}
@@ -97,7 +97,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param loader An explicit ClassLoader to use when instantiating objects
* inside of the Bundle.
*/
- CommonBundle(ClassLoader loader) {
+ BaseBundle(ClassLoader loader) {
this(loader, 0);
}
@@ -107,7 +107,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
*
* @param capacity the initial capacity of the Bundle
*/
- CommonBundle(int capacity) {
+ BaseBundle(int capacity) {
this((ClassLoader) null, capacity);
}
@@ -117,7 +117,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
*
* @param b a Bundle to be copied.
*/
- CommonBundle(CommonBundle b) {
+ BaseBundle(BaseBundle b) {
if (b.mParcelledData != null) {
if (b.mParcelledData == EMPTY_PARCEL) {
mParcelledData = EMPTY_PARCEL;
@@ -148,7 +148,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
*
* @hide
*/
- String getPairValue() {
+ public String getPairValue() {
unparcel();
int size = mMap.size();
if (size > 1) {
@@ -228,7 +228,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
/**
* @hide
*/
- boolean isParcelled() {
+ public boolean isParcelled() {
return mParcelledData != null;
}
@@ -237,7 +237,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
*
* @return the number of mappings as an int.
*/
- int size() {
+ public int size() {
unparcel();
return mMap.size();
}
@@ -245,7 +245,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
/**
* Returns true if the mapping of this Bundle is empty, false otherwise.
*/
- boolean isEmpty() {
+ public boolean isEmpty() {
unparcel();
return mMap.isEmpty();
}
@@ -253,7 +253,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
/**
* Removes all elements from the mapping of this Bundle.
*/
- void clear() {
+ public void clear() {
unparcel();
mMap.clear();
}
@@ -265,7 +265,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String key
* @return true if the key is part of the mapping, false otherwise
*/
- boolean containsKey(String key) {
+ public boolean containsKey(String key) {
unparcel();
return mMap.containsKey(key);
}
@@ -276,7 +276,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String key
* @return an Object, or null
*/
- Object get(String key) {
+ public Object get(String key) {
unparcel();
return mMap.get(key);
}
@@ -286,24 +286,24 @@ abstract class CommonBundle implements Parcelable, Cloneable {
*
* @param key a String key
*/
- void remove(String key) {
+ public void remove(String key) {
unparcel();
mMap.remove(key);
}
/**
- * Inserts all mappings from the given PersistableBundle into this CommonBundle.
+ * Inserts all mappings from the given PersistableBundle into this BaseBundle.
*
* @param bundle a PersistableBundle
*/
- void putAll(PersistableBundle bundle) {
+ public void putAll(PersistableBundle bundle) {
unparcel();
bundle.unparcel();
mMap.putAll(bundle.mMap);
}
/**
- * Inserts all mappings from the given Map into this CommonBundle.
+ * Inserts all mappings from the given Map into this BaseBundle.
*
* @param map a Map
*/
@@ -317,7 +317,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
*
* @return a Set of String keys
*/
- Set<String> keySet() {
+ public Set<String> keySet() {
unparcel();
return mMap.keySet();
}
@@ -377,7 +377,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @param value an int, or null
*/
- void putInt(String key, int value) {
+ public void putInt(String key, int value) {
unparcel();
mMap.put(key, value);
}
@@ -389,7 +389,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @param value a long
*/
- void putLong(String key, long value) {
+ public void putLong(String key, long value) {
unparcel();
mMap.put(key, value);
}
@@ -413,7 +413,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @param value a double
*/
- void putDouble(String key, double value) {
+ public void putDouble(String key, double value) {
unparcel();
mMap.put(key, value);
}
@@ -425,7 +425,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @param value a String, or null
*/
- void putString(String key, String value) {
+ public void putString(String key, String value) {
unparcel();
mMap.put(key, value);
}
@@ -545,7 +545,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @param value an int array object, or null
*/
- void putIntArray(String key, int[] value) {
+ public void putIntArray(String key, int[] value) {
unparcel();
mMap.put(key, value);
}
@@ -557,7 +557,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @param value a long array object, or null
*/
- void putLongArray(String key, long[] value) {
+ public void putLongArray(String key, long[] value) {
unparcel();
mMap.put(key, value);
}
@@ -581,7 +581,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @param value a double array object, or null
*/
- void putDoubleArray(String key, double[] value) {
+ public void putDoubleArray(String key, double[] value) {
unparcel();
mMap.put(key, value);
}
@@ -593,7 +593,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @param value a String array object, or null
*/
- void putStringArray(String key, String[] value) {
+ public void putStringArray(String key, String[] value) {
unparcel();
mMap.put(key, value);
}
@@ -611,18 +611,6 @@ abstract class CommonBundle implements Parcelable, Cloneable {
}
/**
- * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a Bundle object, or null
- */
- void putPersistableBundle(String key, PersistableBundle value) {
- unparcel();
- mMap.put(key, value);
- }
-
- /**
* Returns the value associated with the given key, or false if
* no mapping of the desired type exists for the given key.
*
@@ -789,7 +777,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String
* @return an int value
*/
- int getInt(String key) {
+ public int getInt(String key) {
unparcel();
return getInt(key, 0);
}
@@ -802,7 +790,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param defaultValue Value to return if key does not exist
* @return an int value
*/
- int getInt(String key, int defaultValue) {
+ public int getInt(String key, int defaultValue) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
@@ -823,7 +811,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String
* @return a long value
*/
- long getLong(String key) {
+ public long getLong(String key) {
unparcel();
return getLong(key, 0L);
}
@@ -836,7 +824,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param defaultValue Value to return if key does not exist
* @return a long value
*/
- long getLong(String key, long defaultValue) {
+ public long getLong(String key, long defaultValue) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
@@ -891,7 +879,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String
* @return a double value
*/
- double getDouble(String key) {
+ public double getDouble(String key) {
unparcel();
return getDouble(key, 0.0);
}
@@ -904,7 +892,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param defaultValue Value to return if key does not exist
* @return a double value
*/
- double getDouble(String key, double defaultValue) {
+ public double getDouble(String key, double defaultValue) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
@@ -926,7 +914,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @return a String value, or null
*/
- String getString(String key) {
+ public String getString(String key) {
unparcel();
final Object o = mMap.get(key);
try {
@@ -946,7 +934,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @return the String value associated with the given key, or defaultValue
* if no valid String object is currently mapped to that key.
*/
- String getString(String key, String defaultValue) {
+ public String getString(String key, String defaultValue) {
final String s = getString(key);
return (s == null) ? defaultValue : s;
}
@@ -990,28 +978,6 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* value is explicitly associated with the key.
*
* @param key a String, or null
- * @return a Bundle value, or null
- */
- PersistableBundle getPersistableBundle(String key) {
- unparcel();
- Object o = mMap.get(key);
- if (o == null) {
- return null;
- }
- try {
- return (PersistableBundle) o;
- } catch (ClassCastException e) {
- typeWarning(key, o, "Bundle", e);
- return null;
- }
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
* @return a Serializable value, or null
*/
Serializable getSerializable(String key) {
@@ -1190,7 +1156,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @return an int[] value, or null
*/
- int[] getIntArray(String key) {
+ public int[] getIntArray(String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
@@ -1212,7 +1178,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @return a long[] value, or null
*/
- long[] getLongArray(String key) {
+ public long[] getLongArray(String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
@@ -1256,7 +1222,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @return a double[] value, or null
*/
- double[] getDoubleArray(String key) {
+ public double[] getDoubleArray(String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
@@ -1278,7 +1244,7 @@ abstract class CommonBundle implements Parcelable, Cloneable {
* @param key a String, or null
* @return a String[] value, or null
*/
- String[] getStringArray(String key) {
+ public String[] getStringArray(String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index c85e418..e42c3fe 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -28,14 +28,14 @@ import java.util.Set;
* A mapping from String values to various Parcelable types.
*
*/
-public final class Bundle extends CommonBundle {
+public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
public static final Bundle EMPTY;
static final Parcel EMPTY_PARCEL;
static {
EMPTY = new Bundle();
EMPTY.mMap = ArrayMap.EMPTY;
- EMPTY_PARCEL = CommonBundle.EMPTY_PARCEL;
+ EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL;
}
private boolean mHasFds = false;
@@ -125,14 +125,6 @@ public final class Bundle extends CommonBundle {
}
/**
- * @hide
- */
- @Override
- public String getPairValue() {
- return super.getPairValue();
- }
-
- /**
* Changes the ClassLoader this Bundle uses when instantiating objects.
*
* @param loader An explicit ClassLoader to use when instantiating objects
@@ -168,32 +160,6 @@ public final class Bundle extends CommonBundle {
}
/**
- * @hide
- */
- @Override
- public boolean isParcelled() {
- return super.isParcelled();
- }
-
- /**
- * Returns the number of mappings contained in this Bundle.
- *
- * @return the number of mappings as an int.
- */
- @Override
- public int size() {
- return super.size();
- }
-
- /**
- * Returns true if the mapping of this Bundle is empty, false otherwise.
- */
- @Override
- public boolean isEmpty() {
- return super.isEmpty();
- }
-
- /**
* Removes all elements from the mapping of this Bundle.
*/
@Override
@@ -205,39 +171,6 @@ public final class Bundle extends CommonBundle {
}
/**
- * Returns true if the given key is contained in the mapping
- * of this Bundle.
- *
- * @param key a String key
- * @return true if the key is part of the mapping, false otherwise
- */
- @Override
- public boolean containsKey(String key) {
- return super.containsKey(key);
- }
-
- /**
- * Returns the entry with the given key as an object.
- *
- * @param key a String key
- * @return an Object, or null
- */
- @Override
- public Object get(String key) {
- return super.get(key);
- }
-
- /**
- * Removes any entry with the given key from the mapping of this Bundle.
- *
- * @param key a String key
- */
- @Override
- public void remove(String key) {
- super.remove(key);
- }
-
- /**
* Inserts all mappings from the given Bundle into this Bundle.
*
* @param bundle a Bundle
@@ -253,25 +186,6 @@ public final class Bundle extends CommonBundle {
}
/**
- * Inserts all mappings from the given PersistableBundle into this Bundle.
- *
- * @param bundle a PersistableBundle
- */
- public void putAll(PersistableBundle bundle) {
- super.putAll(bundle);
- }
-
- /**
- * Returns a Set containing the Strings used as keys in this Bundle.
- *
- * @return a Set of String keys
- */
- @Override
- public Set<String> keySet() {
- return super.keySet();
- }
-
- /**
* Reports whether the bundle contains any parcelled file descriptors.
*/
public boolean hasFileDescriptors() {
@@ -384,30 +298,6 @@ public final class Bundle extends CommonBundle {
}
/**
- * Inserts an int value into the mapping of this Bundle, replacing
- * any existing value for the given key.
- *
- * @param key a String, or null
- * @param value an int, or null
- */
- @Override
- public void putInt(String key, int value) {
- super.putInt(key, value);
- }
-
- /**
- * Inserts a long value into the mapping of this Bundle, replacing
- * any existing value for the given key.
- *
- * @param key a String, or null
- * @param value a long
- */
- @Override
- public void putLong(String key, long value) {
- super.putLong(key, value);
- }
-
- /**
* Inserts a float value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
@@ -420,30 +310,6 @@ public final class Bundle extends CommonBundle {
}
/**
- * Inserts a double value into the mapping of this Bundle, replacing
- * any existing value for the given key.
- *
- * @param key a String, or null
- * @param value a double
- */
- @Override
- public void putDouble(String key, double value) {
- super.putDouble(key, value);
- }
-
- /**
- * Inserts a String value into the mapping of this Bundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a String, or null
- */
- @Override
- public void putString(String key, String value) {
- super.putString(key, value);
- }
-
- /**
* Inserts a CharSequence value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
@@ -616,30 +482,6 @@ public final class Bundle extends CommonBundle {
}
/**
- * Inserts an int array value into the mapping of this Bundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value an int array object, or null
- */
- @Override
- public void putIntArray(String key, int[] value) {
- super.putIntArray(key, value);
- }
-
- /**
- * Inserts a long array value into the mapping of this Bundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a long array object, or null
- */
- @Override
- public void putLongArray(String key, long[] value) {
- super.putLongArray(key, value);
- }
-
- /**
* Inserts a float array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
@@ -652,30 +494,6 @@ public final class Bundle extends CommonBundle {
}
/**
- * Inserts a double array value into the mapping of this Bundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a double array object, or null
- */
- @Override
- public void putDoubleArray(String key, double[] value) {
- super.putDoubleArray(key, value);
- }
-
- /**
- * Inserts a String array value into the mapping of this Bundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a String array object, or null
- */
- @Override
- public void putStringArray(String key, String[] value) {
- super.putStringArray(key, value);
- }
-
- /**
* Inserts a CharSequence array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
@@ -700,17 +518,6 @@ public final class Bundle extends CommonBundle {
}
/**
- * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a Bundle object, or null
- */
- public void putPersistableBundle(String key, PersistableBundle value) {
- super.putPersistableBundle(key, value);
- }
-
- /**
* Inserts an {@link IBinder} value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
@@ -846,56 +653,6 @@ public final class Bundle extends CommonBundle {
}
/**
- * Returns the value associated with the given key, or 0 if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @return an int value
- */
- @Override
- public int getInt(String key) {
- return super.getInt(key);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist
- * @return an int value
- */
- @Override
- public int getInt(String key, int defaultValue) {
- return super.getInt(key, defaultValue);
- }
-
- /**
- * Returns the value associated with the given key, or 0L if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @return a long value
- */
- @Override
- public long getLong(String key) {
- return super.getLong(key);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist
- * @return a long value
- */
- @Override
- public long getLong(String key, long defaultValue) {
- return super.getLong(key, defaultValue);
- }
-
- /**
* Returns the value associated with the given key, or 0.0f if
* no mapping of the desired type exists for the given key.
*
@@ -921,58 +678,6 @@ public final class Bundle extends CommonBundle {
}
/**
- * Returns the value associated with the given key, or 0.0 if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @return a double value
- */
- @Override
- public double getDouble(String key) {
- return super.getDouble(key);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist
- * @return a double value
- */
- @Override
- public double getDouble(String key, double defaultValue) {
- return super.getDouble(key, defaultValue);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return a String value, or null
- */
- @Override
- public String getString(String key) {
- return super.getString(key);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String, or null
- * @param defaultValue Value to return if key does not exist
- * @return the String value associated with the given key, or defaultValue
- * if no valid String object is currently mapped to that key.
- */
- @Override
- public String getString(String key, String defaultValue) {
- return super.getString(key, defaultValue);
- }
-
- /**
* Returns the value associated with the given key, or null if
* no mapping of the desired type exists for the given key or a null
* value is explicitly associated with the key.
@@ -1027,18 +732,6 @@ public final class Bundle extends CommonBundle {
* value is explicitly associated with the key.
*
* @param key a String, or null
- * @return a PersistableBundle value, or null
- */
- public PersistableBundle getPersistableBundle(String key) {
- return super.getPersistableBundle(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
* @return a Parcelable value, or null
*/
public <T extends Parcelable> T getParcelable(String key) {
@@ -1232,32 +925,6 @@ public final class Bundle extends CommonBundle {
* value is explicitly associated with the key.
*
* @param key a String, or null
- * @return an int[] value, or null
- */
- @Override
- public int[] getIntArray(String key) {
- return super.getIntArray(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return a long[] value, or null
- */
- @Override
- public long[] getLongArray(String key) {
- return super.getLongArray(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
* @return a float[] value, or null
*/
@Override
@@ -1271,32 +938,6 @@ public final class Bundle extends CommonBundle {
* value is explicitly associated with the key.
*
* @param key a String, or null
- * @return a double[] value, or null
- */
- @Override
- public double[] getDoubleArray(String key) {
- return super.getDoubleArray(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return a String[] value, or null
- */
- @Override
- public String[] getStringArray(String key) {
- return super.getStringArray(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
* @return a CharSequence[] value, or null
*/
@Override
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e98a26b..e84b695 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -191,6 +191,10 @@ public class Environment {
return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_MEDIA, packageName);
}
+ public File[] buildExternalStorageAppMediaDirsForVold(String packageName) {
+ return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_MEDIA, packageName);
+ }
+
public File[] buildExternalStorageAppObbDirs(String packageName) {
return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB, packageName);
}
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index cd8d515..c01f688 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -32,7 +32,8 @@ import java.util.Set;
* restored.
*
*/
-public final class PersistableBundle extends CommonBundle implements XmlUtils.WriteMapCallback {
+public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
+ XmlUtils.WriteMapCallback {
private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
public static final PersistableBundle EMPTY;
static final Parcel EMPTY_PARCEL;
@@ -40,7 +41,7 @@ public final class PersistableBundle extends CommonBundle implements XmlUtils.Wr
static {
EMPTY = new PersistableBundle();
EMPTY.mMap = ArrayMap.EMPTY;
- EMPTY_PARCEL = CommonBundle.EMPTY_PARCEL;
+ EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL;
}
/**
@@ -51,31 +52,6 @@ public final class PersistableBundle extends CommonBundle implements XmlUtils.Wr
}
/**
- * Constructs a PersistableBundle whose data is stored as a Parcel. The data
- * will be unparcelled on first contact, using the assigned ClassLoader.
- *
- * @param parcelledData a Parcel containing a PersistableBundle
- */
- PersistableBundle(Parcel parcelledData) {
- super(parcelledData);
- }
-
- /* package */ PersistableBundle(Parcel parcelledData, int length) {
- super(parcelledData, length);
- }
-
- /**
- * Constructs a new, empty PersistableBundle that uses a specific ClassLoader for
- * instantiating Parcelable and Serializable objects.
- *
- * @param loader An explicit ClassLoader to use when instantiating objects
- * inside of the PersistableBundle.
- */
- public PersistableBundle(ClassLoader loader) {
- super(loader);
- }
-
- /**
* Constructs a new, empty PersistableBundle sized to hold the given number of
* elements. The PersistableBundle will grow as needed.
*
@@ -127,6 +103,10 @@ public final class PersistableBundle extends CommonBundle implements XmlUtils.Wr
}
}
+ /* package */ PersistableBundle(Parcel parcelledData, int length) {
+ super(parcelledData, length);
+ }
+
/**
* Make a PersistableBundle for a single key/value pair.
*
@@ -139,33 +119,6 @@ public final class PersistableBundle extends CommonBundle implements XmlUtils.Wr
}
/**
- * @hide
- */
- @Override
- public String getPairValue() {
- return super.getPairValue();
- }
-
- /**
- * Changes the ClassLoader this PersistableBundle uses when instantiating objects.
- *
- * @param loader An explicit ClassLoader to use when instantiating objects
- * inside of the PersistableBundle.
- */
- @Override
- public void setClassLoader(ClassLoader loader) {
- super.setClassLoader(loader);
- }
-
- /**
- * Return the ClassLoader currently associated with this PersistableBundle.
- */
- @Override
- public ClassLoader getClassLoader() {
- return super.getClassLoader();
- }
-
- /**
* Clones the current PersistableBundle. The internal map is cloned, but the keys and
* values to which it refers are copied by reference.
*/
@@ -175,300 +128,15 @@ public final class PersistableBundle extends CommonBundle implements XmlUtils.Wr
}
/**
- * @hide
- */
- @Override
- public boolean isParcelled() {
- return super.isParcelled();
- }
-
- /**
- * Returns the number of mappings contained in this PersistableBundle.
- *
- * @return the number of mappings as an int.
- */
- @Override
- public int size() {
- return super.size();
- }
-
- /**
- * Returns true if the mapping of this PersistableBundle is empty, false otherwise.
- */
- @Override
- public boolean isEmpty() {
- return super.isEmpty();
- }
-
- /**
- * Removes all elements from the mapping of this PersistableBundle.
- */
- @Override
- public void clear() {
- super.clear();
- }
-
- /**
- * Returns true if the given key is contained in the mapping
- * of this PersistableBundle.
- *
- * @param key a String key
- * @return true if the key is part of the mapping, false otherwise
- */
- @Override
- public boolean containsKey(String key) {
- return super.containsKey(key);
- }
-
- /**
- * Returns the entry with the given key as an object.
- *
- * @param key a String key
- * @return an Object, or null
- */
- @Override
- public Object get(String key) {
- return super.get(key);
- }
-
- /**
- * Removes any entry with the given key from the mapping of this PersistableBundle.
- *
- * @param key a String key
- */
- @Override
- public void remove(String key) {
- super.remove(key);
- }
-
- /**
- * Inserts all mappings from the given PersistableBundle into this Bundle.
- *
- * @param bundle a PersistableBundle
- */
- @Override
- public void putAll(PersistableBundle bundle) {
- super.putAll(bundle);
- }
-
- /**
- * Returns a Set containing the Strings used as keys in this PersistableBundle.
- *
- * @return a Set of String keys
- */
- @Override
- public Set<String> keySet() {
- return super.keySet();
- }
-
- /**
- * Inserts an int value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key.
- *
- * @param key a String, or null
- * @param value an int, or null
- */
- @Override
- public void putInt(String key, int value) {
- super.putInt(key, value);
- }
-
- /**
- * Inserts a long value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key.
- *
- * @param key a String, or null
- * @param value a long
- */
- @Override
- public void putLong(String key, long value) {
- super.putLong(key, value);
- }
-
- /**
- * Inserts a double value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key.
- *
- * @param key a String, or null
- * @param value a double
- */
- @Override
- public void putDouble(String key, double value) {
- super.putDouble(key, value);
- }
-
- /**
- * Inserts a String value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a String, or null
- */
- @Override
- public void putString(String key, String value) {
- super.putString(key, value);
- }
-
- /**
- * Inserts an int array value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value an int array object, or null
- */
- @Override
- public void putIntArray(String key, int[] value) {
- super.putIntArray(key, value);
- }
-
- /**
- * Inserts a long array value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a long array object, or null
- */
- @Override
- public void putLongArray(String key, long[] value) {
- super.putLongArray(key, value);
- }
-
- /**
- * Inserts a double array value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a double array object, or null
- */
- @Override
- public void putDoubleArray(String key, double[] value) {
- super.putDoubleArray(key, value);
- }
-
- /**
- * Inserts a String array value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a String array object, or null
- */
- @Override
- public void putStringArray(String key, String[] value) {
- super.putStringArray(key, value);
- }
-
- /**
* Inserts a PersistableBundle value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a Bundle object, or null
*/
- @Override
public void putPersistableBundle(String key, PersistableBundle value) {
- super.putPersistableBundle(key, value);
- }
-
- /**
- * Returns the value associated with the given key, or 0 if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @return an int value
- */
- @Override
- public int getInt(String key) {
- return super.getInt(key);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist
- * @return an int value
- */
- @Override
- public int getInt(String key, int defaultValue) {
- return super.getInt(key, defaultValue);
- }
-
- /**
- * Returns the value associated with the given key, or 0L if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @return a long value
- */
- @Override
- public long getLong(String key) {
- return super.getLong(key);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist
- * @return a long value
- */
- @Override
- public long getLong(String key, long defaultValue) {
- return super.getLong(key, defaultValue);
- }
-
- /**
- * Returns the value associated with the given key, or 0.0 if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @return a double value
- */
- @Override
- public double getDouble(String key) {
- return super.getDouble(key);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist
- * @return a double value
- */
- @Override
- public double getDouble(String key, double defaultValue) {
- return super.getDouble(key, defaultValue);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return a String value, or null
- */
- @Override
- public String getString(String key) {
- return super.getString(key);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String, or null
- * @param defaultValue Value to return if key does not exist
- * @return the String value associated with the given key, or defaultValue
- * if no valid String object is currently mapped to that key.
- */
- @Override
- public String getString(String key, String defaultValue) {
- return super.getString(key, defaultValue);
+ unparcel();
+ mMap.put(key, value);
}
/**
@@ -479,61 +147,18 @@ public final class PersistableBundle extends CommonBundle implements XmlUtils.Wr
* @param key a String, or null
* @return a Bundle value, or null
*/
- @Override
public PersistableBundle getPersistableBundle(String key) {
- return super.getPersistableBundle(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return an int[] value, or null
- */
- @Override
- public int[] getIntArray(String key) {
- return super.getIntArray(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return a long[] value, or null
- */
- @Override
- public long[] getLongArray(String key) {
- return super.getLongArray(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return a double[] value, or null
- */
- @Override
- public double[] getDoubleArray(String key) {
- return super.getDoubleArray(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return a String[] value, or null
- */
- @Override
- public String[] getStringArray(String key) {
- return super.getStringArray(key);
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (PersistableBundle) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Bundle", e);
+ return null;
+ }
}
public static final Parcelable.Creator<PersistableBundle> CREATOR =
@@ -549,38 +174,6 @@ public final class PersistableBundle extends CommonBundle implements XmlUtils.Wr
}
};
- /**
- * Report the nature of this Parcelable's contents
- */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * Writes the PersistableBundle contents to a Parcel, typically in order for
- * it to be passed through an IBinder connection.
- * @param parcel The parcel to copy this bundle to.
- */
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- final boolean oldAllowFds = parcel.pushAllowFds(false);
- try {
- super.writeToParcelInner(parcel, flags);
- } finally {
- parcel.restoreAllowFds(oldAllowFds);
- }
- }
-
- /**
- * Reads the Parcel contents into this PersistableBundle, typically in order for
- * it to be passed through an IBinder connection.
- * @param parcel The parcel to overwrite this bundle from.
- */
- public void readFromParcel(Parcel parcel) {
- super.readFromParcelInner(parcel);
- }
-
/** @hide */
@Override
public void writeUnknownObject(Object v, String name, XmlSerializer out)
@@ -614,8 +207,29 @@ public final class PersistableBundle extends CommonBundle implements XmlUtils.Wr
}
/**
- * @hide
+ * Report the nature of this Parcelable's contents
*/
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Writes the PersistableBundle contents to a Parcel, typically in order for
+ * it to be passed through an IBinder connection.
+ * @param parcel The parcel to copy this bundle to.
+ */
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ final boolean oldAllowFds = parcel.pushAllowFds(false);
+ try {
+ writeToParcelInner(parcel, flags);
+ } finally {
+ parcel.restoreAllowFds(oldAllowFds);
+ }
+ }
+
+ /** @hide */
public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
XmlPullParserException {
final int outerDepth = in.getDepth();
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 5e005d0..d66fc0f 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -16,7 +16,10 @@
package android.preference;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.media.Ringtone;
@@ -45,11 +48,14 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
private final Context mContext;
private final Handler mHandler;
+ private final H mUiHandler = new H();
private final Callback mCallback;
private final Uri mDefaultUri;
private final AudioManager mAudioManager;
private final int mStreamType;
private final int mMaxStreamVolume;
+ private final Receiver mReceiver = new Receiver();
+ private final Observer mVolumeObserver;
private int mOriginalStreamVolume;
private Ringtone mRingtone;
@@ -63,17 +69,6 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
private static final int MSG_INIT_SAMPLE = 3;
private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
- private ContentObserver mVolumeObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- if (mSeekBar != null && mAudioManager != null) {
- int volume = mAudioManager.getStreamVolume(mStreamType);
- mSeekBar.setProgress(volume);
- }
- }
- };
-
public SeekBarVolumizer(Context context, int streamType, Uri defaultUri,
Callback callback) {
mContext = context;
@@ -85,10 +80,11 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
mHandler = new Handler(thread.getLooper(), this);
mCallback = callback;
mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
+ mVolumeObserver = new Observer(mHandler);
mContext.getContentResolver().registerContentObserver(
System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),
false, mVolumeObserver);
-
+ mReceiver.setListening(true);
if (defaultUri == null) {
if (mStreamType == AudioManager.STREAM_RING) {
defaultUri = Settings.System.DEFAULT_RINGTONE_URI;
@@ -103,6 +99,9 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
}
public void setSeekBar(SeekBar seekBar) {
+ if (mSeekBar != null) {
+ mSeekBar.setOnSeekBarChangeListener(null);
+ }
mSeekBar = seekBar;
mSeekBar.setOnSeekBarChangeListener(null);
mSeekBar.setMax(mMaxStreamVolume);
@@ -150,7 +149,11 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
mCallback.onSampleStarting(this);
}
if (mRingtone != null) {
- mRingtone.play();
+ try {
+ mRingtone.play();
+ } catch (Throwable e) {
+ Log.w(TAG, "Error playing ringtone, stream " + mStreamType, e);
+ }
}
}
}
@@ -172,6 +175,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
postStopSample();
mContext.getContentResolver().unregisterContentObserver(mVolumeObserver);
mSeekBar.setOnSeekBarChangeListener(null);
+ mReceiver.setListening(false);
+ mHandler.getLooper().quitSafely();
}
public void revertVolume() {
@@ -252,4 +257,62 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
postSetVolume(mLastProgress);
}
}
-} \ No newline at end of file
+
+ private final class H extends Handler {
+ private static final int UPDATE_SLIDER = 1;
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == UPDATE_SLIDER) {
+ if (mSeekBar != null) {
+ mSeekBar.setProgress(msg.arg1);
+ mLastProgress = mSeekBar.getProgress();
+ }
+ }
+ }
+
+ public void postUpdateSlider(int volume) {
+ obtainMessage(UPDATE_SLIDER, volume, 0).sendToTarget();
+ }
+ }
+
+ private final class Observer extends ContentObserver {
+ public Observer(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ if (mSeekBar != null && mAudioManager != null) {
+ final int volume = mAudioManager.getStreamVolume(mStreamType);
+ mUiHandler.postUpdateSlider(volume);
+ }
+ }
+ }
+
+ private final class Receiver extends BroadcastReceiver {
+ private boolean mListening;
+
+ public void setListening(boolean listening) {
+ if (mListening == listening) return;
+ mListening = listening;
+ if (listening) {
+ final IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
+ mContext.registerReceiver(this, filter);
+ } else {
+ mContext.unregisterReceiver(this);
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!AudioManager.VOLUME_CHANGED_ACTION.equals(intent.getAction())) return;
+ final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+ final int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
+ if (mSeekBar != null && streamType == mStreamType && streamValue != -1) {
+ mUiHandler.postUpdateSlider(streamValue);
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 846e292..d02fc7b 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -41,12 +41,18 @@ public class ZenModeConfig implements Parcelable {
public static final String SLEEP_MODE_NIGHTS = "nights";
public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights";
+ public static final int SOURCE_ANYONE = 0;
+ public static final int SOURCE_CONTACT = 1;
+ public static final int SOURCE_STAR = 2;
+ public static final int MAX_SOURCE = SOURCE_STAR;
+
private static final int XML_VERSION = 1;
private static final String ZEN_TAG = "zen";
private static final String ZEN_ATT_VERSION = "version";
private static final String ALLOW_TAG = "allow";
private static final String ALLOW_ATT_CALLS = "calls";
private static final String ALLOW_ATT_MESSAGES = "messages";
+ private static final String ALLOW_ATT_FROM = "from";
private static final String SLEEP_TAG = "sleep";
private static final String SLEEP_ATT_MODE = "mode";
@@ -61,6 +67,7 @@ public class ZenModeConfig implements Parcelable {
public boolean allowCalls;
public boolean allowMessages;
+ public int allowFrom = SOURCE_ANYONE;
public String sleepMode;
public int sleepStartHour;
@@ -92,6 +99,7 @@ public class ZenModeConfig implements Parcelable {
conditionIds = new Uri[len];
source.readTypedArray(conditionIds, Uri.CREATOR);
}
+ allowFrom = source.readInt();
}
@Override
@@ -120,6 +128,7 @@ public class ZenModeConfig implements Parcelable {
} else {
dest.writeInt(0);
}
+ dest.writeInt(allowFrom);
}
@Override
@@ -127,6 +136,7 @@ public class ZenModeConfig implements Parcelable {
return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
.append("allowCalls=").append(allowCalls)
.append(",allowMessages=").append(allowMessages)
+ .append(",allowFrom=").append(sourceToString(allowFrom))
.append(",sleepMode=").append(sleepMode)
.append(",sleepStart=").append(sleepStartHour).append('.').append(sleepStartMinute)
.append(",sleepEnd=").append(sleepEndHour).append('.').append(sleepEndMinute)
@@ -137,6 +147,19 @@ public class ZenModeConfig implements Parcelable {
.append(']').toString();
}
+ public static String sourceToString(int source) {
+ switch (source) {
+ case SOURCE_ANYONE:
+ return "anyone";
+ case SOURCE_CONTACT:
+ return "contacts";
+ case SOURCE_STAR:
+ return "stars";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
@Override
public boolean equals(Object o) {
if (!(o instanceof ZenModeConfig)) return false;
@@ -144,6 +167,7 @@ public class ZenModeConfig implements Parcelable {
final ZenModeConfig other = (ZenModeConfig) o;
return other.allowCalls == allowCalls
&& other.allowMessages == allowMessages
+ && other.allowFrom == allowFrom
&& Objects.equals(other.sleepMode, sleepMode)
&& other.sleepStartHour == sleepStartHour
&& other.sleepStartMinute == sleepStartMinute
@@ -155,8 +179,8 @@ public class ZenModeConfig implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(allowCalls, allowMessages, sleepMode, sleepStartHour,
- sleepStartMinute, sleepEndHour, sleepEndMinute,
+ return Objects.hash(allowCalls, allowMessages, allowFrom, sleepMode,
+ sleepStartHour, sleepStartMinute, sleepEndHour, sleepEndMinute,
Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds));
}
@@ -191,6 +215,10 @@ public class ZenModeConfig implements Parcelable {
if (ALLOW_TAG.equals(tag)) {
rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false);
+ rt.allowFrom = safeInt(parser, ALLOW_ATT_FROM, SOURCE_ANYONE);
+ if (rt.allowFrom < SOURCE_ANYONE || rt.allowFrom > MAX_SOURCE) {
+ throw new IndexOutOfBoundsException("bad source in config:" + rt.allowFrom);
+ }
} else if (SLEEP_TAG.equals(tag)) {
final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE);
rt.sleepMode = (SLEEP_MODE_NIGHTS.equals(mode)
@@ -224,6 +252,7 @@ public class ZenModeConfig implements Parcelable {
out.startTag(null, ALLOW_TAG);
out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
+ out.attribute(null, ALLOW_ATT_FROM, Integer.toString(allowFrom));
out.endTag(null, ALLOW_TAG);
out.startTag(null, SLEEP_TAG);
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index cd357b7..2e9077a 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -20,7 +20,6 @@ import android.app.Dialog;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.Region;
@@ -48,9 +47,22 @@ import com.android.internal.app.IVoiceInteractorRequest;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
+import java.lang.ref.WeakReference;
+
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+/**
+ * An active voice interaction session, providing a facility for the implementation
+ * to interact with the user in the voice interaction layer. This interface is no shown
+ * by default, but you can request that it be shown with {@link #showWindow()}, which
+ * will result in a later call to {@link #onCreateContentView()} in which the UI can be
+ * built
+ *
+ * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish}
+ * when done. It can also initiate voice interactions with applications by calling
+ * {@link #startVoiceActivity}</p>.
+ */
public abstract class VoiceInteractionSession implements KeyEvent.Callback {
static final String TAG = "VoiceInteractionSession";
static final boolean DEBUG = true;
@@ -81,11 +93,14 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
final Insets mTmpInsets = new Insets();
final int[] mTmpLocation = new int[2];
+ final WeakReference<VoiceInteractionSession> mWeakRef
+ = new WeakReference<VoiceInteractionSession>(this);
+
final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
@Override
public IVoiceInteractorRequest startConfirmation(String callingPackage,
- IVoiceInteractorCallback callback, String prompt, Bundle extras) {
- Request request = findRequest(callback, true);
+ IVoiceInteractorCallback callback, CharSequence prompt, Bundle extras) {
+ Request request = newRequest(callback);
mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_CONFIRMATION,
new Caller(callingPackage, Binder.getCallingUid()), request,
prompt, extras));
@@ -93,9 +108,19 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
}
@Override
+ public IVoiceInteractorRequest startAbortVoice(String callingPackage,
+ IVoiceInteractorCallback callback, CharSequence message, Bundle extras) {
+ Request request = newRequest(callback);
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_ABORT_VOICE,
+ new Caller(callingPackage, Binder.getCallingUid()), request,
+ message, extras));
+ return request.mInterface;
+ }
+
+ @Override
public IVoiceInteractorRequest startCommand(String callingPackage,
IVoiceInteractorCallback callback, String command, Bundle extras) {
- Request request = findRequest(callback, true);
+ Request request = newRequest(callback);
mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMMAND,
new Caller(callingPackage, Binder.getCallingUid()), request,
command, extras));
@@ -144,29 +169,60 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() {
@Override
public void cancel() throws RemoteException {
- mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
+ VoiceInteractionSession session = mSession.get();
+ if (session != null) {
+ session.mHandlerCaller.sendMessage(
+ session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
+ }
}
};
final IVoiceInteractorCallback mCallback;
- final HandlerCaller mHandlerCaller;
- Request(IVoiceInteractorCallback callback, HandlerCaller handlerCaller) {
+ final WeakReference<VoiceInteractionSession> mSession;
+
+ Request(IVoiceInteractorCallback callback, VoiceInteractionSession session) {
mCallback = callback;
- mHandlerCaller = handlerCaller;
+ mSession = session.mWeakRef;
+ }
+
+ void finishRequest() {
+ VoiceInteractionSession session = mSession.get();
+ if (session == null) {
+ throw new IllegalStateException("VoiceInteractionSession has been destroyed");
+ }
+ Request req = session.removeRequest(mInterface.asBinder());
+ if (req == null) {
+ throw new IllegalStateException("Request not active: " + this);
+ } else if (req != this) {
+ throw new IllegalStateException("Current active request " + req
+ + " not same as calling request " + this);
+ }
}
public void sendConfirmResult(boolean confirmed, Bundle result) {
try {
if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
+ " confirmed=" + confirmed + " result=" + result);
+ finishRequest();
mCallback.deliverConfirmationResult(mInterface, confirmed, result);
} catch (RemoteException e) {
}
}
+ public void sendAbortVoiceResult(Bundle result) {
+ try {
+ if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
+ + " result=" + result);
+ finishRequest();
+ mCallback.deliverAbortVoiceResult(mInterface, result);
+ } catch (RemoteException e) {
+ }
+ }
+
public void sendCommandResult(boolean complete, Bundle result) {
try {
if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
+ " result=" + result);
+ finishRequest();
mCallback.deliverCommandResult(mInterface, complete, result);
} catch (RemoteException e) {
}
@@ -175,6 +231,7 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
public void sendCancelResult() {
try {
if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
+ finishRequest();
mCallback.deliverCancel(mInterface);
} catch (RemoteException e) {
}
@@ -192,9 +249,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
}
static final int MSG_START_CONFIRMATION = 1;
- static final int MSG_START_COMMAND = 2;
- static final int MSG_SUPPORTS_COMMANDS = 3;
- static final int MSG_CANCEL = 4;
+ static final int MSG_START_ABORT_VOICE = 2;
+ static final int MSG_START_COMMAND = 3;
+ static final int MSG_SUPPORTS_COMMANDS = 4;
+ static final int MSG_CANCEL = 5;
static final int MSG_TASK_STARTED = 100;
static final int MSG_TASK_FINISHED = 101;
@@ -210,9 +268,16 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
args = (SomeArgs)msg.obj;
if (DEBUG) Log.d(TAG, "onConfirm: req=" + ((Request) args.arg2).mInterface
+ " prompt=" + args.arg3 + " extras=" + args.arg4);
- onConfirm((Caller)args.arg1, (Request)args.arg2, (String)args.arg3,
+ onConfirm((Caller)args.arg1, (Request)args.arg2, (CharSequence)args.arg3,
(Bundle)args.arg4);
break;
+ case MSG_START_ABORT_VOICE:
+ args = (SomeArgs)msg.obj;
+ if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + ((Request) args.arg2).mInterface
+ + " message=" + args.arg3 + " extras=" + args.arg4);
+ onAbortVoice((Caller) args.arg1, (Request) args.arg2, (CharSequence) args.arg3,
+ (Bundle) args.arg4);
+ break;
case MSG_START_COMMAND:
args = (SomeArgs)msg.obj;
if (DEBUG) Log.d(TAG, "onCommand: req=" + ((Request) args.arg2).mInterface
@@ -330,18 +395,20 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
mCallbacks, true);
}
- Request findRequest(IVoiceInteractorCallback callback, boolean newRequest) {
+ Request newRequest(IVoiceInteractorCallback callback) {
+ synchronized (this) {
+ Request req = new Request(callback, this);
+ mActiveRequests.put(req.mInterface.asBinder(), req);
+ return req;
+ }
+ }
+
+ Request removeRequest(IBinder reqInterface) {
synchronized (this) {
- Request req = mActiveRequests.get(callback.asBinder());
+ Request req = mActiveRequests.get(reqInterface);
if (req != null) {
- if (newRequest) {
- throw new IllegalArgumentException("Given request callback " + callback
- + " is already active");
- }
- return req;
+ mActiveRequests.remove(req);
}
- req = new Request(callback, mHandlerCaller);
- mActiveRequests.put(callback.asBinder(), req);
return req;
}
}
@@ -426,6 +493,27 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
mTheme = theme;
}
+ /**
+ * Ask that a new activity be started for voice interaction. This will create a
+ * new dedicated task in the activity manager for this voice interaction session;
+ * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
+ * will be set for you to make it a new task.
+ *
+ * <p>The newly started activity will be displayed to the user in a special way, as
+ * a layer under the voice interaction UI.</p>
+ *
+ * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor}
+ * through which it can perform voice interactions through your session. These requests
+ * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands},
+ * {@link #onConfirm}, {@link #onCommand}, and {@link #onCancel}.
+ *
+ * <p>You will receive a call to {@link #onTaskStarted} when the task starts up
+ * and {@link #onTaskFinished} when the last activity has finished.
+ *
+ * @param intent The Intent to start this voice interaction. The given Intent will
+ * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since
+ * this is part of a voice interaction.
+ */
public void startVoiceActivity(Intent intent) {
if (mToken == null) {
throw new IllegalStateException("Can't call before onCreate()");
@@ -440,14 +528,23 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
}
}
+ /**
+ * Convenience for inflating views.
+ */
public LayoutInflater getLayoutInflater() {
return mInflater;
}
+ /**
+ * Retrieve the window being used to show the session's UI.
+ */
public Dialog getWindow() {
return mWindow;
}
+ /**
+ * Finish the session.
+ */
public void finish() {
if (mToken == null) {
throw new IllegalStateException("Can't call before onCreate()");
@@ -459,6 +556,12 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
}
}
+ /**
+ * Initiatize a new session.
+ *
+ * @param args The arguments that were supplied to
+ * {@link VoiceInteractionService#startSession VoiceInteractionService.startSession}.
+ */
public void onCreate(Bundle args) {
mTheme = mTheme != 0 ? mTheme
: com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
@@ -473,9 +576,15 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
mWindow.setToken(mToken);
}
+ /**
+ * Last callback to the session as it is being finished.
+ */
public void onDestroy() {
}
+ /**
+ * Hook in which to create the session's UI.
+ */
public View onCreateContentView() {
return null;
}
@@ -508,6 +617,11 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
finish();
}
+ /**
+ * Sessions automatically watch for requests that all system UI be closed (such as when
+ * the user presses HOME), which will appear here. The default implementation always
+ * calls {@link #finish}.
+ */
public void onCloseSystemDialogs() {
finish();
}
@@ -531,15 +645,98 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
outInsets.touchableRegion.setEmpty();
}
+ /**
+ * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)}
+ * has actually started.
+ *
+ * @param intent The original {@link Intent} supplied to
+ * {@link #startVoiceActivity(android.content.Intent)}.
+ * @param taskId Unique ID of the now running task.
+ */
public void onTaskStarted(Intent intent, int taskId) {
}
+ /**
+ * Called when the last activity of a task initiated by
+ * {@link #startVoiceActivity(android.content.Intent)} has finished. The default
+ * implementation calls {@link #finish()} on the assumption that this represents
+ * the completion of a voice action. You can override the implementation if you would
+ * like a different behavior.
+ *
+ * @param intent The original {@link Intent} supplied to
+ * {@link #startVoiceActivity(android.content.Intent)}.
+ * @param taskId Unique ID of the finished task.
+ */
public void onTaskFinished(Intent intent, int taskId) {
finish();
}
- public abstract boolean[] onGetSupportedCommands(Caller caller, String[] commands);
- public abstract void onConfirm(Caller caller, Request request, String prompt, Bundle extras);
+ /**
+ * Request to query for what extended commands the session supports.
+ *
+ * @param caller Who is making the request.
+ * @param commands An array of commands that are being queried.
+ * @return Return an array of booleans indicating which of each entry in the
+ * command array is supported. A true entry in the array indicates the command
+ * is supported; false indicates it is not. The default implementation returns
+ * an array of all false entries.
+ */
+ public boolean[] onGetSupportedCommands(Caller caller, String[] commands) {
+ return new boolean[commands.length];
+ }
+
+ /**
+ * Request to confirm with the user before proceeding with an unrecoverable operation,
+ * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest
+ * VoiceInteractor.ConfirmationRequest}.
+ *
+ * @param caller Who is making the request.
+ * @param request The active request.
+ * @param prompt The prompt informing the user of what will happen, as per
+ * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}.
+ * @param extras Any additional information, as per
+ * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}.
+ */
+ public abstract void onConfirm(Caller caller, Request request, CharSequence prompt,
+ Bundle extras);
+
+ /**
+ * Request to abort the voice interaction session because the voice activity can not
+ * complete its interaction using voice. Corresponds to
+ * {@link android.app.VoiceInteractor.AbortVoiceRequest
+ * VoiceInteractor.AbortVoiceRequest}. The default implementation just sends an empty
+ * confirmation back to allow the activity to exit.
+ *
+ * @param caller Who is making the request.
+ * @param request The active request.
+ * @param message The message informing the user of the problem, as per
+ * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
+ * @param extras Any additional information, as per
+ * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
+ */
+ public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) {
+ request.sendAbortVoiceResult(null);
+ }
+
+ /**
+ * Process an arbitrary extended command from the caller,
+ * corresponding to a {@link android.app.VoiceInteractor.CommandRequest
+ * VoiceInteractor.CommandRequest}.
+ *
+ * @param caller Who is making the request.
+ * @param request The active request.
+ * @param command The command that is being executed, as per
+ * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
+ * @param extras Any additional information, as per
+ * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
+ */
public abstract void onCommand(Caller caller, Request request, String command, Bundle extras);
+
+ /**
+ * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
+ * that was previously delivered to {@link #onConfirm} or {@link #onCommand}.
+ *
+ * @param request The request that is being canceled.
+ */
public abstract void onCancel(Request request);
}
diff --git a/core/java/android/speech/tts/Markup.java b/core/java/android/speech/tts/Markup.java
new file mode 100644
index 0000000..c886e5d
--- /dev/null
+++ b/core/java/android/speech/tts/Markup.java
@@ -0,0 +1,537 @@
+package android.speech.tts;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class that provides markup to a synthesis request to control aspects of speech.
+ * <p>
+ * Markup itself is a feature agnostic data format; the {@link Utterance} class defines the currently
+ * available set of features and should be used to construct instances of the Markup class.
+ * </p>
+ * <p>
+ * A marked up sentence is a tree. Each node has a type, an optional plain text, a set of
+ * parameters, and a list of children.
+ * The <b>type</b> defines what it contains, e.g. "text", "date", "measure", etc. A Markup node
+ * can be either a part of sentence (often a leaf node), or node altering some property of its
+ * children (node with children). The top level node has to be of type "utterance" and its children
+ * are synthesized in order.
+ * The <b>plain text</b> is optional except for the top level node. If the synthesis engine does not
+ * support Markup at all, it should use the plain text of the top level node. If an engine does not
+ * recognize or support a node type, it will try to use the plain text of that node if provided. If
+ * the plain text is null, it will synthesize its children in order.
+ * <b>Parameters</b> are key-value pairs specific to each node type. In case of a date node the
+ * parameters may be for example "month: 7" and "day: 10".
+ * The <b>nested markups</b> are children and can for example be used to nest semiotic classes (a
+ * measure may have a node of type "decimal" as its child) or to modify some property of its
+ * children. See "plain text" on how they are processed if the parent of the children is unknown to
+ * the engine.
+ * <p>
+ */
+public final class Markup implements Parcelable {
+
+ private String mType;
+ private String mPlainText;
+
+ private Bundle mParameters = new Bundle();
+ private List<Markup> mNestedMarkups = new ArrayList<Markup>();
+
+ private static final String TYPE = "type";
+ private static final String PLAIN_TEXT = "plain_text";
+ private static final String MARKUP = "markup";
+
+ private static final String IDENTIFIER_REGEX = "([0-9a-z_]+)";
+ private static final Pattern legalIdentifierPattern = Pattern.compile(IDENTIFIER_REGEX);
+
+ /**
+ * Constructs an empty markup.
+ */
+ public Markup() {}
+
+ /**
+ * Constructs a markup of the given type.
+ */
+ public Markup(String type) {
+ setType(type);
+ }
+
+ /**
+ * Returns the type of this node; can be null.
+ */
+ public String getType() {
+ return mType;
+ }
+
+ /**
+ * Sets the type of this node. can be null. May only contain [0-9a-z_].
+ */
+ public void setType(String type) {
+ if (type != null) {
+ Matcher matcher = legalIdentifierPattern.matcher(type);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Type cannot be empty and may only contain " +
+ "0-9, a-z and underscores.");
+ }
+ }
+ mType = type;
+ }
+
+ /**
+ * Returns this node's plain text; can be null.
+ */
+ public String getPlainText() {
+ return mPlainText;
+ }
+
+ /**
+ * Sets this nodes's plain text; can be null.
+ */
+ public void setPlainText(String plainText) {
+ mPlainText = plainText;
+ }
+
+ /**
+ * Adds or modifies a parameter.
+ * @param key The key; may only contain [0-9a-z_] and cannot be "type" or "plain_text".
+ * @param value The value.
+ * @throws An {@link IllegalArgumentException} if the key is null or empty.
+ * @return this
+ */
+ public Markup setParameter(String key, String value) {
+ if (key == null || key.isEmpty()) {
+ throw new IllegalArgumentException("Key cannot be null or empty.");
+ }
+ if (key.equals("type")) {
+ throw new IllegalArgumentException("Key cannot be \"type\".");
+ }
+ if (key.equals("plain_text")) {
+ throw new IllegalArgumentException("Key cannot be \"plain_text\".");
+ }
+ Matcher matcher = legalIdentifierPattern.matcher(key);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Key may only contain 0-9, a-z and underscores.");
+ }
+
+ if (value != null) {
+ mParameters.putString(key, value);
+ } else {
+ removeParameter(key);
+ }
+ return this;
+ }
+
+ /**
+ * Removes the parameter with the given key
+ */
+ public void removeParameter(String key) {
+ mParameters.remove(key);
+ }
+
+ /**
+ * Returns the value of the parameter.
+ * @param key The parameter key.
+ * @return The value of the parameter or null if the parameter is not set.
+ */
+ public String getParameter(String key) {
+ return mParameters.getString(key);
+ }
+
+ /**
+ * Returns the number of parameters that have been set.
+ */
+ public int parametersSize() {
+ return mParameters.size();
+ }
+
+ /**
+ * Appends a child to the list of children
+ * @param markup The child.
+ * @return This instance.
+ * @throws {@link IllegalArgumentException} if markup is null.
+ */
+ public Markup addNestedMarkup(Markup markup) {
+ if (markup == null) {
+ throw new IllegalArgumentException("Nested markup cannot be null");
+ }
+ mNestedMarkups.add(markup);
+ return this;
+ }
+
+ /**
+ * Removes the given node from its children.
+ * @param markup The child to remove.
+ * @return True if this instance was modified by this operation, false otherwise.
+ */
+ public boolean removeNestedMarkup(Markup markup) {
+ return mNestedMarkups.remove(markup);
+ }
+
+ /**
+ * Returns the index'th child.
+ * @param i The index of the child.
+ * @return The child.
+ * @throws {@link IndexOutOfBoundsException} if i < 0 or i >= nestedMarkupSize()
+ */
+ public Markup getNestedMarkup(int i) {
+ return mNestedMarkups.get(i);
+ }
+
+
+ /**
+ * Returns the number of children.
+ */
+ public int nestedMarkupSize() {
+ return mNestedMarkups.size();
+ }
+
+ /**
+ * Returns a string representation of this Markup instance. Can be deserialized back to a Markup
+ * instance with markupFromString().
+ */
+ public String toString() {
+ StringBuilder out = new StringBuilder();
+ if (mType != null) {
+ out.append(TYPE + ": \"" + mType + "\"");
+ }
+ if (mPlainText != null) {
+ out.append(out.length() > 0 ? " " : "");
+ out.append(PLAIN_TEXT + ": \"" + escapeQuotedString(mPlainText) + "\"");
+ }
+ // Sort the parameters alphabetically by key so we have a stable output.
+ SortedMap<String, String> sortedMap = new TreeMap<String, String>();
+ for (String key : mParameters.keySet()) {
+ sortedMap.put(key, mParameters.getString(key));
+ }
+ for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
+ out.append(out.length() > 0 ? " " : "");
+ out.append(entry.getKey() + ": \"" + escapeQuotedString(entry.getValue()) + "\"");
+ }
+ for (Markup m : mNestedMarkups) {
+ out.append(out.length() > 0 ? " " : "");
+ String nestedStr = m.toString();
+ if (nestedStr.isEmpty()) {
+ out.append(MARKUP + " {}");
+ } else {
+ out.append(MARKUP + " { " + m.toString() + " }");
+ }
+ }
+ return out.toString();
+ }
+
+ /**
+ * Escapes backslashes and double quotes in the plain text and parameter values before this
+ * instance is written to a string.
+ * @param str The string to escape.
+ * @return The escaped string.
+ */
+ private static String escapeQuotedString(String str) {
+ StringBuilder out = new StringBuilder();
+ for (int i = 0; i < str.length(); i++) {
+ char c = str.charAt(i);
+ if (c == '"') {
+ out.append("\\\"");
+ } else if (str.charAt(i) == '\\') {
+ out.append("\\\\");
+ } else {
+ out.append(c);
+ }
+ }
+ return out.toString();
+ }
+
+ /**
+ * The reverse of the escape method, returning plain text and parameter values to their original
+ * form.
+ * @param str An escaped string.
+ * @return The unescaped string.
+ */
+ private static String unescapeQuotedString(String str) {
+ StringBuilder out = new StringBuilder();
+ for (int i = 0; i < str.length(); i++) {
+ char c = str.charAt(i);
+ if (c == '\\') {
+ i++;
+ if (i >= str.length()) {
+ throw new IllegalArgumentException("Unterminated escape sequence in string: " +
+ str);
+ }
+ c = str.charAt(i);
+ if (c == '\\') {
+ out.append("\\");
+ } else if (c == '"') {
+ out.append("\"");
+ } else {
+ throw new IllegalArgumentException("Unsupported escape sequence: \\" + c +
+ " in string " + str);
+ }
+ } else {
+ out.append(c);
+ }
+ }
+ return out.toString();
+ }
+
+ /**
+ * Returns true if the given string consists only of whitespace.
+ * @param str The string to check.
+ * @return True if the given string consists only of whitespace.
+ */
+ private static boolean isWhitespace(String str) {
+ return Pattern.matches("\\s*", str);
+ }
+
+ /**
+ * Parses the given string, and overrides the values of this instance with those contained
+ * in the given string.
+ * @param str The string to parse; can have superfluous whitespace.
+ * @return An empty string on success, else the remainder of the string that could not be
+ * parsed.
+ */
+ private String fromReadableString(String str) {
+ while (!isWhitespace(str)) {
+ String newStr = matchValue(str);
+ if (newStr == null) {
+ newStr = matchMarkup(str);
+
+ if (newStr == null) {
+ return str;
+ }
+ }
+ str = newStr;
+ }
+ return "";
+ }
+
+ // Matches: key : "value"
+ // where key is an identifier and value can contain escaped quotes
+ // there may be superflouous whitespace
+ // The value string may contain quotes and backslashes.
+ private static final String OPTIONAL_WHITESPACE = "\\s*";
+ private static final String VALUE_REGEX = "((\\\\.|[^\\\"])*)";
+ private static final String KEY_VALUE_REGEX =
+ "\\A" + OPTIONAL_WHITESPACE + // start of string
+ IDENTIFIER_REGEX + OPTIONAL_WHITESPACE + ":" + OPTIONAL_WHITESPACE + // key:
+ "\"" + VALUE_REGEX + "\""; // "value"
+ private static final Pattern KEY_VALUE_PATTERN = Pattern.compile(KEY_VALUE_REGEX);
+
+ /**
+ * Tries to match a key-value pair at the start of the string. If found, add that as a parameter
+ * of this instance.
+ * @param str The string to parse.
+ * @return The remainder of the string without the parsed key-value pair on success, else null.
+ */
+ private String matchValue(String str) {
+ // Matches: key: "value"
+ Matcher matcher = KEY_VALUE_PATTERN.matcher(str);
+ if (!matcher.find()) {
+ return null;
+ }
+ String key = matcher.group(1);
+ String value = matcher.group(2);
+
+ if (key == null || value == null) {
+ return null;
+ }
+ String unescapedValue = unescapeQuotedString(value);
+ if (key.equals(TYPE)) {
+ this.mType = unescapedValue;
+ } else if (key.equals(PLAIN_TEXT)) {
+ this.mPlainText = unescapedValue;
+ } else {
+ setParameter(key, unescapedValue);
+ }
+
+ return str.substring(matcher.group(0).length());
+ }
+
+ // matches 'markup {'
+ private static final Pattern OPEN_MARKUP_PATTERN =
+ Pattern.compile("\\A" + OPTIONAL_WHITESPACE + MARKUP + OPTIONAL_WHITESPACE + "\\{");
+ // matches '}'
+ private static final Pattern CLOSE_MARKUP_PATTERN =
+ Pattern.compile("\\A" + OPTIONAL_WHITESPACE + "\\}");
+
+ /**
+ * Tries to parse a Markup specification from the start of the string. If so, add that markup to
+ * the list of nested Markup's of this instance.
+ * @param str The string to parse.
+ * @return The remainder of the string without the parsed Markup on success, else null.
+ */
+ private String matchMarkup(String str) {
+ // find and strip "markup {"
+ Matcher matcher = OPEN_MARKUP_PATTERN.matcher(str);
+
+ if (!matcher.find()) {
+ return null;
+ }
+ String strRemainder = str.substring(matcher.group(0).length());
+ // parse and strip markup contents
+ Markup nestedMarkup = new Markup();
+ strRemainder = nestedMarkup.fromReadableString(strRemainder);
+
+ // find and strip "}"
+ Matcher matcherClose = CLOSE_MARKUP_PATTERN.matcher(strRemainder);
+ if (!matcherClose.find()) {
+ return null;
+ }
+ strRemainder = strRemainder.substring(matcherClose.group(0).length());
+
+ // Everything parsed, add markup
+ this.addNestedMarkup(nestedMarkup);
+
+ // Return remainder
+ return strRemainder;
+ }
+
+ /**
+ * Returns a Markup instance from the string representation generated by toString().
+ * @param string The string representation generated by toString().
+ * @return The new Markup instance.
+ * @throws {@link IllegalArgumentException} if the input cannot be correctly parsed.
+ */
+ public static Markup markupFromString(String string) throws IllegalArgumentException {
+ Markup m = new Markup();
+ if (m.fromReadableString(string).isEmpty()) {
+ return m;
+ } else {
+ throw new IllegalArgumentException("Cannot parse input to Markup");
+ }
+ }
+
+ /**
+ * Compares the specified object with this Markup for equality.
+ * @return True if the given object is a Markup instance with the same type, plain text,
+ * parameters and the nested markups are also equal to each other and in the same order.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if ( this == o ) return true;
+ if ( !(o instanceof Markup) ) return false;
+ Markup m = (Markup) o;
+
+ if (nestedMarkupSize() != this.nestedMarkupSize()) {
+ return false;
+ }
+
+ if (!(mType == null ? m.mType == null : mType.equals(m.mType))) {
+ return false;
+ }
+ if (!(mPlainText == null ? m.mPlainText == null : mPlainText.equals(m.mPlainText))) {
+ return false;
+ }
+ if (!equalBundles(mParameters, m.mParameters)) {
+ return false;
+ }
+
+ for (int i = 0; i < this.nestedMarkupSize(); i++) {
+ if (!mNestedMarkups.get(i).equals(m.mNestedMarkups.get(i))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks if two bundles are equal to each other. Used by equals(o).
+ */
+ private boolean equalBundles(Bundle one, Bundle two) {
+ if (one == null || two == null) {
+ return false;
+ }
+
+ if(one.size() != two.size()) {
+ return false;
+ }
+
+ Set<String> valuesOne = one.keySet();
+ for(String key : valuesOne) {
+ Object valueOne = one.get(key);
+ Object valueTwo = two.get(key);
+ if (valueOne instanceof Bundle && valueTwo instanceof Bundle &&
+ !equalBundles((Bundle) valueOne, (Bundle) valueTwo)) {
+ return false;
+ } else if (valueOne == null) {
+ if (valueTwo != null || !two.containsKey(key)) {
+ return false;
+ }
+ } else if(!valueOne.equals(valueTwo)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns an unmodifiable list of the children.
+ * @return An unmodifiable list of children that throws an {@link UnsupportedOperationException}
+ * if an attempt is made to modify it
+ */
+ public List<Markup> getNestedMarkups() {
+ return Collections.unmodifiableList(mNestedMarkups);
+ }
+
+ /**
+ * @hide
+ */
+ public Markup(Parcel in) {
+ mType = in.readString();
+ mPlainText = in.readString();
+ mParameters = in.readBundle();
+ in.readList(mNestedMarkups, Markup.class.getClassLoader());
+ }
+
+ /**
+ * Creates a deep copy of the given markup.
+ */
+ public Markup(Markup markup) {
+ mType = markup.mType;
+ mPlainText = markup.mPlainText;
+ mParameters = markup.mParameters;
+ for (Markup nested : markup.getNestedMarkups()) {
+ addNestedMarkup(new Markup(nested));
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @hide
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mType);
+ dest.writeString(mPlainText);
+ dest.writeBundle(mParameters);
+ dest.writeList(mNestedMarkups);
+ }
+
+ /**
+ * @hide
+ */
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ public Markup createFromParcel(Parcel in) {
+ return new Markup(in);
+ }
+
+ public Markup[] newArray(int size) {
+ return new Markup[size];
+ }
+ };
+}
+
diff --git a/core/java/android/speech/tts/SynthesisRequestV2.java b/core/java/android/speech/tts/SynthesisRequestV2.java
index a1da49c..130e3f9 100644
--- a/core/java/android/speech/tts/SynthesisRequestV2.java
+++ b/core/java/android/speech/tts/SynthesisRequestV2.java
@@ -4,11 +4,12 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.speech.tts.TextToSpeechClient.UtteranceId;
+import android.util.Log;
/**
* Service-side representation of a synthesis request from a V2 API client. Contains:
* <ul>
- * <li>The utterance to synthesize</li>
+ * <li>The markup object to synthesize containing the utterance.</li>
* <li>The id of the utterance (String, result of {@link UtteranceId#toUniqueString()}</li>
* <li>The synthesis voice name (String, result of {@link VoiceInfo#getName()})</li>
* <li>Voice parameters (Bundle of parameters)</li>
@@ -16,8 +17,11 @@ import android.speech.tts.TextToSpeechClient.UtteranceId;
* </ul>
*/
public final class SynthesisRequestV2 implements Parcelable {
- /** Synthesis utterance. */
- private final String mText;
+
+ private static final String TAG = "SynthesisRequestV2";
+
+ /** Synthesis markup */
+ private final Markup mMarkup;
/** Synthesis id. */
private final String mUtteranceId;
@@ -34,9 +38,9 @@ public final class SynthesisRequestV2 implements Parcelable {
/**
* Constructor for test purposes.
*/
- public SynthesisRequestV2(String text, String utteranceId, String voiceName,
+ public SynthesisRequestV2(Markup markup, String utteranceId, String voiceName,
Bundle voiceParams, Bundle audioParams) {
- this.mText = text;
+ this.mMarkup = markup;
this.mUtteranceId = utteranceId;
this.mVoiceName = voiceName;
this.mVoiceParams = voiceParams;
@@ -49,15 +53,18 @@ public final class SynthesisRequestV2 implements Parcelable {
* @hide
*/
public SynthesisRequestV2(Parcel in) {
- this.mText = in.readString();
+ this.mMarkup = (Markup) in.readValue(Markup.class.getClassLoader());
this.mUtteranceId = in.readString();
this.mVoiceName = in.readString();
this.mVoiceParams = in.readBundle();
this.mAudioParams = in.readBundle();
}
- SynthesisRequestV2(String text, String utteranceId, RequestConfig rconfig) {
- this.mText = text;
+ /**
+ * Constructor to request the synthesis of a sentence.
+ */
+ SynthesisRequestV2(Markup markup, String utteranceId, RequestConfig rconfig) {
+ this.mMarkup = markup;
this.mUtteranceId = utteranceId;
this.mVoiceName = rconfig.getVoice().getName();
this.mVoiceParams = rconfig.getVoiceParams();
@@ -71,7 +78,7 @@ public final class SynthesisRequestV2 implements Parcelable {
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mText);
+ dest.writeValue(mMarkup);
dest.writeString(mUtteranceId);
dest.writeString(mVoiceName);
dest.writeBundle(mVoiceParams);
@@ -82,7 +89,18 @@ public final class SynthesisRequestV2 implements Parcelable {
* @return the text which should be synthesized.
*/
public String getText() {
- return mText;
+ if (mMarkup.getPlainText() == null) {
+ Log.e(TAG, "Plaintext of markup is null.");
+ return "";
+ }
+ return mMarkup.getPlainText();
+ }
+
+ /**
+ * @return the markup which should be synthesized.
+ */
+ public Markup getMarkup() {
+ return mMarkup;
}
/**
diff --git a/core/java/android/speech/tts/TextToSpeechClient.java b/core/java/android/speech/tts/TextToSpeechClient.java
index 85f702b..e17b498 100644
--- a/core/java/android/speech/tts/TextToSpeechClient.java
+++ b/core/java/android/speech/tts/TextToSpeechClient.java
@@ -512,7 +512,6 @@ public class TextToSpeechClient {
}
}
-
/**
* Connects the client to TTS service. This method returns immediately, and connects to the
* service in the background.
@@ -876,7 +875,7 @@ public class TextToSpeechClient {
private static final String ACTION_QUEUE_SPEAK_NAME = "queueSpeak";
/**
- * Speaks the string using the specified queuing strategy using current
+ * Speaks the string using the specified queuing strategy and the current
* voice. This method is asynchronous, i.e. the method just adds the request
* to the queue of TTS requests and then returns. The synthesis might not
* have finished (or even started!) at the time when this method returns.
@@ -887,12 +886,35 @@ public class TextToSpeechClient {
* in {@link RequestCallbacks}.
* @param config Synthesis request configuration. Can't be null. Has to contain a
* voice.
- * @param callbacks Synthesis request callbacks. If null, default request
+ * @param callbacks Synthesis request callbacks. If null, the default request
* callbacks object will be used.
*/
public void queueSpeak(final String utterance, final UtteranceId utteranceId,
final RequestConfig config,
final RequestCallbacks callbacks) {
+ queueSpeak(createMarkupFromString(utterance), utteranceId, config, callbacks);
+ }
+
+ /**
+ * Speaks the {@link Markup} (which can be constructed with {@link Utterance}) using
+ * the specified queuing strategy and the current voice. This method is
+ * asynchronous, i.e. the method just adds the request to the queue of TTS
+ * requests and then returns. The synthesis might not have finished (or even
+ * started!) at the time when this method returns.
+ *
+ * @param markup The Markup to be spoken. The written equivalent of the spoken
+ * text should be no longer than 1000 characters.
+ * @param utteranceId Unique identificator used to track the synthesis progress
+ * in {@link RequestCallbacks}.
+ * @param config Synthesis request configuration. Can't be null. Has to contain a
+ * voice.
+ * @param callbacks Synthesis request callbacks. If null, the default request
+ * callbacks object will be used.
+ */
+ public void queueSpeak(final Markup markup,
+ final UtteranceId utteranceId,
+ final RequestConfig config,
+ final RequestCallbacks callbacks) {
runAction(new Action(ACTION_QUEUE_SPEAK_NAME) {
@Override
public void run(ITextToSpeechService service) throws RemoteException {
@@ -908,7 +930,7 @@ public class TextToSpeechClient {
int queueResult = service.speakV2(
getCallerIdentity(),
- new SynthesisRequestV2(utterance, utteranceId.toUniqueString(), config));
+ new SynthesisRequestV2(markup, utteranceId.toUniqueString(), config));
if (queueResult != Status.SUCCESS) {
removeCallbackAndErr(utteranceId.toUniqueString(), queueResult);
}
@@ -931,12 +953,37 @@ public class TextToSpeechClient {
* @param outputFile File to write the generated audio data to.
* @param config Synthesis request configuration. Can't be null. Have to contain a
* voice.
- * @param callbacks Synthesis request callbacks. If null, default request
+ * @param callbacks Synthesis request callbacks. If null, the default request
* callbacks object will be used.
*/
public void queueSynthesizeToFile(final String utterance, final UtteranceId utteranceId,
final File outputFile, final RequestConfig config,
final RequestCallbacks callbacks) {
+ queueSynthesizeToFile(createMarkupFromString(utterance), utteranceId, outputFile, config, callbacks);
+ }
+
+ /**
+ * Synthesizes the given {@link Markup} (can be constructed with {@link Utterance})
+ * to a file using the specified parameters. This method is asynchronous, i.e. the
+ * method just adds the request to the queue of TTS requests and then returns. The
+ * synthesis might not have finished (or even started!) at the time when this method
+ * returns.
+ *
+ * @param markup The Markup that should be synthesized. The written equivalent of
+ * the spoken text should be no longer than 1000 characters.
+ * @param utteranceId Unique identificator used to track the synthesis progress
+ * in {@link RequestCallbacks}.
+ * @param outputFile File to write the generated audio data to.
+ * @param config Synthesis request configuration. Can't be null. Have to contain a
+ * voice.
+ * @param callbacks Synthesis request callbacks. If null, the default request
+ * callbacks object will be used.
+ */
+ public void queueSynthesizeToFile(
+ final Markup markup,
+ final UtteranceId utteranceId,
+ final File outputFile, final RequestConfig config,
+ final RequestCallbacks callbacks) {
runAction(new Action(ACTION_QUEUE_SYNTHESIZE_TO_FILE) {
@Override
public void run(ITextToSpeechService service) throws RemoteException {
@@ -964,8 +1011,7 @@ public class TextToSpeechClient {
int queueResult = service.synthesizeToFileDescriptorV2(getCallerIdentity(),
fileDescriptor,
- new SynthesisRequestV2(utterance, utteranceId.toUniqueString(),
- config));
+ new SynthesisRequestV2(markup, utteranceId.toUniqueString(), config));
fileDescriptor.close();
if (queueResult != Status.SUCCESS) {
removeCallbackAndErr(utteranceId.toUniqueString(), queueResult);
@@ -981,6 +1027,13 @@ public class TextToSpeechClient {
});
}
+ private static Markup createMarkupFromString(String str) {
+ return new Utterance()
+ .append(new Utterance.TtsText(str))
+ .setNoWarningOnFallback(true)
+ .createMarkup();
+ }
+
private static final String ACTION_QUEUE_SILENCE_NAME = "queueSilence";
/**
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 6b899d9..14a4024 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -352,6 +352,12 @@ public abstract class TextToSpeechService extends Service {
params.putString(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS, "true");
}
+ String noWarning = request.getMarkup().getParameter(Utterance.KEY_NO_WARNING_ON_FALLBACK);
+ if (noWarning == null || noWarning.equals("false")) {
+ Log.w("TextToSpeechService", "The synthesis engine does not support Markup, falling " +
+ "back to the given plain text.");
+ }
+
// Build V1 request
SynthesisRequest requestV1 = new SynthesisRequest(request.getText(), params);
Locale locale = selectedVoice.getLocale();
@@ -856,14 +862,53 @@ public abstract class TextToSpeechService extends Service {
}
}
+ /**
+ * Estimate of the character count equivalent of a Markup instance. Calculated
+ * by summing the characters of all Markups of type "text". Each other node
+ * is counted as a single character, as the character count of other nodes
+ * is non-trivial to calculate and we don't want to accept arbitrarily large
+ * requests.
+ */
+ private int estimateSynthesisLengthFromMarkup(Markup m) {
+ int size = 0;
+ if (m.getType() != null &&
+ m.getType().equals("text") &&
+ m.getParameter("text") != null) {
+ size += m.getParameter("text").length();
+ } else if (m.getType() == null ||
+ !m.getType().equals("utterance")) {
+ size += 1;
+ }
+ for (Markup nested : m.getNestedMarkups()) {
+ size += estimateSynthesisLengthFromMarkup(nested);
+ }
+ return size;
+ }
+
@Override
public boolean isValid() {
- if (mSynthesisRequest.getText() == null) {
- Log.e(TAG, "null synthesis text");
+ if (mSynthesisRequest.getMarkup() == null) {
+ Log.e(TAG, "No markup in request.");
return false;
}
- if (mSynthesisRequest.getText().length() >= TextToSpeech.getMaxSpeechInputLength()) {
- Log.w(TAG, "Text too long: " + mSynthesisRequest.getText().length() + " chars");
+ String type = mSynthesisRequest.getMarkup().getType();
+ if (type == null) {
+ Log.w(TAG, "Top level markup node should have type \"utterance\", not null");
+ return false;
+ } else if (!type.equals("utterance")) {
+ Log.w(TAG, "Top level markup node should have type \"utterance\" instead of " +
+ "\"" + type + "\"");
+ return false;
+ }
+
+ int estimate = estimateSynthesisLengthFromMarkup(mSynthesisRequest.getMarkup());
+ if (estimate >= TextToSpeech.getMaxSpeechInputLength()) {
+ Log.w(TAG, "Text too long: estimated size of text was " + estimate + " chars.");
+ return false;
+ }
+
+ if (estimate <= 0) {
+ Log.e(TAG, "null synthesis text");
return false;
}
diff --git a/core/java/android/speech/tts/Utterance.java b/core/java/android/speech/tts/Utterance.java
new file mode 100644
index 0000000..0a29283
--- /dev/null
+++ b/core/java/android/speech/tts/Utterance.java
@@ -0,0 +1,595 @@
+package android.speech.tts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class acts as a builder for {@link Markup} instances.
+ * <p>
+ * Each Utterance consists of a list of the semiotic classes ({@link Utterance.TtsCardinal} and
+ * {@link Utterance.TtsText}).
+ * <p>Each semiotic class can be supplied with morphosyntactic features
+ * (gender, animacy, multiplicity and case), it is up to the synthesis engine to use this
+ * information during synthesis.
+ * Examples where morphosyntactic features matter:
+ * <ul>
+ * <li>In French, the number one is verbalized differently based on the gender of the noun
+ * it modifies. "un homme" (one man) versus "une femme" (one woman).
+ * <li>In German the grammatical case (accusative, locative, etc) needs to be included to be
+ * verbalize correctly. In German you'd have the sentence "Sie haben 1 kilometer vor Ihnen" (You
+ * have 1 kilometer ahead of you), "1" in this case needs to become inflected to the accusative
+ * form ("einen") instead of the nominative form "ein".
+ * </p>
+ * <p>
+ * Utterance usage example:
+ * Markup m1 = new Utterance().append("The Eiffel Tower is")
+ * .append(new TtsCardinal(324))
+ * .append("meters tall.");
+ * Markup m2 = new Utterance().append("Sie haben")
+ * .append(new TtsCardinal(1).setGender(Utterance.GENDER_MALE)
+ * .append("Tag frei.");
+ * </p>
+ */
+public class Utterance {
+
+ /***
+ * Toplevel type of markup representation.
+ */
+ public static final String TYPE_UTTERANCE = "utterance";
+ /***
+ * The no_warning_on_fallback parameter can be set to "false" or "true", true indicating that
+ * no warning will be given when the synthesizer does not support Markup. This is used when
+ * the user only provides a string to the API instead of a markup.
+ */
+ public static final String KEY_NO_WARNING_ON_FALLBACK = "no_warning_on_fallback";
+
+ // Gender.
+ public final static int GENDER_UNKNOWN = 0;
+ public final static int GENDER_NEUTRAL = 1;
+ public final static int GENDER_MALE = 2;
+ public final static int GENDER_FEMALE = 3;
+
+ // Animacy.
+ public final static int ANIMACY_UNKNOWN = 0;
+ public final static int ANIMACY_ANIMATE = 1;
+ public final static int ANIMACY_INANIMATE = 2;
+
+ // Multiplicity.
+ public final static int MULTIPLICITY_UNKNOWN = 0;
+ public final static int MULTIPLICITY_SINGLE = 1;
+ public final static int MULTIPLICITY_DUAL = 2;
+ public final static int MULTIPLICITY_PLURAL = 3;
+
+ // Case.
+ public final static int CASE_UNKNOWN = 0;
+ public final static int CASE_NOMINATIVE = 1;
+ public final static int CASE_ACCUSATIVE = 2;
+ public final static int CASE_DATIVE = 3;
+ public final static int CASE_ABLATIVE = 4;
+ public final static int CASE_GENITIVE = 5;
+ public final static int CASE_VOCATIVE = 6;
+ public final static int CASE_LOCATIVE = 7;
+ public final static int CASE_INSTRUMENTAL = 8;
+
+ private List<AbstractTts<? extends AbstractTts<?>>> says =
+ new ArrayList<AbstractTts<? extends AbstractTts<?>>>();
+ Boolean mNoWarningOnFallback = null;
+
+ /**
+ * Objects deriving from this class can be appended to a Utterance. This class uses generics
+ * so method from this class can return instances of its child classes, resulting in a better
+ * API (CRTP pattern).
+ */
+ public static abstract class AbstractTts<C extends AbstractTts<C>> {
+
+ protected Markup mMarkup = new Markup();
+
+ /**
+ * Empty constructor.
+ */
+ protected AbstractTts() {
+ }
+
+ /**
+ * Construct with Markup.
+ * @param markup
+ */
+ protected AbstractTts(Markup markup) {
+ mMarkup = markup;
+ }
+
+ /**
+ * Returns the type of this class, e.g. "cardinal" or "measure".
+ * @return The type.
+ */
+ public String getType() {
+ return mMarkup.getType();
+ }
+
+ /**
+ * A fallback plain text can be provided, in case the engine does not support this class
+ * type, or even Markup altogether.
+ * @param plainText A string with the plain text.
+ * @return This instance.
+ */
+ @SuppressWarnings("unchecked")
+ public C setPlainText(String plainText) {
+ mMarkup.setPlainText(plainText);
+ return (C) this;
+ }
+
+ /**
+ * Returns the plain text (fallback) string.
+ * @return Plain text string or null if not set.
+ */
+ public String getPlainText() {
+ return mMarkup.getPlainText();
+ }
+
+ /**
+ * Populates the plainText if not set and builds a Markup instance.
+ * @return The Markup object describing this instance.
+ */
+ public Markup getMarkup() {
+ return new Markup(mMarkup);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected C setParameter(String key, String value) {
+ mMarkup.setParameter(key, value);
+ return (C) this;
+ }
+
+ protected String getParameter(String key) {
+ return mMarkup.getParameter(key);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected C removeParameter(String key) {
+ mMarkup.removeParameter(key);
+ return (C) this;
+ }
+
+ /**
+ * Returns a string representation of this instance, can be deserialized to an equal
+ * Utterance instance.
+ */
+ public String toString() {
+ return mMarkup.toString();
+ }
+
+ /**
+ * Returns a generated plain text alternative for this instance if this instance isn't
+ * better representated by the list of it's children.
+ * @return Best effort plain text representation of this instance, can be null.
+ */
+ public String generatePlainText() {
+ return null;
+ }
+ }
+
+ public static abstract class AbstractTtsSemioticClass<C extends AbstractTtsSemioticClass<C>>
+ extends AbstractTts<C> {
+ // Keys.
+ private static final String KEY_GENDER = "gender";
+ private static final String KEY_ANIMACY = "animacy";
+ private static final String KEY_MULTIPLICITY = "multiplicity";
+ private static final String KEY_CASE = "case";
+
+ protected AbstractTtsSemioticClass() {
+ super();
+ }
+
+ protected AbstractTtsSemioticClass(Markup markup) {
+ super(markup);
+ }
+
+ @SuppressWarnings("unchecked")
+ public C setGender(int gender) {
+ if (gender < 0 || gender > 3) {
+ throw new IllegalArgumentException("Only four types of gender can be set: " +
+ "unknown, neutral, maculine and female.");
+ }
+ if (gender != GENDER_UNKNOWN) {
+ setParameter(KEY_GENDER, String.valueOf(gender));
+ } else {
+ setParameter(KEY_GENDER, null);
+ }
+ return (C) this;
+ }
+
+ public int getGender() {
+ String gender = mMarkup.getParameter(KEY_GENDER);
+ return gender != null ? Integer.valueOf(gender) : GENDER_UNKNOWN;
+ }
+
+ @SuppressWarnings("unchecked")
+ public C setAnimacy(int animacy) {
+ if (animacy < 0 || animacy > 2) {
+ throw new IllegalArgumentException(
+ "Only two types of animacy can be set: unknown, animate and inanimate");
+ }
+ if (animacy != ANIMACY_UNKNOWN) {
+ setParameter(KEY_ANIMACY, String.valueOf(animacy));
+ } else {
+ setParameter(KEY_ANIMACY, null);
+ }
+ return (C) this;
+ }
+
+ public int getAnimacy() {
+ String animacy = getParameter(KEY_ANIMACY);
+ return animacy != null ? Integer.valueOf(animacy) : ANIMACY_UNKNOWN;
+ }
+
+ @SuppressWarnings("unchecked")
+ public C setMultiplicity(int multiplicity) {
+ if (multiplicity < 0 || multiplicity > 3) {
+ throw new IllegalArgumentException(
+ "Only four types of multiplicity can be set: unknown, single, dual and " +
+ "plural.");
+ }
+ if (multiplicity != MULTIPLICITY_UNKNOWN) {
+ setParameter(KEY_MULTIPLICITY, String.valueOf(multiplicity));
+ } else {
+ setParameter(KEY_MULTIPLICITY, null);
+ }
+ return (C) this;
+ }
+
+ public int getMultiplicity() {
+ String multiplicity = mMarkup.getParameter(KEY_MULTIPLICITY);
+ return multiplicity != null ? Integer.valueOf(multiplicity) : MULTIPLICITY_UNKNOWN;
+ }
+
+ @SuppressWarnings("unchecked")
+ public C setCase(int grammaticalCase) {
+ if (grammaticalCase < 0 || grammaticalCase > 8) {
+ throw new IllegalArgumentException(
+ "Only nine types of grammatical case can be set.");
+ }
+ if (grammaticalCase != CASE_UNKNOWN) {
+ setParameter(KEY_CASE, String.valueOf(grammaticalCase));
+ } else {
+ setParameter(KEY_CASE, null);
+ }
+ return (C) this;
+ }
+
+ public int getCase() {
+ String grammaticalCase = mMarkup.getParameter(KEY_CASE);
+ return grammaticalCase != null ? Integer.valueOf(grammaticalCase) : CASE_UNKNOWN;
+ }
+ }
+
+ /**
+ * Class that contains regular text, synthesis engine pronounces it using its regular pipeline.
+ * Parameters:
+ * <ul>
+ * <li>Text: the text to synthesize</li>
+ * </ul>
+ */
+ public static class TtsText extends AbstractTtsSemioticClass<TtsText> {
+
+ // The type of this node.
+ protected static final String TYPE_TEXT = "text";
+ // The text parameter stores the text to be synthesized.
+ private static final String KEY_TEXT = "text";
+
+ /**
+ * Default constructor.
+ */
+ public TtsText() {
+ mMarkup.setType(TYPE_TEXT);
+ }
+
+ /**
+ * Constructor that sets the text to be synthesized.
+ * @param text The text to be synthesized.
+ */
+ public TtsText(String text) {
+ this();
+ setText(text);
+ }
+
+ /**
+ * Constructs a TtsText with the values of the Markup, does not check if the given Markup is
+ * of the right type.
+ */
+ private TtsText(Markup markup) {
+ super(markup);
+ }
+
+ /**
+ * Sets the text to be synthesized.
+ * @return This instance.
+ */
+ public TtsText setText(String text) {
+ setParameter(KEY_TEXT, text);
+ return this;
+ }
+
+ /**
+ * Returns the text to be synthesized.
+ * @return This instance.
+ */
+ public String getText() {
+ return getParameter(KEY_TEXT);
+ }
+
+ /**
+ * Generates a best effort plain text, in this case simply the text.
+ */
+ @Override
+ public String generatePlainText() {
+ return getText();
+ }
+ }
+
+ /**
+ * Contains a cardinal.
+ * Parameters:
+ * <ul>
+ * <li>integer: the integer to synthesize</li>
+ * </ul>
+ */
+ public static class TtsCardinal extends AbstractTtsSemioticClass<TtsCardinal> {
+
+ // The type of this node.
+ protected static final String TYPE_CARDINAL = "cardinal";
+ // The parameter integer stores the integer to synthesize.
+ private static final String KEY_INTEGER = "integer";
+
+ /**
+ * Default constructor.
+ */
+ public TtsCardinal() {
+ mMarkup.setType(TYPE_CARDINAL);
+ }
+
+ /**
+ * Constructor that sets the integer to be synthesized.
+ */
+ public TtsCardinal(int integer) {
+ this();
+ setInteger(integer);
+ }
+
+ /**
+ * Constructor that sets the integer to be synthesized.
+ */
+ public TtsCardinal(String integer) {
+ this();
+ setInteger(integer);
+ }
+
+ /**
+ * Constructs a TtsText with the values of the Markup.
+ * Does not check if the given Markup is of the right type.
+ */
+ private TtsCardinal(Markup markup) {
+ super(markup);
+ }
+
+ /**
+ * Sets the integer.
+ * @return This instance.
+ */
+ public TtsCardinal setInteger(int integer) {
+ return setInteger(String.valueOf(integer));
+ }
+
+ /**
+ * Sets the integer.
+ * @param integer A non-empty string of digits with an optional '-' in front.
+ * @return This instance.
+ */
+ public TtsCardinal setInteger(String integer) {
+ if (!integer.matches("-?\\d+")) {
+ throw new IllegalArgumentException("Expected a cardinal: \"" + integer + "\"");
+ }
+ setParameter(KEY_INTEGER, integer);
+ return this;
+ }
+
+ /**
+ * Returns the integer parameter.
+ */
+ public String getInteger() {
+ return getParameter(KEY_INTEGER);
+ }
+
+ /**
+ * Generates a best effort plain text, in this case simply the integer.
+ */
+ @Override
+ public String generatePlainText() {
+ return getInteger();
+ }
+ }
+
+ /**
+ * Default constructor.
+ */
+ public Utterance() {}
+
+ /**
+ * Returns the plain text of a given Markup if it was set; if it's not set, recursively call the
+ * this same method on its children.
+ */
+ private String constructPlainText(Markup m) {
+ StringBuilder plainText = new StringBuilder();
+ if (m.getPlainText() != null) {
+ plainText.append(m.getPlainText());
+ } else {
+ for (Markup nestedMarkup : m.getNestedMarkups()) {
+ String nestedPlainText = constructPlainText(nestedMarkup);
+ if (!nestedPlainText.isEmpty()) {
+ if (plainText.length() != 0) {
+ plainText.append(" ");
+ }
+ plainText.append(nestedPlainText);
+ }
+ }
+ }
+ return plainText.toString();
+ }
+
+ /**
+ * Creates a Markup instance with auto generated plain texts for the relevant nodes, in case the
+ * user has not provided one already.
+ * @return A Markup instance representing this utterance.
+ */
+ public Markup createMarkup() {
+ Markup markup = new Markup(TYPE_UTTERANCE);
+ StringBuilder plainText = new StringBuilder();
+ for (AbstractTts<? extends AbstractTts<?>> say : says) {
+ // Get a copy of this markup, and generate a plaintext for it if is not set.
+ Markup sayMarkup = say.getMarkup();
+ if (sayMarkup.getPlainText() == null) {
+ sayMarkup.setPlainText(say.generatePlainText());
+ }
+ if (plainText.length() != 0) {
+ plainText.append(" ");
+ }
+ plainText.append(constructPlainText(sayMarkup));
+ markup.addNestedMarkup(sayMarkup);
+ }
+ if (mNoWarningOnFallback != null) {
+ markup.setParameter(KEY_NO_WARNING_ON_FALLBACK,
+ mNoWarningOnFallback ? "true" : "false");
+ }
+ markup.setPlainText(plainText.toString());
+ return markup;
+ }
+
+ /**
+ * Appends an element to this Utterance instance.
+ * @return this instance
+ */
+ public Utterance append(AbstractTts<? extends AbstractTts<?>> say) {
+ says.add(say);
+ return this;
+ }
+
+ private Utterance append(Markup markup) {
+ if (markup.getType().equals(TtsText.TYPE_TEXT)) {
+ append(new TtsText(markup));
+ } else if (markup.getType().equals(TtsCardinal.TYPE_CARDINAL)) {
+ append(new TtsCardinal(markup));
+ } else {
+ // Unknown node, a class we don't know about.
+ if (markup.getPlainText() != null) {
+ append(new TtsText(markup.getPlainText()));
+ } else {
+ // No plainText specified; add its children
+ // seperately. In case of a new prosody node,
+ // we would still verbalize it correctly.
+ for (Markup nested : markup.getNestedMarkups()) {
+ append(nested);
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Returns a string representation of this Utterance instance. Can be deserialized back to an
+ * Utterance instance with utteranceFromString(). Can be used to store utterances to be used
+ * at a later time.
+ */
+ public String toString() {
+ String out = "type: \"" + TYPE_UTTERANCE + "\"";
+ if (mNoWarningOnFallback != null) {
+ out += " no_warning_on_fallback: \"" + (mNoWarningOnFallback ? "true" : "false") + "\"";
+ }
+ for (AbstractTts<? extends AbstractTts<?>> say : says) {
+ out += " markup { " + say.getMarkup().toString() + " }";
+ }
+ return out;
+ }
+
+ /**
+ * Returns an Utterance instance from the string representation generated by toString().
+ * @param string The string representation generated by toString().
+ * @return The new Utterance instance.
+ * @throws {@link IllegalArgumentException} if the input cannot be correctly parsed.
+ */
+ static public Utterance utteranceFromString(String string) throws IllegalArgumentException {
+ Utterance utterance = new Utterance();
+ Markup markup = Markup.markupFromString(string);
+ if (!markup.getType().equals(TYPE_UTTERANCE)) {
+ throw new IllegalArgumentException("Top level markup should be of type \"" +
+ TYPE_UTTERANCE + "\", but was of type \"" +
+ markup.getType() + "\".") ;
+ }
+ for (Markup nestedMarkup : markup.getNestedMarkups()) {
+ utterance.append(nestedMarkup);
+ }
+ return utterance;
+ }
+
+ /**
+ * Appends a new TtsText with the given text.
+ * @param text The text to synthesize.
+ * @return This instance.
+ */
+ public Utterance append(String text) {
+ return append(new TtsText(text));
+ }
+
+ /**
+ * Appends a TtsCardinal representing the given number.
+ * @param integer The integer to synthesize.
+ * @return this
+ */
+ public Utterance append(int integer) {
+ return append(new TtsCardinal(integer));
+ }
+
+ /**
+ * Returns the n'th element in this Utterance.
+ * @param i The index.
+ * @return The n'th element in this Utterance.
+ * @throws {@link IndexOutOfBoundsException} - if i < 0 || i >= size()
+ */
+ public AbstractTts<? extends AbstractTts<?>> get(int i) {
+ return says.get(i);
+ }
+
+ /**
+ * Returns the number of elements in this Utterance.
+ * @return The number of elements in this Utterance.
+ */
+ public int size() {
+ return says.size();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if ( this == o ) return true;
+ if ( !(o instanceof Utterance) ) return false;
+ Utterance utt = (Utterance) o;
+
+ if (says.size() != utt.says.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < says.size(); i++) {
+ if (!says.get(i).getMarkup().equals(utt.says.get(i).getMarkup())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Can be set to true or false, true indicating that the user provided only a string to the API,
+ * at which the system will not issue a warning if the synthesizer falls back onto the plain
+ * text when the synthesizer does not support Markup.
+ */
+ public Utterance setNoWarningOnFallback(boolean noWarning) {
+ mNoWarningOnFallback = noWarning;
+ return this;
+ }
+}
diff --git a/core/java/android/tv/ITvInputClient.aidl b/core/java/android/tv/ITvInputClient.aidl
index ac83356..ef89c68 100644
--- a/core/java/android/tv/ITvInputClient.aidl
+++ b/core/java/android/tv/ITvInputClient.aidl
@@ -17,6 +17,7 @@
package android.tv;
import android.content.ComponentName;
+import android.os.Bundle;
import android.tv.ITvInputSession;
import android.view.InputChannel;
@@ -29,4 +30,6 @@ oneway interface ITvInputClient {
void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq);
void onAvailabilityChanged(in String inputId, boolean isAvailable);
void onSessionReleased(int seq);
+ void onSessionEvent(in String name, in Bundle args, int seq);
+ void onVideoSizeChanged(int width, int height, int seq);
}
diff --git a/core/java/android/tv/ITvInputSessionCallback.aidl b/core/java/android/tv/ITvInputSessionCallback.aidl
index a2bd0d7..e27b8bf 100644
--- a/core/java/android/tv/ITvInputSessionCallback.aidl
+++ b/core/java/android/tv/ITvInputSessionCallback.aidl
@@ -16,6 +16,7 @@
package android.tv;
+import android.os.Bundle;
import android.tv.ITvInputSession;
/**
@@ -25,4 +26,6 @@ import android.tv.ITvInputSession;
*/
oneway interface ITvInputSessionCallback {
void onSessionCreated(ITvInputSession session);
+ void onSessionEvent(in String name, in Bundle args);
+ void onVideoSizeChanged(int width, int height);
}
diff --git a/core/java/android/tv/TvInputManager.java b/core/java/android/tv/TvInputManager.java
index dfa84f8..d0c2ca6 100644
--- a/core/java/android/tv/TvInputManager.java
+++ b/core/java/android/tv/TvInputManager.java
@@ -18,6 +18,7 @@ package android.tv;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -85,6 +86,29 @@ public final class TvInputManager {
*/
public void onSessionReleased(Session session) {
}
+
+ /**
+ * This is called at the beginning of the playback of a channel and later when the size of
+ * the video has been changed.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback
+ * @param width the width of the video
+ * @param height the height of the video
+ * @hide
+ */
+ public void onVideoSizeChanged(Session session, int width, int height) {
+ }
+
+ /**
+ * This is called when a custom event has been sent from this session.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback
+ * @param eventType The type of the event.
+ * @param eventArgs Optional arguments of the event.
+ * @hide
+ */
+ public void onSessionEvent(Session session, String eventType, Bundle eventArgs) {
+ }
}
private static final class SessionCallbackRecord {
@@ -116,6 +140,24 @@ public final class TvInputManager {
}
});
}
+
+ public void postVideoSizeChanged(final int width, final int height) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onVideoSizeChanged(mSession, width, height);
+ }
+ });
+ }
+
+ public void postSessionEvent(final String eventType, final Bundle eventArgs) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onSessionEvent(mSession, eventType, eventArgs);
+ }
+ });
+ }
}
/**
@@ -196,6 +238,30 @@ public final class TvInputManager {
}
@Override
+ public void onVideoSizeChanged(int width, int height, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postVideoSizeChanged(width, height);
+ }
+ }
+
+ @Override
+ public void onSessionEvent(String eventType, Bundle eventArgs, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postSessionEvent(eventType, eventArgs);
+ }
+ }
+
+ @Override
public void onAvailabilityChanged(String inputId, boolean isAvailable) {
synchronized (mTvInputListenerRecordsMap) {
List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
index cb0142f..03d24db 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -23,6 +23,7 @@ import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -156,6 +157,7 @@ public abstract class TvInputService extends Service {
private boolean mOverlayViewEnabled;
private IBinder mWindowToken;
private Rect mOverlayFrame;
+ private ITvInputSessionCallback mSessionCallback;
public TvInputSessionImpl() {
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
@@ -188,6 +190,52 @@ public abstract class TvInputService extends Service {
}
/**
+ * Dispatches an event to the application using this session.
+ *
+ * @param eventType The type of the event.
+ * @param eventArgs Optional arguments of the event.
+ * @hide
+ */
+ public void dispatchSessionEvent(final String eventType, final Bundle eventArgs) {
+ if (eventType == null) {
+ throw new IllegalArgumentException("eventType should not be null.");
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "dispatchSessionEvent(" + eventType + ")");
+ mSessionCallback.onSessionEvent(eventType, eventArgs);
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in sending event (event=" + eventType + ")");
+ }
+ }
+ });
+ }
+
+ /**
+ * Sends the change on the size of the video. This is expected to be called at the
+ * beginning of the playback and later when the size has been changed.
+ *
+ * @param width The width of the video.
+ * @param height The height of the video.
+ * @hide
+ */
+ public void dispatchVideoSizeChanged(final int width, final int height) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "dispatchVideoSizeChanged");
+ mSessionCallback.onVideoSizeChanged(width, height);
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in dispatchVideoSizeChanged");
+ }
+ }
+ });
+ }
+
+ /**
* Called when the session is released.
*/
public abstract void onRelease();
@@ -394,9 +442,7 @@ public abstract class TvInputService extends Service {
mWindowManager.removeView(mOverlayView);
mOverlayView = null;
}
- if (DEBUG) {
- Log.d(TAG, "create overlay view(" + frame + ")");
- }
+ if (DEBUG) Log.d(TAG, "create overlay view(" + frame + ")");
mWindowToken = windowToken;
mOverlayFrame = frame;
if (!mOverlayViewEnabled) {
@@ -431,9 +477,7 @@ public abstract class TvInputService extends Service {
* @param frame A new position of the overlay view.
*/
void relayoutOverlayView(Rect frame) {
- if (DEBUG) {
- Log.d(TAG, "relayout overlay view(" + frame + ")");
- }
+ if (DEBUG) Log.d(TAG, "relayoutOverlayView(" + frame + ")");
mOverlayFrame = frame;
if (!mOverlayViewEnabled || mOverlayView == null) {
return;
@@ -449,9 +493,7 @@ public abstract class TvInputService extends Service {
* Removes the current overlay view.
*/
void removeOverlayView(boolean clearWindowToken) {
- if (DEBUG) {
- Log.d(TAG, "remove overlay view(" + mOverlayView + ")");
- }
+ if (DEBUG) Log.d(TAG, "removeOverlayView(" + mOverlayView + ")");
if (clearWindowToken) {
mWindowToken = null;
mOverlayFrame = null;
@@ -498,6 +540,10 @@ public abstract class TvInputService extends Service {
mOverlayView.getViewRootImpl().dispatchInputEvent(event, receiver);
return Session.DISPATCH_IN_PROGRESS;
}
+
+ private void setSessionCallback(ITvInputSessionCallback callback) {
+ mSessionCallback = callback;
+ }
}
private final class ServiceHandler extends Handler {
@@ -517,6 +563,7 @@ public abstract class TvInputService extends Service {
// Failed to create a session.
cb.onSessionCreated(null);
} else {
+ sessionImpl.setSessionCallback(cb);
ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
sessionImpl, channel);
cb.onSessionCreated(stub);
diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java
index 59b6386..2d31701 100644
--- a/core/java/android/tv/TvView.java
+++ b/core/java/android/tv/TvView.java
@@ -18,6 +18,7 @@ package android.tv;
import android.content.Context;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.tv.TvInputManager.Session;
@@ -379,5 +380,23 @@ public class TvView extends SurfaceView {
mExternalCallback.onSessionReleased(session);
}
}
+
+ @Override
+ public void onVideoSizeChanged(Session session, int width, int height) {
+ if (DEBUG) {
+ Log.d(TAG, "onVideoSizeChanged(" + width + ", " + height + ")");
+ }
+ if (mExternalCallback != null) {
+ mExternalCallback.onVideoSizeChanged(session, width, height);
+ }
+ }
+
+ @Override
+ public void onSessionEvent(TvInputManager.Session session, String eventType,
+ Bundle eventArgs) {
+ if (mExternalCallback != null) {
+ mExternalCallback.onSessionEvent(session, eventType, eventArgs);
+ }
+ }
}
}
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 424d860..5056097 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -75,22 +75,10 @@ class GLES20Canvas extends HardwareCanvas {
// Constructors
///////////////////////////////////////////////////////////////////////////
- /**
- * Creates a canvas to render directly on screen.
- */
- GLES20Canvas(boolean translucent) {
- this(false, translucent);
- }
-
- protected GLES20Canvas(boolean record, boolean translucent) {
- mOpaque = !translucent;
-
- if (record) {
- mRenderer = nCreateDisplayListRenderer();
- } else {
- mRenderer = nCreateRenderer();
- }
-
+ // TODO: Merge with GLES20RecordingCanvas
+ protected GLES20Canvas() {
+ mOpaque = false;
+ mRenderer = nCreateDisplayListRenderer();
setupFinalizer();
}
@@ -102,7 +90,6 @@ class GLES20Canvas extends HardwareCanvas {
}
}
- private static native long nCreateRenderer();
private static native long nCreateDisplayListRenderer();
private static native void nResetDisplayListRenderer(long renderer);
private static native void nDestroyRenderer(long renderer);
@@ -131,36 +118,6 @@ class GLES20Canvas extends HardwareCanvas {
private static native void nSetProperty(String name, String value);
///////////////////////////////////////////////////////////////////////////
- // Hardware layers
- ///////////////////////////////////////////////////////////////////////////
-
- @Override
- void pushLayerUpdate(HardwareLayer layer) {
- nPushLayerUpdate(mRenderer, layer.getLayer());
- }
-
- @Override
- void cancelLayerUpdate(HardwareLayer layer) {
- nCancelLayerUpdate(mRenderer, layer.getLayer());
- }
-
- @Override
- void flushLayerUpdates() {
- nFlushLayerUpdates(mRenderer);
- }
-
- @Override
- void clearLayerUpdates() {
- nClearLayerUpdates(mRenderer);
- }
-
- static native boolean nCopyLayer(long layerId, long bitmap);
- private static native void nClearLayerUpdates(long renderer);
- private static native void nFlushLayerUpdates(long renderer);
- private static native void nPushLayerUpdate(long renderer, long layer);
- private static native void nCancelLayerUpdate(long renderer, long layer);
-
- ///////////////////////////////////////////////////////////////////////////
// Canvas management
///////////////////////////////////////////////////////////////////////////
@@ -234,20 +191,6 @@ class GLES20Canvas extends HardwareCanvas {
private static native void nFinish(long renderer);
- /**
- * Returns the size of the stencil buffer required by the underlying
- * implementation.
- *
- * @return The minimum number of bits the stencil buffer must. Always >= 0.
- *
- * @hide
- */
- public static int getStencilSize() {
- return nGetStencilSize();
- }
-
- private static native int nGetStencilSize();
-
///////////////////////////////////////////////////////////////////////////
// Functor
///////////////////////////////////////////////////////////////////////////
@@ -284,49 +227,6 @@ class GLES20Canvas extends HardwareCanvas {
*/
static final int FLUSH_CACHES_FULL = 2;
- /**
- * Flush caches to reclaim as much memory as possible. The amount of memory
- * to reclaim is indicate by the level parameter.
- *
- * The level can be one of {@link #FLUSH_CACHES_MODERATE} or
- * {@link #FLUSH_CACHES_FULL}.
- *
- * @param level Hint about the amount of memory to reclaim
- */
- static void flushCaches(int level) {
- nFlushCaches(level);
- }
-
- private static native void nFlushCaches(int level);
-
- /**
- * Release all resources associated with the underlying caches. This should
- * only be called after a full flushCaches().
- *
- * @hide
- */
- static void terminateCaches() {
- nTerminateCaches();
- }
-
- private static native void nTerminateCaches();
-
- static boolean initCaches() {
- return nInitCaches();
- }
-
- private static native boolean nInitCaches();
-
- ///////////////////////////////////////////////////////////////////////////
- // Atlas
- ///////////////////////////////////////////////////////////////////////////
-
- static void initAtlas(GraphicBuffer buffer, long[] map) {
- nInitAtlas(buffer, map, map.length);
- }
-
- private static native void nInitAtlas(GraphicBuffer buffer, long[] map, int count);
-
///////////////////////////////////////////////////////////////////////////
// Display list
///////////////////////////////////////////////////////////////////////////
@@ -899,12 +799,6 @@ class GLES20Canvas extends HardwareCanvas {
private static native void nDrawPath(long renderer, long path, long paint);
private static native void nDrawRects(long renderer, long region, long paint);
- void drawRects(float[] rects, int count, Paint paint) {
- nDrawRects(mRenderer, rects, count, paint.mNativePaint);
- }
-
- private static native void nDrawRects(long renderer, float[] rects, int count, long paint);
-
@Override
public void drawPicture(Picture picture) {
if (picture.createdFromStream) {
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index a94ec3a..b2961e5 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -36,7 +36,7 @@ class GLES20RecordingCanvas extends GLES20Canvas {
RenderNode mNode;
private GLES20RecordingCanvas() {
- super(true, true);
+ super();
}
static GLES20RecordingCanvas obtain(@NonNull RenderNode node) {
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
deleted file mode 100644
index 64a4c41..0000000
--- a/core/java/android/view/GLRenderer.java
+++ /dev/null
@@ -1,1527 +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 android.view;
-
-import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_BAD_NATIVE_WINDOW;
-import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_CONFIG_CAVEAT;
-import static javax.microedition.khronos.egl.EGL10.EGL_DEFAULT_DISPLAY;
-import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_DRAW;
-import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_HEIGHT;
-import static javax.microedition.khronos.egl.EGL10.EGL_NONE;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_DISPLAY;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE;
-import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE;
-import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLES;
-import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLE_BUFFERS;
-import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_SUCCESS;
-import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE;
-import static javax.microedition.khronos.egl.EGL10.EGL_WIDTH;
-import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT;
-
-import android.content.ComponentCallbacks2;
-import android.graphics.Bitmap;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.SurfaceTexture;
-import android.opengl.EGL14;
-import android.opengl.GLUtils;
-import android.opengl.ManagedEGLContext;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Surface.OutOfResourcesException;
-
-import com.google.android.gles_jni.EGLImpl;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.locks.ReentrantLock;
-
-import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGL11;
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.egl.EGLDisplay;
-import javax.microedition.khronos.egl.EGLSurface;
-import javax.microedition.khronos.opengles.GL;
-
-/**
- * Hardware renderer using OpenGL
- *
- * @hide
- */
-public class GLRenderer extends HardwareRenderer {
- static final int SURFACE_STATE_ERROR = 0;
- static final int SURFACE_STATE_SUCCESS = 1;
- static final int SURFACE_STATE_UPDATED = 2;
-
- static final int FUNCTOR_PROCESS_DELAY = 4;
-
- /**
- * Number of frames to profile.
- */
- private static final int PROFILE_MAX_FRAMES = 128;
-
- /**
- * Number of floats per profiled frame.
- */
- private static final int PROFILE_FRAME_DATA_COUNT = 3;
-
- private static final int PROFILE_DRAW_MARGIN = 0;
- private static final int PROFILE_DRAW_WIDTH = 3;
- private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
- private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
- private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
- private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
- private static final int PROFILE_DRAW_DP_PER_MS = 7;
-
- private static final String[] VISUALIZERS = {
- PROFILE_PROPERTY_VISUALIZE_BARS,
- };
-
- private static final String[] OVERDRAW = {
- OVERDRAW_PROPERTY_SHOW,
- };
- private static final int GL_VERSION = 2;
-
- static EGL10 sEgl;
- static EGLDisplay sEglDisplay;
- static EGLConfig sEglConfig;
- static final Object[] sEglLock = new Object[0];
- int mWidth = -1, mHeight = -1;
-
- static final ThreadLocal<ManagedEGLContext> sEglContextStorage
- = new ThreadLocal<ManagedEGLContext>();
-
- EGLContext mEglContext;
- Thread mEglThread;
-
- EGLSurface mEglSurface;
-
- GL mGl;
- HardwareCanvas mCanvas;
-
- String mName;
-
- long mFrameCount;
- Paint mDebugPaint;
-
- static boolean sDirtyRegions;
- static final boolean sDirtyRegionsRequested;
- static {
- String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
- //noinspection PointlessBooleanExpression,ConstantConditions
- sDirtyRegions = "true".equalsIgnoreCase(dirtyProperty);
- sDirtyRegionsRequested = sDirtyRegions;
- }
-
- boolean mDirtyRegionsEnabled;
- boolean mUpdateDirtyRegions;
-
- boolean mProfileEnabled;
- int mProfileVisualizerType = -1;
- float[] mProfileData;
- ReentrantLock mProfileLock;
- int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
-
- GraphDataProvider mDebugDataProvider;
- float[][] mProfileShapes;
- Paint mProfilePaint;
-
- boolean mDebugDirtyRegions;
- int mDebugOverdraw = -1;
-
- final boolean mTranslucent;
-
- private boolean mDestroyed;
-
- private final Rect mRedrawClip = new Rect();
-
- private final int[] mSurfaceSize = new int[2];
-
- private long mDrawDelta = Long.MAX_VALUE;
-
- private GLES20Canvas mGlCanvas;
-
- private DisplayMetrics mDisplayMetrics;
-
- private static EGLSurface sPbuffer;
- private static final Object[] sPbufferLock = new Object[0];
-
- private List<HardwareLayer> mAttachedLayers = new ArrayList<HardwareLayer>();
-
- private static class GLRendererEglContext extends ManagedEGLContext {
- final Handler mHandler = new Handler();
-
- public GLRendererEglContext(EGLContext context) {
- super(context);
- }
-
- @Override
- public void onTerminate(final EGLContext eglContext) {
- // Make sure we do this on the correct thread.
- if (mHandler.getLooper() != Looper.myLooper()) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- onTerminate(eglContext);
- }
- });
- return;
- }
-
- synchronized (sEglLock) {
- if (sEgl == null) return;
-
- if (EGLImpl.getInitCount(sEglDisplay) == 1) {
- usePbufferSurface(eglContext);
- GLES20Canvas.terminateCaches();
-
- sEgl.eglDestroyContext(sEglDisplay, eglContext);
- sEglContextStorage.set(null);
- sEglContextStorage.remove();
-
- sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
- sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
- EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
- sEgl.eglReleaseThread();
- sEgl.eglTerminate(sEglDisplay);
-
- sEgl = null;
- sEglDisplay = null;
- sEglConfig = null;
- sPbuffer = null;
- }
- }
- }
- }
-
- HardwareCanvas createCanvas() {
- return mGlCanvas = new GLES20Canvas(mTranslucent);
- }
-
- ManagedEGLContext createManagedContext(EGLContext eglContext) {
- return new GLRendererEglContext(mEglContext);
- }
-
- int[] getConfig(boolean dirtyRegions) {
- //noinspection PointlessBooleanExpression,ConstantConditions
- final int stencilSize = GLES20Canvas.getStencilSize();
- final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
-
- return new int[] {
- EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_ALPHA_SIZE, 8,
- EGL_DEPTH_SIZE, 0,
- EGL_CONFIG_CAVEAT, EGL_NONE,
- EGL_STENCIL_SIZE, stencilSize,
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
- EGL_NONE
- };
- }
-
- void initCaches() {
- if (GLES20Canvas.initCaches()) {
- // Caches were (re)initialized, rebind atlas
- initAtlas();
- }
- }
-
- void initAtlas() {
- IBinder binder = ServiceManager.getService("assetatlas");
- if (binder == null) return;
-
- IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
- try {
- if (atlas.isCompatible(android.os.Process.myPpid())) {
- GraphicBuffer buffer = atlas.getBuffer();
- if (buffer != null) {
- long[] map = atlas.getMap();
- if (map != null) {
- GLES20Canvas.initAtlas(buffer, map);
- }
- // If IAssetAtlas is not the same class as the IBinder
- // we are using a remote service and we can safely
- // destroy the graphic buffer
- if (atlas.getClass() != binder.getClass()) {
- buffer.destroy();
- }
- }
- }
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Could not acquire atlas", e);
- }
- }
-
- boolean canDraw() {
- return mGl != null && mCanvas != null && mGlCanvas != null;
- }
-
- int onPreDraw(Rect dirty) {
- return mGlCanvas.onPreDraw(dirty);
- }
-
- void onPostDraw() {
- mGlCanvas.onPostDraw();
- }
-
- void drawProfileData(View.AttachInfo attachInfo) {
- if (mDebugDataProvider != null) {
- final GraphDataProvider provider = mDebugDataProvider;
- initProfileDrawData(attachInfo, provider);
-
- final int height = provider.getVerticalUnitSize();
- final int margin = provider.getHorizontaUnitMargin();
- final int width = provider.getHorizontalUnitSize();
-
- int x = 0;
- int count = 0;
- int current = 0;
-
- final float[] data = provider.getData();
- final int elementCount = provider.getElementCount();
- final int graphType = provider.getGraphType();
-
- int totalCount = provider.getFrameCount() * elementCount;
- if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) {
- totalCount -= elementCount;
- }
-
- for (int i = 0; i < totalCount; i += elementCount) {
- if (data[i] < 0.0f) break;
-
- int index = count * 4;
- if (i == provider.getCurrentFrame() * elementCount) current = index;
-
- x += margin;
- int x2 = x + width;
-
- int y2 = mHeight;
- int y1 = (int) (y2 - data[i] * height);
-
- switch (graphType) {
- case GraphDataProvider.GRAPH_TYPE_BARS: {
- for (int j = 0; j < elementCount; j++) {
- //noinspection MismatchedReadAndWriteOfArray
- final float[] r = mProfileShapes[j];
- r[index] = x;
- r[index + 1] = y1;
- r[index + 2] = x2;
- r[index + 3] = y2;
-
- y2 = y1;
- if (j < elementCount - 1) {
- y1 = (int) (y2 - data[i + j + 1] * height);
- }
- }
- } break;
- case GraphDataProvider.GRAPH_TYPE_LINES: {
- for (int j = 0; j < elementCount; j++) {
- //noinspection MismatchedReadAndWriteOfArray
- final float[] r = mProfileShapes[j];
- r[index] = (x + x2) * 0.5f;
- r[index + 1] = index == 0 ? y1 : r[index - 1];
- r[index + 2] = r[index] + width;
- r[index + 3] = y1;
-
- y2 = y1;
- if (j < elementCount - 1) {
- y1 = (int) (y2 - data[i + j + 1] * height);
- }
- }
- } break;
- }
-
-
- x += width;
- count++;
- }
-
- x += margin;
-
- drawGraph(graphType, count);
- drawCurrentFrame(graphType, current);
- drawThreshold(x, height);
- }
- }
-
- private void drawGraph(int graphType, int count) {
- for (int i = 0; i < mProfileShapes.length; i++) {
- mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
- switch (graphType) {
- case GraphDataProvider.GRAPH_TYPE_BARS:
- mGlCanvas.drawRects(mProfileShapes[i], count * 4, mProfilePaint);
- break;
- case GraphDataProvider.GRAPH_TYPE_LINES:
- mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);
- break;
- }
- }
- }
-
- private void drawCurrentFrame(int graphType, int index) {
- if (index >= 0) {
- mDebugDataProvider.setupCurrentFramePaint(mProfilePaint);
- switch (graphType) {
- case GraphDataProvider.GRAPH_TYPE_BARS:
- mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1],
- mProfileShapes[2][index + 2], mProfileShapes[0][index + 3],
- mProfilePaint);
- break;
- case GraphDataProvider.GRAPH_TYPE_LINES:
- mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1],
- mProfileShapes[2][index], mHeight, mProfilePaint);
- break;
- }
- }
- }
-
- private void drawThreshold(int x, int height) {
- float threshold = mDebugDataProvider.getThreshold();
- if (threshold > 0.0f) {
- mDebugDataProvider.setupThresholdPaint(mProfilePaint);
- int y = (int) (mHeight - threshold * height);
- mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
- }
- }
-
- private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) {
- if (mProfileShapes == null) {
- final int elementCount = provider.getElementCount();
- final int frameCount = provider.getFrameCount();
-
- mProfileShapes = new float[elementCount][];
- for (int i = 0; i < elementCount; i++) {
- mProfileShapes[i] = new float[frameCount * 4];
- }
-
- mProfilePaint = new Paint();
- }
-
- mProfilePaint.reset();
- if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) {
- mProfilePaint.setAntiAlias(true);
- }
-
- if (mDisplayMetrics == null) {
- mDisplayMetrics = new DisplayMetrics();
- }
-
- attachInfo.mDisplay.getMetrics(mDisplayMetrics);
- provider.prepare(mDisplayMetrics);
- }
-
- @Override
- void destroy(boolean full) {
- try {
- if (full && mCanvas != null) {
- mCanvas = null;
- }
-
- if (!isEnabled() || mDestroyed) {
- setEnabled(false);
- return;
- }
-
- destroySurface();
- setEnabled(false);
-
- mDestroyed = true;
- mGl = null;
- } finally {
- if (full && mGlCanvas != null) {
- mGlCanvas = null;
- }
- }
- }
-
- @Override
- void pushLayerUpdate(HardwareLayer layer) {
- mGlCanvas.pushLayerUpdate(layer);
- }
-
- @Override
- void flushLayerUpdates() {
- if (validate()) {
- flushLayerChanges();
- mGlCanvas.flushLayerUpdates();
- }
- }
-
- @Override
- HardwareLayer createTextureLayer() {
- validate();
- return HardwareLayer.createTextureLayer(this);
- }
-
- @Override
- public HardwareLayer createDisplayListLayer(int width, int height) {
- validate();
- return HardwareLayer.createDisplayListLayer(this, width, height);
- }
-
- @Override
- void onLayerCreated(HardwareLayer hardwareLayer) {
- mAttachedLayers.add(hardwareLayer);
- }
-
- boolean hasContext() {
- return sEgl != null && mEglContext != null
- && mEglContext.equals(sEgl.eglGetCurrentContext());
- }
-
- @Override
- void onLayerDestroyed(HardwareLayer layer) {
- if (mGlCanvas != null) {
- mGlCanvas.cancelLayerUpdate(layer);
- }
- if (hasContext()) {
- long backingLayer = layer.detachBackingLayer();
- nDestroyLayer(backingLayer);
- }
- mAttachedLayers.remove(layer);
- }
-
- @Override
- public SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
- return layer.createSurfaceTexture();
- }
-
- @Override
- boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap) {
- if (!validate()) {
- throw new IllegalStateException("Could not acquire hardware rendering context");
- }
- layer.flushChanges();
- return GLES20Canvas.nCopyLayer(layer.getLayer(), bitmap.mNativeBitmap);
- }
-
- @Override
- boolean safelyRun(Runnable action) {
- boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
-
- if (needsContext) {
- GLRendererEglContext managedContext =
- (GLRendererEglContext) sEglContextStorage.get();
- if (managedContext == null) return false;
- usePbufferSurface(managedContext.getContext());
- }
-
- try {
- action.run();
- } finally {
- if (needsContext) {
- sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
- EGL_NO_SURFACE, EGL_NO_CONTEXT);
- }
- }
-
- return true;
- }
-
- @Override
- void invokeFunctor(long functor, boolean waitForCompletion) {
- boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
- boolean hasContext = !needsContext;
-
- if (needsContext) {
- GLRendererEglContext managedContext =
- (GLRendererEglContext) sEglContextStorage.get();
- if (managedContext != null) {
- usePbufferSurface(managedContext.getContext());
- hasContext = true;
- }
- }
-
- try {
- nInvokeFunctor(functor, hasContext);
- } finally {
- if (needsContext) {
- sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
- EGL_NO_SURFACE, EGL_NO_CONTEXT);
- }
- }
- }
-
- private static native void nInvokeFunctor(long functor, boolean hasContext);
-
- @Override
- void destroyHardwareResources(final View view) {
- if (view != null) {
- safelyRun(new Runnable() {
- @Override
- public void run() {
- if (mCanvas != null) {
- mCanvas.clearLayerUpdates();
- }
- destroyResources(view);
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
- }
- });
- }
- }
-
- private static void destroyResources(View view) {
- view.destroyHardwareResources();
-
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
-
- int count = group.getChildCount();
- for (int i = 0; i < count; i++) {
- destroyResources(group.getChildAt(i));
- }
- }
- }
-
- static void startTrimMemory(int level) {
- if (sEgl == null || sEglConfig == null) return;
-
- GLRendererEglContext managedContext =
- (GLRendererEglContext) sEglContextStorage.get();
- // We do not have OpenGL objects
- if (managedContext == null) {
- return;
- } else {
- usePbufferSurface(managedContext.getContext());
- }
-
- if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
- } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
- }
- }
-
- static void endTrimMemory() {
- if (sEgl != null && sEglDisplay != null) {
- sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- }
- }
-
- private static void usePbufferSurface(EGLContext eglContext) {
- synchronized (sPbufferLock) {
- // Create a temporary 1x1 pbuffer so we have a context
- // to clear our OpenGL objects
- if (sPbuffer == null) {
- sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
- EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
- });
- }
- }
- sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
- }
-
- GLRenderer(boolean translucent) {
- mTranslucent = translucent;
-
- loadSystemProperties();
- }
-
- @Override
- void setOpaque(boolean opaque) {
- // Not supported
- }
-
- @Override
- boolean loadSystemProperties() {
- boolean value;
- boolean changed = false;
-
- String profiling = SystemProperties.get(PROFILE_PROPERTY);
- int graphType = search(VISUALIZERS, profiling);
- value = graphType >= 0;
-
- if (graphType != mProfileVisualizerType) {
- changed = true;
- mProfileVisualizerType = graphType;
-
- mProfileShapes = null;
- mProfilePaint = null;
-
- if (value) {
- mDebugDataProvider = new GraphDataProvider(graphType);
- } else {
- mDebugDataProvider = null;
- }
- }
-
- // If on-screen profiling is not enabled, we need to check whether
- // console profiling only is enabled
- if (!value) {
- value = Boolean.parseBoolean(profiling);
- }
-
- if (value != mProfileEnabled) {
- changed = true;
- mProfileEnabled = value;
-
- if (mProfileEnabled) {
- Log.d(LOG_TAG, "Profiling hardware renderer");
-
- int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY,
- PROFILE_MAX_FRAMES);
- mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
- for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
- mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
- }
-
- mProfileLock = new ReentrantLock();
- } else {
- mProfileData = null;
- mProfileLock = null;
- mProfileVisualizerType = -1;
- }
-
- mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
- }
-
- value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false);
- if (value != mDebugDirtyRegions) {
- changed = true;
- mDebugDirtyRegions = value;
-
- if (mDebugDirtyRegions) {
- Log.d(LOG_TAG, "Debugging dirty regions");
- }
- }
-
- String overdraw = SystemProperties.get(HardwareRenderer.DEBUG_OVERDRAW_PROPERTY);
- int debugOverdraw = search(OVERDRAW, overdraw);
- if (debugOverdraw != mDebugOverdraw) {
- changed = true;
- mDebugOverdraw = debugOverdraw;
- }
-
- if (loadProperties()) {
- changed = true;
- }
-
- return changed;
- }
-
- private static int search(String[] values, String value) {
- for (int i = 0; i < values.length; i++) {
- if (values[i].equals(value)) return i;
- }
- return -1;
- }
-
- @Override
- void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) {
- if (mProfileEnabled) {
- pw.printf("\n\tDraw\tProcess\tExecute\n");
-
- mProfileLock.lock();
- try {
- for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
- if (mProfileData[i] < 0) {
- break;
- }
- pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
- mProfileData[i + 2]);
- mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
- }
- mProfileCurrentFrame = mProfileData.length;
- } finally {
- mProfileLock.unlock();
- }
- }
- }
-
- /**
- * Indicates whether this renderer instance can track and update dirty regions.
- */
- boolean hasDirtyRegions() {
- return mDirtyRegionsEnabled;
- }
-
- /**
- * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
- * is invoked and the requested flag is turned off. The error code is
- * also logged as a warning.
- */
- void checkEglErrors() {
- if (isEnabled()) {
- checkEglErrorsForced();
- }
- }
-
- private void checkEglErrorsForced() {
- int error = sEgl.eglGetError();
- if (error != EGL_SUCCESS) {
- // something bad has happened revert to
- // normal rendering.
- Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error));
- fallback(error != EGL11.EGL_CONTEXT_LOST);
- }
- }
-
- private void fallback(boolean fallback) {
- destroy(true);
- if (fallback) {
- // we'll try again if it was context lost
- setRequested(false);
- Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
- + "Switching back to software rendering.");
- }
- }
-
- @Override
- boolean initialize(Surface surface) throws OutOfResourcesException {
- if (isRequested() && !isEnabled()) {
- boolean contextCreated = initializeEgl();
- mGl = createEglSurface(surface);
- mDestroyed = false;
-
- if (mGl != null) {
- int err = sEgl.eglGetError();
- if (err != EGL_SUCCESS) {
- destroy(true);
- setRequested(false);
- } else {
- if (mCanvas == null) {
- mCanvas = createCanvas();
- }
- setEnabled(true);
-
- if (contextCreated) {
- initAtlas();
- }
- }
-
- return mCanvas != null;
- }
- }
- return false;
- }
-
- @Override
- void updateSurface(Surface surface) throws OutOfResourcesException {
- if (isRequested() && isEnabled()) {
- createEglSurface(surface);
- }
- }
-
- @Override
- void pauseSurface(Surface surface) {
- // No-op
- }
-
- boolean initializeEgl() {
- synchronized (sEglLock) {
- if (sEgl == null && sEglConfig == null) {
- sEgl = (EGL10) EGLContext.getEGL();
-
- // Get to the default display.
- sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
-
- if (sEglDisplay == EGL_NO_DISPLAY) {
- throw new RuntimeException("eglGetDisplay failed "
- + GLUtils.getEGLErrorString(sEgl.eglGetError()));
- }
-
- // We can now initialize EGL for that display
- int[] version = new int[2];
- if (!sEgl.eglInitialize(sEglDisplay, version)) {
- throw new RuntimeException("eglInitialize failed " +
- GLUtils.getEGLErrorString(sEgl.eglGetError()));
- }
-
- checkEglErrorsForced();
-
- sEglConfig = loadEglConfig();
- }
- }
-
- ManagedEGLContext managedContext = sEglContextStorage.get();
- mEglContext = managedContext != null ? managedContext.getContext() : null;
- mEglThread = Thread.currentThread();
-
- if (mEglContext == null) {
- mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
- sEglContextStorage.set(createManagedContext(mEglContext));
- return true;
- }
-
- return false;
- }
-
- private EGLConfig loadEglConfig() {
- EGLConfig eglConfig = chooseEglConfig();
- if (eglConfig == null) {
- // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
- if (sDirtyRegions) {
- sDirtyRegions = false;
- eglConfig = chooseEglConfig();
- if (eglConfig == null) {
- throw new RuntimeException("eglConfig not initialized");
- }
- } else {
- throw new RuntimeException("eglConfig not initialized");
- }
- }
- return eglConfig;
- }
-
- private EGLConfig chooseEglConfig() {
- EGLConfig[] configs = new EGLConfig[1];
- int[] configsCount = new int[1];
- int[] configSpec = getConfig(sDirtyRegions);
-
- // Debug
- final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, "");
- if ("all".equalsIgnoreCase(debug)) {
- sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount);
-
- EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]];
- sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs,
- configsCount[0], configsCount);
-
- for (EGLConfig config : debugConfigs) {
- printConfig(config);
- }
- }
-
- if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
- throw new IllegalArgumentException("eglChooseConfig failed " +
- GLUtils.getEGLErrorString(sEgl.eglGetError()));
- } else if (configsCount[0] > 0) {
- if ("choice".equalsIgnoreCase(debug)) {
- printConfig(configs[0]);
- }
- return configs[0];
- }
-
- return null;
- }
-
- private static void printConfig(EGLConfig config) {
- int[] value = new int[1];
-
- Log.d(LOG_TAG, "EGL configuration " + config + ":");
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value);
- Log.d(LOG_TAG, " RED_SIZE = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value);
- Log.d(LOG_TAG, " GREEN_SIZE = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value);
- Log.d(LOG_TAG, " BLUE_SIZE = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value);
- Log.d(LOG_TAG, " ALPHA_SIZE = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value);
- Log.d(LOG_TAG, " DEPTH_SIZE = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value);
- Log.d(LOG_TAG, " STENCIL_SIZE = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLE_BUFFERS, value);
- Log.d(LOG_TAG, " SAMPLE_BUFFERS = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLES, value);
- Log.d(LOG_TAG, " SAMPLES = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value);
- Log.d(LOG_TAG, " SURFACE_TYPE = 0x" + Integer.toHexString(value[0]));
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_CONFIG_CAVEAT, value);
- Log.d(LOG_TAG, " CONFIG_CAVEAT = 0x" + Integer.toHexString(value[0]));
- }
-
- GL createEglSurface(Surface surface) throws OutOfResourcesException {
- // Check preconditions.
- if (sEgl == null) {
- throw new RuntimeException("egl not initialized");
- }
- if (sEglDisplay == null) {
- throw new RuntimeException("eglDisplay not initialized");
- }
- if (sEglConfig == null) {
- throw new RuntimeException("eglConfig not initialized");
- }
- if (Thread.currentThread() != mEglThread) {
- throw new IllegalStateException("HardwareRenderer cannot be used "
- + "from multiple threads");
- }
-
- // In case we need to destroy an existing surface
- destroySurface();
-
- // Create an EGL surface we can render into.
- if (!createSurface(surface)) {
- return null;
- }
-
- initCaches();
-
- return mEglContext.getGL();
- }
-
- private void enableDirtyRegions() {
- // If mDirtyRegions is set, this means we have an EGL configuration
- // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
- if (sDirtyRegions) {
- if (!(mDirtyRegionsEnabled = preserveBackBuffer())) {
- Log.w(LOG_TAG, "Backbuffer cannot be preserved");
- }
- } else if (sDirtyRegionsRequested) {
- // If mDirtyRegions is not set, our EGL configuration does not
- // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default
- // swap behavior might be EGL_BUFFER_PRESERVED, which means we
- // want to set mDirtyRegions. We try to do this only if dirty
- // regions were initially requested as part of the device
- // configuration (see RENDER_DIRTY_REGIONS)
- mDirtyRegionsEnabled = isBackBufferPreserved();
- }
- }
-
- EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
- final int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, GL_VERSION, EGL_NONE };
-
- EGLContext context = egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT,
- attribs);
- if (context == null || context == EGL_NO_CONTEXT) {
- //noinspection ConstantConditions
- throw new IllegalStateException(
- "Could not create an EGL context. eglCreateContext failed with error: " +
- GLUtils.getEGLErrorString(sEgl.eglGetError()));
- }
-
- return context;
- }
-
- void destroySurface() {
- if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
- if (mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) {
- sEgl.eglMakeCurrent(sEglDisplay,
- EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- }
- sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
- mEglSurface = null;
- }
- }
-
- @Override
- void invalidate(Surface surface) {
- // Cancels any existing buffer to ensure we'll get a buffer
- // of the right size before we call eglSwapBuffers
- sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
- if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
- sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
- mEglSurface = null;
- setEnabled(false);
- }
-
- if (surface.isValid()) {
- if (!createSurface(surface)) {
- return;
- }
-
- mUpdateDirtyRegions = true;
-
- if (mCanvas != null) {
- setEnabled(true);
- }
- }
- }
-
- private boolean createSurface(Surface surface) {
- mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, surface, null);
-
- if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
- int error = sEgl.eglGetError();
- if (error == EGL_BAD_NATIVE_WINDOW) {
- Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
- return false;
- }
- throw new RuntimeException("createWindowSurface failed "
- + GLUtils.getEGLErrorString(error));
- }
-
- if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
- throw new IllegalStateException("eglMakeCurrent failed " +
- GLUtils.getEGLErrorString(sEgl.eglGetError()));
- }
-
- enableDirtyRegions();
-
- return true;
- }
-
- boolean validate() {
- return checkRenderContext() != SURFACE_STATE_ERROR;
- }
-
- @Override
- void setup(int width, int height, float lightX, float lightY, float lightZ, float lightRadius) {
- if (validate()) {
- mCanvas.setViewport(width, height);
- mCanvas.initializeLight(lightX, lightY, lightZ, lightRadius);
- mWidth = width;
- mHeight = height;
- }
- }
-
- @Override
- int getWidth() {
- return mWidth;
- }
-
- @Override
- int getHeight() {
- return mHeight;
- }
-
- @Override
- void setName(String name) {
- mName = name;
- }
-
- @Override
- void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
- Rect dirty) {
- if (canDraw()) {
- if (!hasDirtyRegions()) {
- dirty = null;
- }
- attachInfo.mIgnoreDirtyState = true;
- attachInfo.mDrawingTime = SystemClock.uptimeMillis();
-
- view.mPrivateFlags |= View.PFLAG_DRAWN;
-
- // We are already on the correct thread
- final int surfaceState = checkRenderContextUnsafe();
- if (surfaceState != SURFACE_STATE_ERROR) {
- HardwareCanvas canvas = mCanvas;
-
- if (mProfileEnabled) {
- mProfileLock.lock();
- }
-
- dirty = beginFrame(canvas, dirty, surfaceState);
-
- RenderNode displayList = buildDisplayList(view, canvas);
-
- flushLayerChanges();
-
- // buildDisplayList() calls into user code which can cause
- // an eglMakeCurrent to happen with a different surface/context.
- // We must therefore check again here.
- if (checkRenderContextUnsafe() == SURFACE_STATE_ERROR) {
- return;
- }
-
- int saveCount = 0;
- int status = RenderNode.STATUS_DONE;
-
- long start = getSystemTime();
- try {
- status = prepareFrame(dirty);
-
- saveCount = canvas.save();
- callbacks.onHardwarePreDraw(canvas);
-
- if (displayList != null) {
- status |= drawDisplayList(canvas, displayList, status);
- } else {
- // Shouldn't reach here
- view.draw(canvas);
- }
- } catch (Exception e) {
- Log.e(LOG_TAG, "An error has occurred while drawing:", e);
- } finally {
- callbacks.onHardwarePostDraw(canvas);
- canvas.restoreToCount(saveCount);
- view.mRecreateDisplayList = false;
-
- mDrawDelta = getSystemTime() - start;
-
- if (mDrawDelta > 0) {
- mFrameCount++;
-
- debugDirtyRegions(dirty, canvas);
- drawProfileData(attachInfo);
- }
- }
-
- onPostDraw();
-
- swapBuffers(status);
-
- if (mProfileEnabled) {
- mProfileLock.unlock();
- }
-
- attachInfo.mIgnoreDirtyState = false;
- }
- }
- }
-
- private void flushLayerChanges() {
- // Loop through and apply any pending layer changes
- for (int i = 0; i < mAttachedLayers.size(); i++) {
- HardwareLayer layer = mAttachedLayers.get(i);
- layer.flushChanges();
- if (!layer.isValid()) {
- // The layer was removed from mAttachedLayers, rewind i by 1
- // Note that this shouldn't actually happen as View.getHardwareLayer()
- // is already flushing for error checking reasons
- i--;
- }
- }
- }
-
- @Override
- void fence() {
- // Everything is immediate, so this is a no-op
- }
-
- private RenderNode buildDisplayList(View view, HardwareCanvas canvas) {
- view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
- == View.PFLAG_INVALIDATED;
- view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
-
- long buildDisplayListStartTime = startBuildDisplayListProfiling();
- canvas.clearLayerUpdates();
-
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
- RenderNode renderNode = view.getDisplayList();
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-
- endBuildDisplayListProfiling(buildDisplayListStartTime);
-
- return renderNode;
- }
-
- private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) {
- // We had to change the current surface and/or context, redraw everything
- if (surfaceState == SURFACE_STATE_UPDATED) {
- dirty = null;
- beginFrame(null);
- } else {
- int[] size = mSurfaceSize;
- beginFrame(size);
-
- if (size[1] != mHeight || size[0] != mWidth) {
- mWidth = size[0];
- mHeight = size[1];
-
- canvas.setViewport(mWidth, mHeight);
-
- dirty = null;
- }
- }
-
- if (mDebugDataProvider != null) dirty = null;
-
- return dirty;
- }
-
- private long startBuildDisplayListProfiling() {
- if (mProfileEnabled) {
- mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT;
- if (mProfileCurrentFrame >= mProfileData.length) {
- mProfileCurrentFrame = 0;
- }
-
- return System.nanoTime();
- }
- return 0;
- }
-
- private void endBuildDisplayListProfiling(long getDisplayListStartTime) {
- if (mProfileEnabled) {
- long now = System.nanoTime();
- float total = (now - getDisplayListStartTime) * 0.000001f;
- //noinspection PointlessArithmeticExpression
- mProfileData[mProfileCurrentFrame] = total;
- }
- }
-
- private int prepareFrame(Rect dirty) {
- int status;
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame");
- try {
- status = onPreDraw(dirty);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
- return status;
- }
-
- private int drawDisplayList(HardwareCanvas canvas, RenderNode displayList,
- int status) {
-
- long drawDisplayListStartTime = 0;
- if (mProfileEnabled) {
- drawDisplayListStartTime = System.nanoTime();
- }
-
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList");
- nPrepareTree(displayList.getNativeDisplayList());
- try {
- status |= canvas.drawDisplayList(displayList, mRedrawClip,
- RenderNode.FLAG_CLIP_CHILDREN);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
-
- if (mProfileEnabled) {
- long now = System.nanoTime();
- float total = (now - drawDisplayListStartTime) * 0.000001f;
- mProfileData[mProfileCurrentFrame + 1] = total;
- }
-
- return status;
- }
-
- private void swapBuffers(int status) {
- if ((status & RenderNode.STATUS_DREW) == RenderNode.STATUS_DREW) {
- long eglSwapBuffersStartTime = 0;
- if (mProfileEnabled) {
- eglSwapBuffersStartTime = System.nanoTime();
- }
-
- sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
-
- if (mProfileEnabled) {
- long now = System.nanoTime();
- float total = (now - eglSwapBuffersStartTime) * 0.000001f;
- mProfileData[mProfileCurrentFrame + 2] = total;
- }
-
- checkEglErrors();
- }
- }
-
- private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) {
- if (mDebugDirtyRegions) {
- if (mDebugPaint == null) {
- mDebugPaint = new Paint();
- mDebugPaint.setColor(0x7fff0000);
- }
-
- if (dirty != null && (mFrameCount & 1) == 0) {
- canvas.drawRect(dirty, mDebugPaint);
- }
- }
- }
-
- /**
- * Ensures the current EGL context and surface are the ones we expect.
- * This method throws an IllegalStateException if invoked from a thread
- * that did not initialize EGL.
- *
- * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
- * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
- * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
- *
- * @see #checkRenderContextUnsafe()
- */
- int checkRenderContext() {
- if (mEglThread != Thread.currentThread()) {
- throw new IllegalStateException("Hardware acceleration can only be used with a " +
- "single UI thread.\nOriginal thread: " + mEglThread + "\n" +
- "Current thread: " + Thread.currentThread());
- }
-
- return checkRenderContextUnsafe();
- }
-
- /**
- * Ensures the current EGL context and surface are the ones we expect.
- * This method does not check the current thread.
- *
- * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
- * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
- * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
- *
- * @see #checkRenderContext()
- */
- private int checkRenderContextUnsafe() {
- if (!mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW)) ||
- !mEglContext.equals(sEgl.eglGetCurrentContext())) {
- if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
- Log.e(LOG_TAG, "eglMakeCurrent failed " +
- GLUtils.getEGLErrorString(sEgl.eglGetError()));
- fallback(true);
- return SURFACE_STATE_ERROR;
- } else {
- if (mUpdateDirtyRegions) {
- enableDirtyRegions();
- mUpdateDirtyRegions = false;
- }
- return SURFACE_STATE_UPDATED;
- }
- }
- return SURFACE_STATE_SUCCESS;
- }
-
- private static int dpToPx(int dp, float density) {
- return (int) (dp * density + 0.5f);
- }
-
- static native boolean loadProperties();
-
- static native void setupShadersDiskCache(String cacheFile);
-
- /**
- * Notifies EGL that the frame is about to be rendered.
- * @param size
- */
- static native void beginFrame(int[] size);
-
- /**
- * Returns the current system time according to the renderer.
- * This method is used for debugging only and should not be used
- * as a clock.
- */
- static native long getSystemTime();
-
- /**
- * Preserves the back buffer of the current surface after a buffer swap.
- * Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current
- * surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL
- * config that supports EGL_SWAP_BEHAVIOR_PRESERVED_BIT.
- *
- * @return True if the swap behavior was successfully changed,
- * false otherwise.
- */
- static native boolean preserveBackBuffer();
-
- /**
- * Indicates whether the current surface preserves its back buffer
- * after a buffer swap.
- *
- * @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED,
- * false otherwise
- */
- static native boolean isBackBufferPreserved();
-
- static native void nDestroyLayer(long layerPtr);
-
- private static native void nPrepareTree(long displayListPtr);
-
- class GraphDataProvider {
- /**
- * Draws the graph as bars. Frame elements are stacked on top of
- * each other.
- */
- public static final int GRAPH_TYPE_BARS = 0;
- /**
- * Draws the graph as lines. The number of series drawn corresponds
- * to the number of elements.
- */
- public static final int GRAPH_TYPE_LINES = 1;
-
- private final int mGraphType;
-
- private int mVerticalUnit;
- private int mHorizontalUnit;
- private int mHorizontalMargin;
- private int mThresholdStroke;
-
- public GraphDataProvider(int graphType) {
- mGraphType = graphType;
- }
-
- void prepare(DisplayMetrics metrics) {
- final float density = metrics.density;
-
- mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
- mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
- mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density);
- mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
- }
-
- int getGraphType() {
- return mGraphType;
- }
-
- int getVerticalUnitSize() {
- return mVerticalUnit;
- }
-
- int getHorizontalUnitSize() {
- return mHorizontalUnit;
- }
-
- int getHorizontaUnitMargin() {
- return mHorizontalMargin;
- }
-
- float[] getData() {
- return mProfileData;
- }
-
- float getThreshold() {
- return 16;
- }
-
- int getFrameCount() {
- return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
- }
-
- int getElementCount() {
- return PROFILE_FRAME_DATA_COUNT;
- }
-
- int getCurrentFrame() {
- return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
- }
-
- void setupGraphPaint(Paint paint, int elementIndex) {
- paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
- if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
- }
-
- void setupThresholdPaint(Paint paint) {
- paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
- paint.setStrokeWidth(mThresholdStroke);
- }
-
- void setupCurrentFramePaint(Paint paint) {
- paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
- if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
- }
- }
-}
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 9568760..b8e7d8c 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -110,48 +110,6 @@ public abstract class HardwareCanvas extends Canvas {
return RenderNode.STATUS_DONE;
}
- /**
- * Indicates that the specified layer must be updated as soon as possible.
- *
- * @param layer The layer to update
- *
- * @see #clearLayerUpdates()
- *
- * @hide
- */
- abstract void pushLayerUpdate(HardwareLayer layer);
-
- /**
- * Cancels a queued layer update. If the specified layer was not
- * queued for update, this method has no effect.
- *
- * @param layer The layer whose update to cancel
- *
- * @see #pushLayerUpdate(HardwareLayer)
- * @see #clearLayerUpdates()
- *
- * @hide
- */
- abstract void cancelLayerUpdate(HardwareLayer layer);
-
- /**
- * Immediately executes all enqueued layer updates.
- *
- * @see #pushLayerUpdate(HardwareLayer)
- *
- * @hide
- */
- abstract void flushLayerUpdates();
-
- /**
- * Removes all enqueued layer updates.
- *
- * @see #pushLayerUpdate(HardwareLayer)
- *
- * @hide
- */
- abstract void clearLayerUpdates();
-
public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
CanvasProperty<Float> radius, CanvasProperty<Paint> paint);
}
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index 4d78733..6acb134 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -22,6 +22,8 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
+import com.android.internal.util.VirtualRefBasePtr;
+
/**
* A hardware layer can be used to render graphics operations into a hardware
* friendly buffer. For instance, with an OpenGL backend a hardware layer
@@ -36,7 +38,7 @@ final class HardwareLayer {
private static final int LAYER_TYPE_DISPLAY_LIST = 2;
private HardwareRenderer mRenderer;
- private Finalizer mFinalizer;
+ private VirtualRefBasePtr mFinalizer;
private RenderNode mDisplayList;
private final int mLayerType;
@@ -47,10 +49,7 @@ final class HardwareLayer {
}
mRenderer = renderer;
mLayerType = type;
- mFinalizer = new Finalizer(deferredUpdater);
-
- // Layer is considered initialized at this point, notify the HardwareRenderer
- mRenderer.onLayerCreated(this);
+ mFinalizer = new VirtualRefBasePtr(deferredUpdater);
}
private void assertType(int type) {
@@ -59,6 +58,10 @@ final class HardwareLayer {
}
}
+ boolean hasDisplayList() {
+ return mDisplayList != null;
+ }
+
/**
* Update the paint used when drawing this layer.
*
@@ -66,7 +69,8 @@ final class HardwareLayer {
* @see View#setLayerPaint(android.graphics.Paint)
*/
public void setLayerPaint(Paint paint) {
- nSetLayerPaint(mFinalizer.mDeferredUpdater, paint.mNativePaint);
+ nSetLayerPaint(mFinalizer.get(), paint.mNativePaint);
+ mRenderer.pushLayerUpdate(this);
}
/**
@@ -75,7 +79,7 @@ final class HardwareLayer {
* @return True if the layer can be rendered into, false otherwise
*/
public boolean isValid() {
- return mFinalizer != null && mFinalizer.mDeferredUpdater != 0;
+ return mFinalizer != null && mFinalizer.get() != 0;
}
/**
@@ -91,35 +95,14 @@ final class HardwareLayer {
mDisplayList.destroyDisplayListData();
mDisplayList = null;
}
- if (mRenderer != null) {
- mRenderer.onLayerDestroyed(this);
- mRenderer = null;
- }
- doDestroyLayerUpdater();
+ mRenderer.onLayerDestroyed(this);
+ mRenderer = null;
+ mFinalizer.release();
+ mFinalizer = null;
}
public long getDeferredLayerUpdater() {
- return mFinalizer.mDeferredUpdater;
- }
-
- /**
- * Destroys the deferred layer updater but not the backing layer. The
- * backing layer is instead returned and is the caller's responsibility
- * to destroy/recycle as appropriate.
- *
- * It is safe to call this in onLayerDestroyed only
- */
- public long detachBackingLayer() {
- long backingLayer = nDetachBackingLayer(mFinalizer.mDeferredUpdater);
- doDestroyLayerUpdater();
- return backingLayer;
- }
-
- private void doDestroyLayerUpdater() {
- if (mFinalizer != null) {
- mFinalizer.destroy();
- mFinalizer = null;
- }
+ return mFinalizer.get();
}
public RenderNode startRecording() {
@@ -132,7 +115,7 @@ final class HardwareLayer {
}
public void endRecording(Rect dirtyRect) {
- nUpdateRenderLayer(mFinalizer.mDeferredUpdater, mDisplayList.getNativeDisplayList(),
+ nUpdateRenderLayer(mFinalizer.get(), mDisplayList.getNativeDisplayList(),
dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
mRenderer.pushLayerUpdate(this);
}
@@ -160,7 +143,7 @@ final class HardwareLayer {
* match the desired values.
*/
public boolean prepare(int width, int height, boolean isOpaque) {
- return nPrepare(mFinalizer.mDeferredUpdater, width, height, isOpaque);
+ return nPrepare(mFinalizer.get(), width, height, isOpaque);
}
/**
@@ -169,7 +152,8 @@ final class HardwareLayer {
* @param matrix The transform to apply to the layer.
*/
public void setTransform(Matrix matrix) {
- nSetTransform(mFinalizer.mDeferredUpdater, matrix.native_instance);
+ nSetTransform(mFinalizer.get(), matrix.native_instance);
+ mRenderer.pushLayerUpdate(this);
}
/**
@@ -183,41 +167,25 @@ final class HardwareLayer {
surface.detachFromGLContext();
// SurfaceTexture owns the texture name and detachFromGLContext
// should have deleted it
- nOnTextureDestroyed(mFinalizer.mDeferredUpdater);
+ nOnTextureDestroyed(mFinalizer.get());
}
});
}
- /**
- * This exists to minimize impact into the current HardwareLayer paths as
- * some of the specifics of how to handle error cases in the fully
- * deferred model will work
- */
- @Deprecated
- public void flushChanges() {
- if (HardwareRenderer.sUseRenderThread) {
- // Not supported, don't try.
- return;
- }
-
- boolean success = nFlushChanges(mFinalizer.mDeferredUpdater);
- if (!success) {
- destroy();
- }
- }
-
public long getLayer() {
- return nGetLayer(mFinalizer.mDeferredUpdater);
+ return nGetLayer(mFinalizer.get());
}
public void setSurfaceTexture(SurfaceTexture surface) {
assertType(LAYER_TYPE_TEXTURE);
- nSetSurfaceTexture(mFinalizer.mDeferredUpdater, surface, false);
+ nSetSurfaceTexture(mFinalizer.get(), surface, false);
+ mRenderer.pushLayerUpdate(this);
}
public void updateSurfaceTexture() {
assertType(LAYER_TYPE_TEXTURE);
- nUpdateSurfaceTexture(mFinalizer.mDeferredUpdater);
+ nUpdateSurfaceTexture(mFinalizer.get());
+ mRenderer.pushLayerUpdate(this);
}
/**
@@ -225,48 +193,20 @@ final class HardwareLayer {
*/
SurfaceTexture createSurfaceTexture() {
assertType(LAYER_TYPE_TEXTURE);
- SurfaceTexture st = new SurfaceTexture(nGetTexName(mFinalizer.mDeferredUpdater));
- nSetSurfaceTexture(mFinalizer.mDeferredUpdater, st, true);
+ SurfaceTexture st = new SurfaceTexture(nGetTexName(mFinalizer.get()));
+ nSetSurfaceTexture(mFinalizer.get(), st, true);
return st;
}
- /**
- * This should only be used by HardwareRenderer! Do not call directly
- */
- static HardwareLayer createTextureLayer(HardwareRenderer renderer) {
- return new HardwareLayer(renderer, nCreateTextureLayer(), LAYER_TYPE_TEXTURE);
- }
-
static HardwareLayer adoptTextureLayer(HardwareRenderer renderer, long layer) {
return new HardwareLayer(renderer, layer, LAYER_TYPE_TEXTURE);
}
- /**
- * This should only be used by HardwareRenderer! Do not call directly
- */
- static HardwareLayer createDisplayListLayer(HardwareRenderer renderer,
- int width, int height) {
- return new HardwareLayer(renderer, nCreateRenderLayer(width, height), LAYER_TYPE_DISPLAY_LIST);
- }
-
static HardwareLayer adoptDisplayListLayer(HardwareRenderer renderer, long layer) {
return new HardwareLayer(renderer, layer, LAYER_TYPE_DISPLAY_LIST);
}
- /** This also creates the underlying layer */
- private static native long nCreateTextureLayer();
- private static native long nCreateRenderLayer(int width, int height);
-
private static native void nOnTextureDestroyed(long layerUpdater);
- private static native long nDetachBackingLayer(long layerUpdater);
-
- /** This also destroys the underlying layer if it is still attached.
- * Note it does not recycle the underlying layer, but instead queues it
- * for deferred deletion.
- * The HardwareRenderer should use detachBackingLayer() in the
- * onLayerDestroyed() callback to do recycling if desired.
- */
- private static native void nDestroyLayerUpdater(long layerUpdater);
private static native boolean nPrepare(long layerUpdater, int width, int height, boolean isOpaque);
private static native void nSetLayerPaint(long layerUpdater, long paint);
@@ -281,28 +221,4 @@ final class HardwareLayer {
private static native long nGetLayer(long layerUpdater);
private static native int nGetTexName(long layerUpdater);
-
- private static class Finalizer {
- private long mDeferredUpdater;
-
- public Finalizer(long deferredUpdater) {
- mDeferredUpdater = deferredUpdater;
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- destroy();
- } finally {
- super.finalize();
- }
- }
-
- void destroy() {
- if (mDeferredUpdater != 0) {
- nDestroyLayerUpdater(mDeferredUpdater);
- mDeferredUpdater = 0;
- }
- }
- }
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 60f8ee3..d67c974 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -171,9 +171,6 @@ public abstract class HardwareRenderer {
*/
public static boolean sSystemRendererDisabled = false;
- /** @hide */
- public static boolean sUseRenderThread = true;
-
private boolean mEnabled;
private boolean mRequested = true;
@@ -309,7 +306,7 @@ public abstract class HardwareRenderer {
* @hide
*/
public static void setupDiskCache(File cacheDir) {
- GLRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
+ ThreadedRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
}
/**
@@ -323,12 +320,6 @@ public abstract class HardwareRenderer {
abstract void pushLayerUpdate(HardwareLayer layer);
/**
- * Tells the HardwareRenderer that a layer was created. The renderer should
- * make sure to apply any pending layer changes at the start of a new frame
- */
- abstract void onLayerCreated(HardwareLayer hardwareLayer);
-
- /**
* Tells the HardwareRenderer that the layer is destroyed. The renderer
* should remove the layer from any update queues.
*/
@@ -475,11 +466,7 @@ public abstract class HardwareRenderer {
static HardwareRenderer create(boolean translucent) {
HardwareRenderer renderer = null;
if (GLES20Canvas.isAvailable()) {
- if (sUseRenderThread) {
- renderer = new ThreadedRenderer(translucent);
- } else {
- renderer = new GLRenderer(translucent);
- }
+ renderer = new ThreadedRenderer(translucent);
}
return renderer;
}
@@ -506,7 +493,7 @@ public abstract class HardwareRenderer {
* see {@link android.content.ComponentCallbacks}
*/
static void startTrimMemory(int level) {
- GLRenderer.startTrimMemory(level);
+ ThreadedRenderer.startTrimMemory(level);
}
/**
@@ -514,7 +501,7 @@ public abstract class HardwareRenderer {
* cleanup special resources used by the memory trimming process.
*/
static void endTrimMemory() {
- GLRenderer.endTrimMemory();
+ ThreadedRenderer.endTrimMemory();
}
/**
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index e918119..4979059 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -219,6 +219,15 @@ public final class RenderNodeAnimator extends Animator {
return mTarget;
}
+ /**
+ * WARNING: May only be called once!!!
+ * TODO: Fix above -_-
+ */
+ public void setStartValue(float startValue) {
+ checkMutable();
+ nSetStartValue(mNativePtr.get(), startValue);
+ }
+
@Override
public void setStartDelay(long startDelay) {
checkMutable();
@@ -282,11 +291,12 @@ public final class RenderNodeAnimator extends Animator {
}
private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis,
- int property, float deltaValue);
+ int property, float finalValue);
private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis,
- long canvasProperty, float deltaValue);
+ long canvasProperty, float finalValue);
private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis,
- long canvasProperty, int paintField, float deltaValue);
+ long canvasProperty, int paintField, float finalValue);
+ private static native void nSetStartValue(long nativePtr, float startValue);
private static native void nSetDuration(long nativePtr, long duration);
private static native long nGetDuration(long nativePtr);
private static native void nSetStartDelay(long nativePtr, long startDelay);
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index cac23a8..9b3ef7f 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -294,12 +294,7 @@ public class ThreadedRenderer extends HardwareRenderer {
@Override
void pushLayerUpdate(HardwareLayer layer) {
- // TODO: Remove this, it's not needed outside of GLRenderer
- }
-
- @Override
- void onLayerCreated(HardwareLayer layer) {
- // TODO: Is this actually useful?
+ nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
}
@Override
@@ -309,7 +304,7 @@ public class ThreadedRenderer extends HardwareRenderer {
@Override
void onLayerDestroyed(HardwareLayer layer) {
- nDestroyLayer(mNativeProxy, layer.getDeferredLayerUpdater());
+ nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
}
@Override
@@ -336,6 +331,14 @@ public class ThreadedRenderer extends HardwareRenderer {
}
}
+ static void startTrimMemory(int level) {
+ // TODO
+ }
+
+ static void endTrimMemory() {
+ // TODO
+ }
+
private static class AtlasInitializer {
static AtlasInitializer sInstance = new AtlasInitializer();
@@ -372,6 +375,8 @@ public class ThreadedRenderer extends HardwareRenderer {
}
}
+ static native void setupShadersDiskCache(String cacheFile);
+
private static native void nSetAtlas(GraphicBuffer buffer, long[] map);
private static native long nCreateRootRenderNode();
@@ -398,7 +403,8 @@ public class ThreadedRenderer extends HardwareRenderer {
private static native long nCreateDisplayListLayer(long nativeProxy, int width, int height);
private static native long nCreateTextureLayer(long nativeProxy);
private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
- private static native void nDestroyLayer(long nativeProxy, long layer);
+ private static native void nPushLayerUpdate(long nativeProxy, long layer);
+ private static native void nCancelLayerUpdate(long nativeProxy, long layer);
private static native void nFlushCaches(long nativeProxy, int flushMode);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ce266d7..b500e46 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13593,12 +13593,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
- // The layer is not valid if the underlying GPU resources cannot be allocated
- mHardwareLayer.flushChanges();
- if (!mHardwareLayer.isValid()) {
- return null;
- }
-
mHardwareLayer.setLayerPaint(mLayerPaint);
RenderNode displayList = mHardwareLayer.startRecording();
updateDisplayListIfDirty(displayList, true);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ac25b57..f3d1e3c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -716,17 +716,6 @@ public final class ViewRootImpl implements ViewParent,
if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled
&& forceHwAccelerated)) {
- if (!HardwareRenderer.sUseRenderThread) {
- // TODO: Delete
- // Don't enable hardware acceleration when we're not on the main thread
- if (!HardwareRenderer.sSystemRendererDisabled &&
- Looper.getMainLooper() != Looper.myLooper()) {
- Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware "
- + "acceleration outside of the main thread, aborting");
- return;
- }
- }
-
if (mAttachInfo.mHardwareRenderer != null) {
mAttachInfo.mHardwareRenderer.destroy(true);
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index d45d686..2b4677c 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1199,6 +1199,9 @@ public interface WindowManagerPolicy {
/**
* Notifies the keyguard to start fading out.
+ *
+ * @param startTime the start time of the animation in uptime milliseconds
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
- public void startKeyguardExitAnimation(long fadeoutDuration);
+ public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index c9eb130..9a46052 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2495,17 +2495,25 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
/**
- * Positions the selector in a way that mimics keyboard focus. If the
- * selector drawable supports hotspots, this manages the focus hotspot.
+ * Positions the selector in a way that mimics keyboard focus.
*/
void positionSelectorLikeFocus(int position, View sel) {
+ // If we're changing position, update the visibility since the selector
+ // is technically being detached from the previous selection.
+ final Drawable selector = mSelector;
+ final boolean manageState = selector != null && mSelectorPosition != position
+ && position != INVALID_POSITION;
+ if (manageState) {
+ selector.setVisible(false, false);
+ }
+
positionSelector(position, sel);
- final Drawable selector = mSelector;
- if (selector != null && position != INVALID_POSITION) {
+ if (manageState) {
final Rect bounds = mSelectorRect;
final float x = bounds.exactCenterX();
final float y = bounds.exactCenterY();
+ selector.setVisible(getVisibility() == VISIBLE, false);
selector.setHotspot(x, y);
}
}
@@ -2520,8 +2528,18 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (sel instanceof SelectionBoundsAdjuster) {
((SelectionBoundsAdjuster)sel).adjustListItemSelectionBounds(selectorRect);
}
- positionSelector(selectorRect.left, selectorRect.top, selectorRect.right,
- selectorRect.bottom);
+
+ // Adjust for selection padding.
+ selectorRect.left -= mSelectionLeftPadding;
+ selectorRect.top -= mSelectionTopPadding;
+ selectorRect.right += mSelectionRightPadding;
+ selectorRect.bottom += mSelectionBottomPadding;
+
+ // Update the selector drawable.
+ final Drawable selector = mSelector;
+ if (selector != null) {
+ selector.setBounds(selectorRect);
+ }
final boolean isChildViewEnabled = mIsChildViewEnabled;
if (sel.isEnabled() != isChildViewEnabled) {
@@ -2532,11 +2550,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
- private void positionSelector(int l, int t, int r, int b) {
- mSelectorRect.set(l - mSelectionLeftPadding, t - mSelectionTopPadding, r
- + mSelectionRightPadding, b + mSelectionBottomPadding);
- }
-
@Override
protected void dispatchDraw(Canvas canvas) {
int saveCount = 0;
diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java
index a9a5eae..acee592 100644
--- a/core/java/android/widget/ActionMenuView.java
+++ b/core/java/android/widget/ActionMenuView.java
@@ -570,9 +570,9 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
mMenu = new MenuBuilder(context);
mMenu.setCallback(new MenuBuilderCallback());
mPresenter = new ActionMenuPresenter(context);
- mPresenter.setMenuView(this);
mPresenter.setCallback(new ActionMenuPresenterCallback());
mMenu.addMenuPresenter(mPresenter);
+ mPresenter.setMenuView(this);
}
return mMenu;
@@ -652,6 +652,11 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
return false;
}
+ /** @hide */
+ public void setExpandedActionViewsExclusive(boolean exclusive) {
+ mPresenter.setExpandedActionViewsExclusive(exclusive);
+ }
+
/**
* Interface responsible for receiving menu item click events if the items themselves
* do not have individual item click listeners.
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 265dbcd..2c1a77c 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -24,6 +24,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.text.InputType;
+import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
@@ -814,8 +815,7 @@ public class DatePicker extends FrameLayout {
mSpinners.removeAllViews();
// We use numeric spinners for year and day, but textual months. Ask icu4c what
// order the user's locale uses for that combination. http://b/7207103.
- String pattern = ICU.getBestDateTimePattern("yyyyMMMdd",
- Locale.getDefault().toString());
+ String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd");
char[] order = ICU.getDateFormatOrder(pattern);
final int spinnerCount = order.length;
for (int i = 0; i < spinnerCount; i++) {
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 5033bee..419c582 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -127,6 +127,8 @@ public class Toolbar extends ViewGroup {
// Clear me after use.
private final ArrayList<View> mTempViews = new ArrayList<View>();
+ private final int[] mTempMargins = new int[2];
+
private OnMenuItemClickListener mOnMenuItemClickListener;
private final ActionMenuView.OnMenuItemClickListener mMenuViewItemClickListener =
@@ -220,7 +222,7 @@ public class Toolbar extends ViewGroup {
final CharSequence subtitle = a.getText(R.styleable.Toolbar_subtitle);
if (!TextUtils.isEmpty(subtitle)) {
- setSubtitle(title);
+ setSubtitle(subtitle);
}
a.recycle();
}
@@ -557,6 +559,28 @@ public class Toolbar extends ViewGroup {
}
/**
+ * Sets the text color, size, style, hint color, and highlight color
+ * from the specified TextAppearance resource.
+ */
+ public void setTitleTextAppearance(Context context, int resId) {
+ mTitleTextAppearance = resId;
+ if (mTitleTextView != null) {
+ mTitleTextView.setTextAppearance(context, resId);
+ }
+ }
+
+ /**
+ * Sets the text color, size, style, hint color, and highlight color
+ * from the specified TextAppearance resource.
+ */
+ public void setSubtitleTextAppearance(Context context, int resId) {
+ mSubtitleTextAppearance = resId;
+ if (mSubtitleTextView != null) {
+ mSubtitleTextView.setTextAppearance(context, resId);
+ }
+ }
+
+ /**
* Set the icon to use for the toolbar's navigation button.
*
* <p>The navigation button appears at the start of the toolbar if present. Setting an icon
@@ -681,10 +705,23 @@ public class Toolbar extends ViewGroup {
* @return The toolbar's Menu
*/
public Menu getMenu() {
- ensureMenuView();
+ ensureMenu();
return mMenuView.getMenu();
}
+ private void ensureMenu() {
+ ensureMenuView();
+ if (mMenuView.peekMenu() == null) {
+ // Initialize a new menu for the first time.
+ final MenuBuilder menu = (MenuBuilder) mMenuView.getMenu();
+ if (mExpandedMenuPresenter == null) {
+ mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
+ }
+ mMenuView.setExpandedActionViewsExclusive(true);
+ menu.addMenuPresenter(mExpandedMenuPresenter);
+ }
+ }
+
private void ensureMenuView() {
if (mMenuView == null) {
mMenuView = new ActionMenuView(getContext());
@@ -906,12 +943,49 @@ public class Toolbar extends ViewGroup {
child.measure(childWidthSpec, childHeightSpec);
}
+ /**
+ * Returns the width + uncollapsed margins
+ */
+ private int measureChildCollapseMargins(View child,
+ int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed, int[] collapsingMargins) {
+ final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+
+ final int leftDiff = lp.leftMargin - collapsingMargins[0];
+ final int rightDiff = lp.rightMargin - collapsingMargins[1];
+ final int leftMargin = Math.max(0, leftDiff);
+ final int rightMargin = Math.max(0, rightDiff);
+ final int hMargins = leftMargin + rightMargin;
+ collapsingMargins[0] = Math.max(0, -leftDiff);
+ collapsingMargins[1] = Math.max(0, -rightDiff);
+
+ final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
+ mPaddingLeft + mPaddingRight + hMargins + widthUsed, lp.width);
+ final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
+ mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ + heightUsed, lp.height);
+
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ return child.getMeasuredWidth() + hMargins;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = 0;
int height = 0;
int childState = 0;
+ final int[] collapsingMargins = mTempMargins;
+ final int marginStartIndex;
+ final int marginEndIndex;
+ if (isLayoutRtl()) {
+ marginStartIndex = 1;
+ marginEndIndex = 0;
+ } else {
+ marginStartIndex = 0;
+ marginEndIndex = 1;
+ }
+
// System views measure first.
int navWidth = 0;
@@ -934,7 +1008,9 @@ public class Toolbar extends ViewGroup {
childState = combineMeasuredStates(childState, mCollapseButtonView.getMeasuredState());
}
- width += Math.max(getContentInsetStart(), navWidth);
+ final int contentInsetStart = getContentInsetStart();
+ width += Math.max(contentInsetStart, navWidth);
+ collapsingMargins[marginStartIndex] = Math.max(0, contentInsetStart - navWidth);
int menuWidth = 0;
if (shouldLayout(mMenuView)) {
@@ -946,21 +1022,21 @@ public class Toolbar extends ViewGroup {
childState = combineMeasuredStates(childState, mMenuView.getMeasuredState());
}
- width += Math.max(getContentInsetEnd(), menuWidth);
+ final int contentInsetEnd = getContentInsetEnd();
+ width += Math.max(contentInsetEnd, menuWidth);
+ collapsingMargins[marginEndIndex] = Math.max(0, contentInsetEnd - menuWidth);
if (shouldLayout(mExpandedActionView)) {
- measureChildWithMargins(mExpandedActionView, widthMeasureSpec, width,
- heightMeasureSpec, 0);
- width += mExpandedActionView.getMeasuredWidth() +
- getHorizontalMargins(mExpandedActionView);
+ width += measureChildCollapseMargins(mExpandedActionView, widthMeasureSpec, width,
+ heightMeasureSpec, 0, collapsingMargins);
height = Math.max(height, mExpandedActionView.getMeasuredHeight() +
getVerticalMargins(mExpandedActionView));
childState = combineMeasuredStates(childState, mExpandedActionView.getMeasuredState());
}
if (shouldLayout(mLogoView)) {
- measureChildWithMargins(mLogoView, widthMeasureSpec, width, heightMeasureSpec, 0);
- width += mLogoView.getMeasuredWidth() + getHorizontalMargins(mLogoView);
+ width += measureChildCollapseMargins(mLogoView, widthMeasureSpec, width,
+ heightMeasureSpec, 0, collapsingMargins);
height = Math.max(height, mLogoView.getMeasuredHeight() +
getVerticalMargins(mLogoView));
childState = combineMeasuredStates(childState, mLogoView.getMeasuredState());
@@ -971,17 +1047,18 @@ public class Toolbar extends ViewGroup {
final int titleVertMargins = mTitleMarginTop + mTitleMarginBottom;
final int titleHorizMargins = mTitleMarginStart + mTitleMarginEnd;
if (shouldLayout(mTitleTextView)) {
- measureChildWithMargins(mTitleTextView, widthMeasureSpec, width + titleHorizMargins,
- heightMeasureSpec, titleVertMargins);
+ titleWidth = measureChildCollapseMargins(mTitleTextView, widthMeasureSpec,
+ width + titleHorizMargins, heightMeasureSpec, titleVertMargins,
+ collapsingMargins);
titleWidth = mTitleTextView.getMeasuredWidth() + getHorizontalMargins(mTitleTextView);
titleHeight = mTitleTextView.getMeasuredHeight() + getVerticalMargins(mTitleTextView);
childState = combineMeasuredStates(childState, mTitleTextView.getMeasuredState());
}
if (shouldLayout(mSubtitleTextView)) {
- measureChildWithMargins(mSubtitleTextView, widthMeasureSpec, width + titleHorizMargins,
- heightMeasureSpec, titleHeight + titleVertMargins);
- titleWidth = Math.max(titleWidth, mSubtitleTextView.getMeasuredWidth() +
- getHorizontalMargins(mSubtitleTextView));
+ titleWidth = Math.max(titleWidth, measureChildCollapseMargins(mSubtitleTextView,
+ widthMeasureSpec, width + titleHorizMargins,
+ heightMeasureSpec, titleHeight + titleVertMargins,
+ collapsingMargins));
titleHeight += mSubtitleTextView.getMeasuredHeight() +
getVerticalMargins(mSubtitleTextView);
childState = combineMeasuredStates(childState, mSubtitleTextView.getMeasuredState());
@@ -999,8 +1076,8 @@ public class Toolbar extends ViewGroup {
continue;
}
- measureChildWithMargins(child, widthMeasureSpec, width, heightMeasureSpec, 0);
- width += child.getMeasuredWidth() + getHorizontalMargins(child);
+ width += measureChildCollapseMargins(child, widthMeasureSpec, width,
+ heightMeasureSpec, 0, collapsingMargins);
height = Math.max(height, child.getMeasuredHeight() + getVerticalMargins(child));
childState = combineMeasuredStates(childState, child.getMeasuredState());
}
@@ -1031,46 +1108,51 @@ public class Toolbar extends ViewGroup {
int left = paddingLeft;
int right = width - paddingRight;
+ final int[] collapsingMargins = mTempMargins;
+ collapsingMargins[0] = collapsingMargins[1] = 0;
+
if (shouldLayout(mNavButtonView)) {
if (isRtl) {
- right = layoutChildRight(mNavButtonView, right);
+ right = layoutChildRight(mNavButtonView, right, collapsingMargins);
} else {
- left = layoutChildLeft(mNavButtonView, left);
+ left = layoutChildLeft(mNavButtonView, left, collapsingMargins);
}
}
if (shouldLayout(mCollapseButtonView)) {
if (isRtl) {
- right = layoutChildRight(mCollapseButtonView, right);
+ right = layoutChildRight(mCollapseButtonView, right, collapsingMargins);
} else {
- left = layoutChildLeft(mCollapseButtonView, left);
+ left = layoutChildLeft(mCollapseButtonView, left, collapsingMargins);
}
}
if (shouldLayout(mMenuView)) {
if (isRtl) {
- left = layoutChildLeft(mMenuView, left);
+ left = layoutChildLeft(mMenuView, left, collapsingMargins);
} else {
- right = layoutChildRight(mMenuView, right);
+ right = layoutChildRight(mMenuView, right, collapsingMargins);
}
}
+ collapsingMargins[0] = Math.max(0, getContentInsetLeft() - left);
+ collapsingMargins[1] = Math.max(0, getContentInsetRight() - (width - paddingRight - right));
left = Math.max(left, getContentInsetLeft());
right = Math.min(right, width - paddingRight - getContentInsetRight());
if (shouldLayout(mExpandedActionView)) {
if (isRtl) {
- right = layoutChildRight(mExpandedActionView, right);
+ right = layoutChildRight(mExpandedActionView, right, collapsingMargins);
} else {
- left = layoutChildLeft(mExpandedActionView, left);
+ left = layoutChildLeft(mExpandedActionView, left, collapsingMargins);
}
}
if (shouldLayout(mLogoView)) {
if (isRtl) {
- right = layoutChildRight(mLogoView, right);
+ right = layoutChildRight(mLogoView, right, collapsingMargins);
} else {
- left = layoutChildLeft(mLogoView, left);
+ left = layoutChildLeft(mLogoView, left, collapsingMargins);
}
}
@@ -1119,48 +1201,52 @@ public class Toolbar extends ViewGroup {
break;
}
if (isRtl) {
+ final int rd = mTitleMarginStart - collapsingMargins[1];
+ right -= Math.max(0, rd);
+ collapsingMargins[1] = Math.max(0, -rd);
int titleRight = right;
int subtitleRight = right;
+
if (layoutTitle) {
final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
- titleRight -= lp.rightMargin + mTitleMarginStart;
final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth();
final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
- titleRight = titleLeft - lp.leftMargin - mTitleMarginEnd;
+ titleRight = titleLeft - mTitleMarginEnd;
titleTop = titleBottom + lp.bottomMargin;
}
if (layoutSubtitle) {
final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
- subtitleRight -= lp.rightMargin + mTitleMarginStart;
titleTop += lp.topMargin;
final int subtitleLeft = subtitleRight - mSubtitleTextView.getMeasuredWidth();
final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
- subtitleRight = subtitleRight - lp.leftMargin - mTitleMarginEnd;
+ subtitleRight = subtitleRight - mTitleMarginEnd;
titleTop = subtitleBottom + lp.bottomMargin;
}
right = Math.max(titleRight, subtitleRight);
} else {
+ final int ld = mTitleMarginStart - collapsingMargins[0];
+ left += Math.max(0, ld);
+ collapsingMargins[0] = Math.max(0, -ld);
int titleLeft = left;
int subtitleLeft = left;
+
if (layoutTitle) {
final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
- titleLeft += lp.leftMargin + mTitleMarginStart;
final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth();
final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
- titleLeft = titleRight + lp.rightMargin + mTitleMarginEnd;
+ titleLeft = titleRight + mTitleMarginEnd;
titleTop = titleBottom + lp.bottomMargin;
}
if (layoutSubtitle) {
final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
- subtitleLeft += lp.leftMargin + mTitleMarginStart;
titleTop += lp.topMargin;
final int subtitleRight = subtitleLeft + mSubtitleTextView.getMeasuredWidth();
final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
- subtitleLeft = subtitleRight + lp.rightMargin + mTitleMarginEnd;
+ subtitleLeft = subtitleRight + mTitleMarginEnd;
titleTop = subtitleBottom + lp.bottomMargin;
}
left = Math.max(titleLeft, subtitleLeft);
@@ -1173,19 +1259,19 @@ public class Toolbar extends ViewGroup {
addCustomViewsWithGravity(mTempViews, Gravity.LEFT);
final int leftViewsCount = mTempViews.size();
for (int i = 0; i < leftViewsCount; i++) {
- left = layoutChildLeft(mTempViews.get(i), left);
+ left = layoutChildLeft(mTempViews.get(i), left, collapsingMargins);
}
addCustomViewsWithGravity(mTempViews, Gravity.RIGHT);
final int rightViewsCount = mTempViews.size();
for (int i = 0; i < rightViewsCount; i++) {
- right = layoutChildRight(mTempViews.get(i), right);
+ right = layoutChildRight(mTempViews.get(i), right, collapsingMargins);
}
// Centered views try to center with respect to the whole bar, but views pinned
// to the left or right can push the mass of centered views to one side or the other.
addCustomViewsWithGravity(mTempViews, Gravity.CENTER_HORIZONTAL);
- final int centerViewsWidth = getViewListMeasuredWidth(mTempViews);
+ final int centerViewsWidth = getViewListMeasuredWidth(mTempViews, collapsingMargins);
final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2;
final int halfCenterViewsWidth = centerViewsWidth / 2;
int centerLeft = parentCenter - halfCenterViewsWidth;
@@ -1198,25 +1284,35 @@ public class Toolbar extends ViewGroup {
final int centerViewsCount = mTempViews.size();
for (int i = 0; i < centerViewsCount; i++) {
- centerLeft = layoutChildLeft(mTempViews.get(i), centerLeft);
+ centerLeft = layoutChildLeft(mTempViews.get(i), centerLeft, collapsingMargins);
}
mTempViews.clear();
}
- private int getViewListMeasuredWidth(List<View> views) {
+ private int getViewListMeasuredWidth(List<View> views, int[] collapsingMargins) {
+ int collapseLeft = collapsingMargins[0];
+ int collapseRight = collapsingMargins[1];
int width = 0;
final int count = views.size();
for (int i = 0; i < count; i++) {
final View v = views.get(i);
final LayoutParams lp = (LayoutParams) v.getLayoutParams();
- width += lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin;
+ final int l = lp.leftMargin - collapseLeft;
+ final int r = lp.rightMargin - collapseRight;
+ final int leftMargin = Math.max(0, l);
+ final int rightMargin = Math.max(0, r);
+ collapseLeft = Math.max(0, -l);
+ collapseRight = Math.max(0, -r);
+ width += leftMargin + v.getMeasuredWidth() + rightMargin;
}
return width;
}
- private int layoutChildLeft(View child, int left) {
+ private int layoutChildLeft(View child, int left, int[] collapsingMargins) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- left += lp.leftMargin;
+ final int l = lp.leftMargin - collapsingMargins[0];
+ left += Math.max(0, l);
+ collapsingMargins[0] = Math.max(0, -l);
final int top = getChildTop(child);
final int childWidth = child.getMeasuredWidth();
child.layout(left, top, left + childWidth, top + child.getMeasuredHeight());
@@ -1224,9 +1320,11 @@ public class Toolbar extends ViewGroup {
return left;
}
- private int layoutChildRight(View child, int right) {
+ private int layoutChildRight(View child, int right, int[] collapsingMargins) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- right -= lp.rightMargin;
+ final int r = lp.rightMargin - collapsingMargins[1];
+ right -= Math.max(0, r);
+ collapsingMargins[1] = Math.max(0, -r);
final int top = getChildTop(child);
final int childWidth = child.getMeasuredWidth();
child.layout(right - childWidth, top, right, top + child.getMeasuredHeight());
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index 03d3b22..77f0dec 100644
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -25,16 +25,18 @@ import android.content.res.ObbInfo;
interface IMediaContainerService {
String copyResourceToContainer(in Uri packageURI, String containerId, String key,
String resFileName, String publicResFileName, boolean isExternal,
- boolean isForwardLocked);
+ boolean isForwardLocked, in String abiOverride);
int copyResource(in Uri packageURI, in ContainerEncryptionParams encryptionParams,
in ParcelFileDescriptor outStream);
- PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold);
+ PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold,
+ in String abiOverride);
boolean checkInternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in long threshold);
- boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked);
+ boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in String abiOverride);
ObbInfo getObbInfo(in String filename);
long calculateDirectorySize(in String directory);
/** Return file system stats: [0] is total bytes, [1] is available bytes */
long[] getFileSystemStats(in String path);
void clearDirectory(in String directory);
- long calculateInstalledSize(in String packagePath, boolean isForwardLocked);
+ long calculateInstalledSize(in String packagePath, boolean isForwardLocked,
+ in String abiOverride);
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractor.aidl b/core/java/com/android/internal/app/IVoiceInteractor.aidl
index 737906a..2900595 100644
--- a/core/java/com/android/internal/app/IVoiceInteractor.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractor.aidl
@@ -26,7 +26,9 @@ import com.android.internal.app.IVoiceInteractorRequest;
*/
interface IVoiceInteractor {
IVoiceInteractorRequest startConfirmation(String callingPackage,
- IVoiceInteractorCallback callback, String prompt, in Bundle extras);
+ IVoiceInteractorCallback callback, CharSequence prompt, in Bundle extras);
+ IVoiceInteractorRequest startAbortVoice(String callingPackage,
+ IVoiceInteractorCallback callback, CharSequence message, in Bundle extras);
IVoiceInteractorRequest startCommand(String callingPackage,
IVoiceInteractorCallback callback, String command, in Bundle extras);
boolean[] supportsCommands(String callingPackage, in String[] commands);
diff --git a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
index c6f93e1..8dbf9d4 100644
--- a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
@@ -26,6 +26,7 @@ import com.android.internal.app.IVoiceInteractorRequest;
oneway interface IVoiceInteractorCallback {
void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed,
in Bundle result);
+ void deliverAbortVoiceResult(IVoiceInteractorRequest request, in Bundle result);
void deliverCommandResult(IVoiceInteractorRequest request, boolean complete, in Bundle result);
void deliverCancel(IVoiceInteractorRequest request);
}
diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java
index afb6f7c..6056bf2 100644
--- a/core/java/com/android/internal/app/ToolbarActionBar.java
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -23,37 +23,50 @@ import android.content.Context;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.view.ActionMode;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.View;
+import android.view.Window;
import android.widget.SpinnerAdapter;
import android.widget.Toolbar;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.widget.DecorToolbar;
+import com.android.internal.widget.ToolbarWidgetWrapper;
import java.util.ArrayList;
-import java.util.Map;
public class ToolbarActionBar extends ActionBar {
private Toolbar mToolbar;
- private View mCustomView;
-
- private int mDisplayOptions;
-
- private int mNavResId;
- private int mIconResId;
- private int mLogoResId;
- private Drawable mNavDrawable;
- private Drawable mIconDrawable;
- private Drawable mLogoDrawable;
- private int mTitleResId;
- private int mSubtitleResId;
- private CharSequence mTitle;
- private CharSequence mSubtitle;
+ private DecorToolbar mDecorToolbar;
+ private Window.Callback mWindowCallback;
private boolean mLastMenuVisibility;
private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
new ArrayList<OnMenuVisibilityListener>();
- public ToolbarActionBar(Toolbar toolbar) {
+ private final Runnable mMenuInvalidator = new Runnable() {
+ @Override
+ public void run() {
+ populateOptionsMenu();
+ }
+ };
+
+ private final Toolbar.OnMenuItemClickListener mMenuClicker =
+ new Toolbar.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ return mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
+ }
+ };
+
+ public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback windowCallback) {
mToolbar = toolbar;
+ mDecorToolbar = new ToolbarWidgetWrapper(toolbar);
+ mWindowCallback = windowCallback;
+ toolbar.setOnMenuItemClickListener(mMenuClicker);
+ mDecorToolbar.setWindowTitle(title);
}
@Override
@@ -63,19 +76,8 @@ public class ToolbarActionBar extends ActionBar {
@Override
public void setCustomView(View view, LayoutParams layoutParams) {
- if (mCustomView != null) {
- mToolbar.removeView(mCustomView);
- }
- mCustomView = view;
- if (view != null) {
- mToolbar.addView(view, generateLayoutParams(layoutParams));
- }
- }
-
- private Toolbar.LayoutParams generateLayoutParams(LayoutParams lp) {
- final Toolbar.LayoutParams result = new Toolbar.LayoutParams(lp);
- result.gravity = lp.gravity;
- return result;
+ view.setLayoutParams(layoutParams);
+ mDecorToolbar.setCustomView(view);
}
@Override
@@ -86,48 +88,22 @@ public class ToolbarActionBar extends ActionBar {
@Override
public void setIcon(int resId) {
- mIconResId = resId;
- mIconDrawable = null;
- updateToolbarLogo();
+ mDecorToolbar.setIcon(resId);
}
@Override
public void setIcon(Drawable icon) {
- mIconResId = 0;
- mIconDrawable = icon;
- updateToolbarLogo();
+ mDecorToolbar.setIcon(icon);
}
@Override
public void setLogo(int resId) {
- mLogoResId = resId;
- mLogoDrawable = null;
- updateToolbarLogo();
+ mDecorToolbar.setLogo(resId);
}
@Override
public void setLogo(Drawable logo) {
- mLogoResId = 0;
- mLogoDrawable = logo;
- updateToolbarLogo();
- }
-
- private void updateToolbarLogo() {
- Drawable drawable = null;
- if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) {
- final int resId;
- if ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) {
- resId = mLogoResId;
- drawable = mLogoDrawable;
- } else {
- resId = mIconResId;
- drawable = mIconDrawable;
- }
- if (resId != 0) {
- drawable = mToolbar.getContext().getDrawable(resId);
- }
- }
- mToolbar.setLogo(drawable);
+ mDecorToolbar.setLogo(logo);
}
@Override
@@ -219,42 +195,22 @@ public class ToolbarActionBar extends ActionBar {
@Override
public void setTitle(CharSequence title) {
- mTitle = title;
- mTitleResId = 0;
- updateToolbarTitle();
+ mDecorToolbar.setTitle(title);
}
@Override
public void setTitle(int resId) {
- mTitleResId = resId;
- mTitle = null;
- updateToolbarTitle();
+ mDecorToolbar.setTitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null);
}
@Override
public void setSubtitle(CharSequence subtitle) {
- mSubtitle = subtitle;
- mSubtitleResId = 0;
- updateToolbarTitle();
+ mDecorToolbar.setSubtitle(subtitle);
}
@Override
public void setSubtitle(int resId) {
- mSubtitleResId = resId;
- mSubtitle = null;
- updateToolbarTitle();
- }
-
- private void updateToolbarTitle() {
- final Context context = mToolbar.getContext();
- CharSequence title = null;
- CharSequence subtitle = null;
- if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
- title = mTitleResId != 0 ? context.getText(mTitleResId) : mTitle;
- subtitle = mSubtitleResId != 0 ? context.getText(mSubtitleResId) : mSubtitle;
- }
- mToolbar.setTitle(title);
- mToolbar.setSubtitle(subtitle);
+ mDecorToolbar.setSubtitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null);
}
@Override
@@ -264,9 +220,8 @@ public class ToolbarActionBar extends ActionBar {
@Override
public void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask) {
- final int oldOptions = mDisplayOptions;
- mDisplayOptions = (options & mask) | (mDisplayOptions & ~mask);
- final int optionsChanged = oldOptions ^ mDisplayOptions;
+ mDecorToolbar.setDisplayOptions((options & mask) |
+ mDecorToolbar.getDisplayOptions() & ~mask);
}
@Override
@@ -301,7 +256,7 @@ public class ToolbarActionBar extends ActionBar {
@Override
public View getCustomView() {
- return mCustomView;
+ return mDecorToolbar.getCustomView();
}
@Override
@@ -327,7 +282,7 @@ public class ToolbarActionBar extends ActionBar {
@Override
public int getDisplayOptions() {
- return mDisplayOptions;
+ return mDecorToolbar.getDisplayOptions();
}
@Override
@@ -425,6 +380,54 @@ public class ToolbarActionBar extends ActionBar {
return mToolbar.getVisibility() == View.VISIBLE;
}
+ @Override
+ public boolean openOptionsMenu() {
+ return mToolbar.showOverflowMenu();
+ }
+
+ @Override
+ public boolean invalidateOptionsMenu() {
+ mToolbar.removeCallbacks(mMenuInvalidator);
+ mToolbar.postOnAnimation(mMenuInvalidator);
+ return true;
+ }
+
+ @Override
+ public boolean collapseActionView() {
+ if (mToolbar.hasExpandedActionView()) {
+ mToolbar.collapseActionView();
+ return true;
+ }
+ return false;
+ }
+
+ void populateOptionsMenu() {
+ final Menu menu = mToolbar.getMenu();
+ final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null;
+ if (mb != null) {
+ mb.stopDispatchingItemsChanged();
+ }
+ try {
+ menu.clear();
+ if (!mWindowCallback.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu) ||
+ !mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) {
+ menu.clear();
+ }
+ } finally {
+ if (mb != null) {
+ mb.startDispatchingItemsChanged();
+ }
+ }
+ }
+
+ @Override
+ public boolean onMenuKeyEvent(KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ openOptionsMenu();
+ }
+ return true;
+ }
+
public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
mMenuVisibilityListeners.add(listener);
}
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index 5c7a4e6..c0b5b97 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -588,7 +588,7 @@ public class WindowDecorActionBar extends ActionBar implements
return;
}
- final FragmentTransaction trans = ((View) mDecorToolbar).isInEditMode() ? null :
+ final FragmentTransaction trans = mDecorToolbar.getViewGroup().isInEditMode() ? null :
mActivity.getFragmentManager().beginTransaction().disallowAddToBackStack();
if (mSelectedTab == tab) {
@@ -847,7 +847,7 @@ public class WindowDecorActionBar extends ActionBar implements
mDecorToolbar.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE);
if (mTabScrollView != null && !mDecorToolbar.hasEmbeddedTabs() &&
- isCollapsed((View) mDecorToolbar)) {
+ isCollapsed(mDecorToolbar.getViewGroup())) {
mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
}
}
@@ -959,7 +959,7 @@ public class WindowDecorActionBar extends ActionBar implements
// Clear out the context mode views after the animation finishes
mContextView.closeMode();
- ((View) mDecorToolbar).sendAccessibilityEvent(
+ mDecorToolbar.getViewGroup().sendAccessibilityEvent(
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
mOverlayLayout.setHideOnContentScrollEnabled(mHideOnContentScroll);
diff --git a/core/java/com/android/internal/backup/BackupConstants.java b/core/java/com/android/internal/backup/BackupConstants.java
deleted file mode 100644
index 4c276b7..0000000
--- a/core/java/com/android/internal/backup/BackupConstants.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2009 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.internal.backup;
-
-/**
- * Constants used internally between the backup manager and its transports
- */
-public class BackupConstants {
- public static final int TRANSPORT_OK = 0;
- public static final int TRANSPORT_ERROR = 1;
- public static final int TRANSPORT_NOT_INITIALIZED = 2;
- public static final int AGENT_ERROR = 3;
- public static final int AGENT_UNKNOWN = 4;
-}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index f2b29ef..7292116 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -104,7 +104,7 @@ public class LocalTransport extends BackupTransport {
public int initializeDevice() {
if (DEBUG) Log.v(TAG, "wiping all data");
deleteContents(mCurrentSetDir);
- return BackupConstants.TRANSPORT_OK;
+ return BackupTransport.TRANSPORT_OK;
}
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
@@ -166,7 +166,7 @@ public class LocalTransport extends BackupTransport {
entity.write(buf, 0, dataSize);
} catch (IOException e) {
Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
- return BackupConstants.TRANSPORT_ERROR;
+ return BackupTransport.TRANSPORT_ERROR;
} finally {
entity.close();
}
@@ -174,11 +174,11 @@ public class LocalTransport extends BackupTransport {
entityFile.delete();
}
}
- return BackupConstants.TRANSPORT_OK;
+ return BackupTransport.TRANSPORT_OK;
} catch (IOException e) {
// oops, something went wrong. abort the operation and return error.
Log.v(TAG, "Exception reading backup input:", e);
- return BackupConstants.TRANSPORT_ERROR;
+ return BackupTransport.TRANSPORT_ERROR;
}
}
@@ -208,12 +208,12 @@ public class LocalTransport extends BackupTransport {
}
packageDir.delete();
}
- return BackupConstants.TRANSPORT_OK;
+ return BackupTransport.TRANSPORT_OK;
}
public int finishBackup() {
if (DEBUG) Log.v(TAG, "finishBackup()");
- return BackupConstants.TRANSPORT_OK;
+ return BackupTransport.TRANSPORT_OK;
}
// Restore handling
@@ -249,7 +249,7 @@ public class LocalTransport extends BackupTransport {
mRestorePackage = -1;
mRestoreToken = token;
mRestoreDataDir = new File(mDataDir, Long.toString(token));
- return BackupConstants.TRANSPORT_OK;
+ return BackupTransport.TRANSPORT_OK;
}
public String nextRestorePackage() {
@@ -281,7 +281,7 @@ public class LocalTransport extends BackupTransport {
ArrayList<DecodedFilename> blobs = contentsByKey(packageDir);
if (blobs == null) { // nextRestorePackage() ensures the dir exists, so this is an error
Log.e(TAG, "No keys for package: " + packageDir);
- return BackupConstants.TRANSPORT_ERROR;
+ return BackupTransport.TRANSPORT_ERROR;
}
// We expect at least some data if the directory exists in the first place
@@ -302,10 +302,10 @@ public class LocalTransport extends BackupTransport {
in.close();
}
}
- return BackupConstants.TRANSPORT_OK;
+ return BackupTransport.TRANSPORT_OK;
} catch (IOException e) {
Log.e(TAG, "Unable to read backup records", e);
- return BackupConstants.TRANSPORT_ERROR;
+ return BackupTransport.TRANSPORT_ERROR;
}
}
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index ba419f9..dab3aff 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -20,6 +20,7 @@ import android.content.pm.PackageManager;
import android.util.Slog;
import java.io.File;
+import java.io.IOException;
/**
* Native libraries helper.
@@ -141,4 +142,18 @@ public class NativeLibraryHelper {
return deletedFiles;
}
+
+ // We don't care about the other return values for now.
+ private static final int BITCODE_PRESENT = 1;
+
+ public static boolean hasRenderscriptBitcode(ApkHandle handle) throws IOException {
+ final int returnVal = hasRenderscriptBitcode(handle.apkHandle);
+ if (returnVal < 0) {
+ throw new IOException("Error scanning APK, code: " + returnVal);
+ }
+
+ return (returnVal == BITCODE_PRESENT);
+ }
+
+ private static native int hasRenderscriptBitcode(long apkHandle);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index df96488..7dbde69 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -34,6 +34,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.TreeMap;
/**
@@ -117,6 +118,24 @@ public class InputMethodSubtypeSwitchingController {
+ " mIsSystemLanguage=" + mIsSystemLanguage
+ "}";
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof ImeSubtypeListItem) {
+ final ImeSubtypeListItem that = (ImeSubtypeListItem)o;
+ if (!Objects.equals(this.mImi, that.mImi)) {
+ return false;
+ }
+ if (this.mSubtypeId != that.mSubtypeId) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
}
private static class InputMethodAndSubtypeList {
@@ -276,7 +295,7 @@ public class InputMethodSubtypeSwitchingController {
private final List<ImeSubtypeListItem> mImeSubtypeList;
private final int[] mUsageHistoryOfSubtypeListItemIndex;
- public DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) {
+ private DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) {
mImeSubtypeList = imeSubtypeListItems;
mUsageHistoryOfSubtypeListItemIndex = new int[mImeSubtypeList.size()];
final int N = mImeSubtypeList.size();
@@ -347,15 +366,53 @@ public class InputMethodSubtypeSwitchingController {
@VisibleForTesting
public static class ControllerImpl {
- // TODO: Switch to DynamicRotationList for smarter rotation.
- private final StaticRotationList mSwitchingAwareSubtypeList;
- private final StaticRotationList mSwitchingUnawareSubtypeList;
-
- public ControllerImpl(final List<ImeSubtypeListItem> sortedItems) {
- mSwitchingAwareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
- true /* supportsSwitchingToNextInputMethod */));
- mSwitchingUnawareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
- false /* supportsSwitchingToNextInputMethod */));
+ private final DynamicRotationList mSwitchingAwareRotationList;
+ private final StaticRotationList mSwitchingUnawareRotationList;
+
+ public static ControllerImpl createFrom(final ControllerImpl currentInstance,
+ final List<ImeSubtypeListItem> sortedEnabledItems) {
+ DynamicRotationList switchingAwareRotationList = null;
+ {
+ final List<ImeSubtypeListItem> switchingAwareImeSubtypes =
+ filterImeSubtypeList(sortedEnabledItems,
+ true /* supportsSwitchingToNextInputMethod */);
+ if (currentInstance != null &&
+ currentInstance.mSwitchingAwareRotationList != null &&
+ Objects.equals(currentInstance.mSwitchingAwareRotationList.mImeSubtypeList,
+ switchingAwareImeSubtypes)) {
+ // Can reuse the current instance.
+ switchingAwareRotationList = currentInstance.mSwitchingAwareRotationList;
+ }
+ if (switchingAwareRotationList == null) {
+ switchingAwareRotationList = new DynamicRotationList(switchingAwareImeSubtypes);
+ }
+ }
+
+ StaticRotationList switchingUnawareRotationList = null;
+ {
+ final List<ImeSubtypeListItem> switchingUnawareImeSubtypes = filterImeSubtypeList(
+ sortedEnabledItems, false /* supportsSwitchingToNextInputMethod */);
+ if (currentInstance != null &&
+ currentInstance.mSwitchingUnawareRotationList != null &&
+ Objects.equals(
+ currentInstance.mSwitchingUnawareRotationList.mImeSubtypeList,
+ switchingUnawareImeSubtypes)) {
+ // Can reuse the current instance.
+ switchingUnawareRotationList = currentInstance.mSwitchingUnawareRotationList;
+ }
+ if (switchingUnawareRotationList == null) {
+ switchingUnawareRotationList =
+ new StaticRotationList(switchingUnawareImeSubtypes);
+ }
+ }
+
+ return new ControllerImpl(switchingAwareRotationList, switchingUnawareRotationList);
+ }
+
+ private ControllerImpl(final DynamicRotationList switchingAwareRotationList,
+ final StaticRotationList switchingUnawareRotationList) {
+ mSwitchingAwareRotationList = switchingAwareRotationList;
+ mSwitchingUnawareRotationList = switchingUnawareRotationList;
}
public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi,
@@ -364,10 +421,10 @@ public class InputMethodSubtypeSwitchingController {
return null;
}
if (imi.supportsSwitchingToNextInputMethod()) {
- return mSwitchingAwareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi,
+ return mSwitchingAwareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
subtype);
} else {
- return mSwitchingUnawareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi,
+ return mSwitchingUnawareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
subtype);
}
}
@@ -376,10 +433,9 @@ public class InputMethodSubtypeSwitchingController {
if (imi == null) {
return;
}
- // TODO: Enable the following code when DynamicRotationList is enabled.
- // if (imi.supportsSwitchingToNextInputMethod()) {
- // mSwitchingAwareSubtypeList.onUserAction(imi, subtype);
- // }
+ if (imi.supportsSwitchingToNextInputMethod()) {
+ mSwitchingAwareRotationList.onUserAction(imi, subtype);
+ }
}
private static List<ImeSubtypeListItem> filterImeSubtypeList(
@@ -424,7 +480,8 @@ public class InputMethodSubtypeSwitchingController {
public void resetCircularListLocked(Context context) {
mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
- mController = new ControllerImpl(mSubtypeList.getSortedInputMethodAndSubtypeList());
+ mController = ControllerImpl.createFrom(mController,
+ mSubtypeList.getSortedInputMethodAndSubtypeList());
}
public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index 40834ba..17685fd 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -22,9 +22,6 @@ import android.os.Looper;
import android.os.Message;
public class HandlerCaller {
-
- public final Context mContext;
-
final Looper mMainLooper;
final Handler mH;
@@ -47,7 +44,6 @@ public class HandlerCaller {
public HandlerCaller(Context context, Looper looper, Callback callback,
boolean asyncHandler) {
- mContext = context;
mMainLooper = looper != null ? looper : context.getMainLooper();
mH = new MyHandler(mMainLooper, asyncHandler);
mCallback = callback;
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index f22800c..a5421f5 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -60,6 +60,9 @@ interface IKeyguardService {
/**
* Notifies that the activity behind has now been drawn and it's safe to remove the wallpaper
* and keyguard flag.
+ *
+ * @param startTime the start time of the animation in uptime milliseconds
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
- oneway void startKeyguardExitAnimation(long fadeoutDuration);
+ oneway void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index a56fa36..d66ef83 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -169,6 +169,15 @@ public class ArrayUtils
return false;
}
+ public static boolean contains(long[] array, long value) {
+ for (long element : array) {
+ if (element == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public static long total(long[] array) {
long total = 0;
for (long value : array) {
@@ -229,6 +238,14 @@ public class ArrayUtils
return array;
}
+ /**
+ * Appends a new value to a copy of the array and returns the copy. If
+ * the value is already present, the original array is returned
+ * @param cur The original array, or null to represent an empty array.
+ * @param val The value to add.
+ * @return A new array that contains all of the values of the original array
+ * with the new value added, or the original array.
+ */
public static int[] appendInt(int[] cur, int val) {
if (cur == null) {
return new int[] { val };
@@ -264,4 +281,48 @@ public class ArrayUtils
}
return cur;
}
+
+ /**
+ * Appends a new value to a copy of the array and returns the copy. If
+ * the value is already present, the original array is returned
+ * @param cur The original array, or null to represent an empty array.
+ * @param val The value to add.
+ * @return A new array that contains all of the values of the original array
+ * with the new value added, or the original array.
+ */
+ public static long[] appendLong(long[] cur, long val) {
+ if (cur == null) {
+ return new long[] { val };
+ }
+ final int N = cur.length;
+ for (int i = 0; i < N; i++) {
+ if (cur[i] == val) {
+ return cur;
+ }
+ }
+ long[] ret = new long[N + 1];
+ System.arraycopy(cur, 0, ret, 0, N);
+ ret[N] = val;
+ return ret;
+ }
+
+ public static long[] removeLong(long[] cur, long val) {
+ if (cur == null) {
+ return null;
+ }
+ final int N = cur.length;
+ for (int i = 0; i < N; i++) {
+ if (cur[i] == val) {
+ long[] ret = new long[N - 1];
+ if (i > 0) {
+ System.arraycopy(cur, 0, ret, 0, i);
+ }
+ if (i < (N - 1)) {
+ System.arraycopy(cur, i + 1, ret, i, N - i - 1);
+ }
+ return ret;
+ }
+ }
+ return cur;
+ }
}
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index f6722a6..c0d1e88 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -183,6 +183,33 @@ public class Preconditions {
}
/**
+ * Ensures that the argument int value is within the inclusive range.
+ *
+ * @param value a int value
+ * @param lower the lower endpoint of the inclusive range
+ * @param upper the upper endpoint of the inclusive range
+ * @param valueName the name of the argument to use if the check fails
+ *
+ * @return the validated int value
+ *
+ * @throws IllegalArgumentException if {@code value} was not within the range
+ */
+ public static int checkArgumentInRange(int value, int lower, int upper,
+ String valueName) {
+ if (value < lower) {
+ throw new IllegalArgumentException(
+ String.format(
+ "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
+ } else if (value > upper) {
+ throw new IllegalArgumentException(
+ String.format(
+ "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
+ }
+
+ return value;
+ }
+
+ /**
* Ensures that the array is not {@code null}, and none if its elements are {@code null}.
*
* @param value an array of boxed objects
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 81e67d8..af966b1 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -57,9 +57,9 @@ public class Protocol {
public static final int BASE_DNS_PINGER = 0x00050000;
public static final int BASE_NSD_MANAGER = 0x00060000;
public static final int BASE_NETWORK_STATE_TRACKER = 0x00070000;
- public static final int BASE_CONNECTIVITY_SERVICE = 0x00080000;
+ public static final int BASE_CONNECTIVITY_MANAGER = 0x00080000;
public static final int BASE_NETWORK_AGENT = 0x00081000;
public static final int BASE_NETWORK_MONITOR = 0x00082000;
- public static final int BASE_CONNECTIVITY_MANAGER = 0x00083000;
+ public static final int BASE_NETWORK_FACTORY = 0x00083000;
//TODO: define all used protocols
}
diff --git a/core/java/com/android/internal/util/VirtualRefBasePtr.java b/core/java/com/android/internal/util/VirtualRefBasePtr.java
index 0bd4d3a..52306f1 100644
--- a/core/java/com/android/internal/util/VirtualRefBasePtr.java
+++ b/core/java/com/android/internal/util/VirtualRefBasePtr.java
@@ -32,11 +32,17 @@ public final class VirtualRefBasePtr {
return mNativePtr;
}
+ public void release() {
+ if (mNativePtr != 0) {
+ nDecStrong(mNativePtr);
+ mNativePtr = 0;
+ }
+ }
+
@Override
protected void finalize() throws Throwable {
try {
- nDecStrong(mNativePtr);
- mNativePtr = 0;
+ release();
} finally {
super.finalize();
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 25e3463..d31c5cc 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -43,7 +43,6 @@ import android.view.View;
import android.widget.Button;
import com.android.internal.R;
-import com.android.internal.telephony.ITelephony;
import com.google.android.collect.Lists;
import java.security.MessageDigest;
@@ -1360,19 +1359,11 @@ public class LockPatternUtils {
/**
* Resumes a call in progress. Typically launched from the EmergencyCall button
* on various lockscreens.
- *
- * @return true if we were able to tell InCallScreen to show.
*/
- public boolean resumeCall() {
- ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
- try {
- if (phone != null && phone.showCallScreen()) {
- return true;
- }
- } catch (RemoteException e) {
- // What can we do?
- }
- return false;
+ public void resumeCall() {
+ TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ telephonyManager.showCallScreen();
}
private void finishBiometricWeak() {
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 36ed344..d841d53 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -57,6 +57,7 @@ public class LockPatternView extends View {
private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h)
private static final boolean PROFILE_DRAWING = false;
+ private final CellState[][] mCellStates;
private boolean mDrawingProfilingStarted = false;
private Paint mPaint = new Paint();
@@ -187,6 +188,12 @@ public class LockPatternView extends View {
}
}
+ public static class CellState {
+ public float scale = 1.0f;
+ public float translateY = 0.0f;
+ public float alpha = 1.0f;
+ }
+
/**
* How to display the current pattern.
*/
@@ -296,6 +303,18 @@ public class LockPatternView extends View {
mBitmapHeight = Math.max(mBitmapHeight, bitmap.getHeight());
}
+ mPaint.setFilterBitmap(true);
+
+ mCellStates = new CellState[3][3];
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ mCellStates[i][j] = new CellState();
+ }
+ }
+ }
+
+ public CellState[][] getCellStates() {
+ return mCellStates;
}
private Bitmap getBitmapFor(int resId) {
@@ -873,18 +892,22 @@ public class LockPatternView extends View {
//float centerY = mPaddingTop + i * mSquareHeight + (mSquareHeight / 2);
for (int j = 0; j < 3; j++) {
float leftX = paddingLeft + j * squareWidth;
- drawCircle(canvas, (int) leftX, (int) topY, drawLookup[i][j]);
+ float scale = mCellStates[i][j].scale;
+ mPaint.setAlpha((int) (mCellStates[i][j].alpha * 255));
+ float translationY = mCellStates[i][j].translateY;
+ drawCircle(canvas, (int) leftX, (int) topY + translationY, scale, drawLookup[i][j]);
}
}
+ // Reset the alpha to draw normally
+ mPaint.setAlpha(255);
+
// TODO: the path should be created and cached every time we hit-detect a cell
// only the last segment of the path should be computed here
// draw the path of the pattern (unless we are in stealth mode)
final boolean drawPath = !mInStealthMode;
// draw the arrows associated with the path (unless we are in stealth mode)
- boolean oldFlag = (mPaint.getFlags() & Paint.FILTER_BITMAP_FLAG) != 0;
- mPaint.setFilterBitmap(true); // draw with higher quality since we render with transforms
if (drawPath) {
for (int i = 0; i < count - 1; i++) {
Cell cell = pattern.get(i);
@@ -898,7 +921,8 @@ public class LockPatternView extends View {
}
float leftX = paddingLeft + cell.column * squareWidth;
- float topY = paddingTop + cell.row * squareHeight;
+ float topY = paddingTop + cell.row * squareHeight
+ + mCellStates[cell.row][cell.column].translateY;
drawArrow(canvas, leftX, topY, cell, next);
}
@@ -919,6 +943,9 @@ public class LockPatternView extends View {
float centerX = getCenterXForColumn(cell.column);
float centerY = getCenterYForRow(cell.row);
+
+ // Respect translation in animation
+ centerY += mCellStates[cell.row][cell.column].translateY;
if (i == 0) {
currentPath.moveTo(centerX, centerY);
} else {
@@ -933,8 +960,6 @@ public class LockPatternView extends View {
}
canvas.drawPath(currentPath, mPathPaint);
}
-
- mPaint.setFilterBitmap(oldFlag); // restore default flag
}
private void drawArrow(Canvas canvas, float leftX, float topY, Cell start, Cell end) {
@@ -979,7 +1004,8 @@ public class LockPatternView extends View {
* @param topY
* @param partOfPattern Whether this circle is part of the pattern.
*/
- private void drawCircle(Canvas canvas, int leftX, int topY, boolean partOfPattern) {
+ private void drawCircle(Canvas canvas, float leftX, float topY, float scale,
+ boolean partOfPattern) {
Bitmap outerCircle;
Bitmap innerCircle;
@@ -1019,7 +1045,7 @@ public class LockPatternView extends View {
mCircleMatrix.setTranslate(leftX + offsetX, topY + offsetY);
mCircleMatrix.preTranslate(mBitmapWidth/2, mBitmapHeight/2);
- mCircleMatrix.preScale(sx, sy);
+ mCircleMatrix.preScale(sx * scale, sy * scale);
mCircleMatrix.preTranslate(-mBitmapWidth/2, -mBitmapHeight/2);
canvas.drawBitmap(outerCircle, mCircleMatrix, mPaint);
diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
index f90aaea..3e15c32 100644
--- a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
+++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
@@ -80,16 +81,20 @@ public class ToolbarWidgetWrapper implements DecorToolbar {
public ToolbarWidgetWrapper(Toolbar toolbar) {
mToolbar = toolbar;
+ mTitle = toolbar.getTitle();
+ mSubtitle = toolbar.getSubtitle();
+ mTitleSet = !TextUtils.isEmpty(mTitle);
+
final TypedArray a = toolbar.getContext().obtainStyledAttributes(null,
R.styleable.ActionBar, R.attr.actionBarStyle, 0);
final CharSequence title = a.getText(R.styleable.ActionBar_title);
- if (title != null) {
+ if (!TextUtils.isEmpty(title)) {
setTitle(title);
}
final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle);
- if (subtitle != null) {
+ if (!TextUtils.isEmpty(subtitle)) {
setSubtitle(subtitle);
}
@@ -132,6 +137,16 @@ public class ToolbarWidgetWrapper implements DecorToolbar {
mToolbar.setContentInsetsRelative(contentInsetStart, contentInsetEnd);
}
+ final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
+ if (titleTextStyle != 0) {
+ mToolbar.setTitleTextAppearance(mToolbar.getContext(), titleTextStyle);
+ }
+
+ final int subtitleTextStyle = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0);
+ if (subtitleTextStyle != 0) {
+ mToolbar.setSubtitleTextAppearance(mToolbar.getContext(), subtitleTextStyle);
+ }
+
a.recycle();
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index f446c3a..a1cd7f7 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -57,7 +57,6 @@ LOCAL_SRC_FILES:= \
android_view_KeyEvent.cpp \
android_view_KeyCharacterMap.cpp \
android_view_GraphicBuffer.cpp \
- android_view_GLRenderer.cpp \
android_view_GLES20Canvas.cpp \
android_view_HardwareLayer.cpp \
android_view_ThreadedRenderer.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index e069876..e0c5e96 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -130,7 +130,6 @@ extern int register_android_view_RenderNode(JNIEnv* env);
extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
extern int register_android_view_GraphicBuffer(JNIEnv* env);
extern int register_android_view_GLES20Canvas(JNIEnv* env);
-extern int register_android_view_GLRenderer(JNIEnv* env);
extern int register_android_view_HardwareLayer(JNIEnv* env);
extern int register_android_view_ThreadedRenderer(JNIEnv* env);
extern int register_android_view_Surface(JNIEnv* env);
@@ -1214,7 +1213,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_view_RenderNodeAnimator),
REG_JNI(register_android_view_GraphicBuffer),
REG_JNI(register_android_view_GLES20Canvas),
- REG_JNI(register_android_view_GLRenderer),
REG_JNI(register_android_view_HardwareLayer),
REG_JNI(register_android_view_ThreadedRenderer),
REG_JNI(register_android_view_Surface),
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index ef57e3d..d17f46c 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -3,6 +3,8 @@
#include "SkCamera.h"
+#include "GraphicsJNI.h"
+
static jfieldID gNativeInstanceFieldID;
static void Camera_constructor(JNIEnv* env, jobject obj) {
@@ -93,7 +95,7 @@ static void Camera_getMatrix(JNIEnv* env, jobject obj, jlong matrixHandle) {
}
static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
- SkCanvas* native_canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* native_canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
v->applyToCanvas((SkCanvas*)native_canvas);
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index f7acbd7..5fca582 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -42,22 +42,26 @@
#include <utils/Log.h>
-static uint32_t get_thread_msec() {
-#if defined(HAVE_POSIX_CLOCKS)
- struct timespec tm;
+namespace android {
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
+// Holds an SkCanvas reference plus additional native data.
+class NativeCanvasWrapper {
+public:
+ NativeCanvasWrapper(SkCanvas* canvas)
+ : mCanvas(canvas) { }
- return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000;
-#else
- struct timeval tv;
+ SkCanvas* getCanvas() const {
+ return mCanvas.get();
+ }
- gettimeofday(&tv, NULL);
- return tv.tv_sec * 1000LL + tv.tv_usec / 1000;
-#endif
-}
+ void setCanvas(SkCanvas* canvas) {
+ SkASSERT(canvas);
+ mCanvas.reset(canvas);
+ }
-namespace android {
+private:
+ SkAutoTUnref<SkCanvas> mCanvas;
+};
class ClipCopier : public SkCanvas::ClipVisitor {
public:
@@ -86,27 +90,30 @@ static jboolean hasNonEmptyClip(const SkCanvas& canvas) {
class SkCanvasGlue {
public:
- static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
- canvas->unref();
+ // Get the SkCanvas for a given native handle.
+ static inline SkCanvas* getNativeCanvas(jlong nativeHandle) {
+ SkASSERT(nativeHandle);
+ NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
+ SkCanvas* canvas = wrapper->getCanvas();
+ SkASSERT(canvas);
+
+ return canvas;
}
- static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ // Construct an SkCanvas from the bitmap.
+ static SkCanvas* createCanvas(SkBitmap* bitmap) {
if (bitmap) {
- return reinterpret_cast<jlong>(new SkCanvas(*bitmap));
- } else {
- // Create an empty bitmap device to prevent callers from crashing
- // if they attempt to draw into this canvas.
- SkBitmap emptyBitmap;
- return reinterpret_cast<jlong>(new SkCanvas(emptyBitmap));
+ return SkNEW_ARGS(SkCanvas, (*bitmap));
}
+
+ // Create an empty bitmap device to prevent callers from crashing
+ // if they attempt to draw into this canvas.
+ SkBitmap emptyBitmap;
+ return new SkCanvas(emptyBitmap);
}
- static void copyCanvasState(JNIEnv* env, jobject clazz,
- jlong srcCanvasHandle, jlong dstCanvasHandle) {
- SkCanvas* srcCanvas = reinterpret_cast<SkCanvas*>(srcCanvasHandle);
- SkCanvas* dstCanvas = reinterpret_cast<SkCanvas*>(dstCanvasHandle);
+ // Copy the canvas matrix & clip state.
+ static void copyCanvasState(SkCanvas* srcCanvas, SkCanvas* dstCanvas) {
if (srcCanvas && dstCanvas) {
dstCanvas->setMatrix(srcCanvas->getTotalMatrix());
if (NULL != srcCanvas->getDevice() && NULL != dstCanvas->getDevice()) {
@@ -116,6 +123,42 @@ public:
}
}
+ // Native JNI handlers
+ static void finalizer(JNIEnv* env, jobject clazz, jlong nativeHandle) {
+ NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
+ delete wrapper;
+ }
+
+ // Native wrapper constructor used by Canvas(Bitmap)
+ static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
+ // No check - 0 is a valid bitmapHandle.
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkCanvas* canvas = createCanvas(bitmap);
+
+ return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
+ }
+
+ // Native wrapper constructor used by Canvas(native_canvas)
+ static jlong initCanvas(JNIEnv* env, jobject, jlong canvasHandle) {
+ SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
+ }
+
+ // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
+ // optionally copying canvas matrix & clip state.
+ static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+ jboolean copyState) {
+ NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(canvasHandle);
+ SkCanvas* newCanvas = createCanvas(reinterpret_cast<SkBitmap*>(bitmapHandle));
+ NPE_CHECK_RETURN_VOID(env, newCanvas);
+
+ if (copyState == JNI_TRUE) {
+ copyCanvasState(wrapper->getCanvas(), newCanvas);
+ }
+
+ // setCanvas() unrefs the old canvas.
+ wrapper->setCanvas(newCanvas);
+ }
static void freeCaches(JNIEnv* env, jobject) {
// these are called in no particular order
@@ -163,7 +206,7 @@ public:
static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds,
jlong paintHandle, jint flags) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
SkRect* bounds_ = NULL;
SkRect storage;
@@ -177,7 +220,7 @@ public:
static jint saveLayer4F(JNIEnv* env, jobject, jlong canvasHandle,
jfloat l, jfloat t, jfloat r, jfloat b,
jlong paintHandle, jint flags) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
SkRect bounds;
bounds.set(l, t, r, b);
@@ -188,7 +231,7 @@ public:
static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle,
jobject bounds, jint alpha, jint flags) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkRect* bounds_ = NULL;
SkRect storage;
if (bounds != NULL) {
@@ -203,7 +246,7 @@ public:
static jint saveLayerAlpha4F(JNIEnv* env, jobject, jlong canvasHandle,
jfloat l, jfloat t, jfloat r, jfloat b,
jint alpha, jint flags) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkRect bounds;
bounds.set(l, t, r, b);
int result = canvas->saveLayerAlpha(&bounds, alpha,
@@ -259,14 +302,14 @@ public:
static void concat(JNIEnv* env, jobject, jlong canvasHandle,
jlong matrixHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
canvas->concat(*matrix);
}
static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle,
jlong matrixHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
if (NULL == matrix) {
canvas->resetMatrix();
@@ -318,8 +361,8 @@ public:
static jboolean clipRect(JNIEnv* env, jobject, jlong canvasHandle,
jfloat left, jfloat top, jfloat right, jfloat bottom,
jint op) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkRect rect;
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
rect.set(left, top, right, bottom);
canvas->clipRect(rect, static_cast<SkRegion::Op>(op));
return hasNonEmptyClip(*canvas);
@@ -327,7 +370,7 @@ public:
static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle,
jlong pathHandle, jint op) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
canvas->clipPath(*reinterpret_cast<SkPath*>(pathHandle),
static_cast<SkRegion::Op>(op));
return hasNonEmptyClip(*canvas);
@@ -335,7 +378,7 @@ public:
static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle,
jlong deviceRgnHandle, jint op) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
canvas->clipRegion(*deviceRgn, static_cast<SkRegion::Op>(op));
return hasNonEmptyClip(*canvas);
@@ -343,13 +386,13 @@ public:
static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle,
jlong filterHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
canvas->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
}
static jboolean quickReject__RectF(JNIEnv* env, jobject, jlong canvasHandle,
jobject rect) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkRect rect_;
GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
bool result = canvas->quickReject(rect_);
@@ -358,7 +401,7 @@ public:
static jboolean quickReject__Path(JNIEnv* env, jobject, jlong canvasHandle,
jlong pathHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
bool result = canvas->quickReject(*reinterpret_cast<SkPath*>(pathHandle));
return result ? JNI_TRUE : JNI_FALSE;
}
@@ -366,7 +409,7 @@ public:
static jboolean quickReject__FFFF(JNIEnv* env, jobject, jlong canvasHandle,
jfloat left, jfloat top, jfloat right,
jfloat bottom) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkRect r;
r.set(left, top, right, bottom);
bool result = canvas->quickReject(r);
@@ -375,32 +418,32 @@ public:
static void drawRGB(JNIEnv* env, jobject, jlong canvasHandle,
jint r, jint g, jint b) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
canvas->drawARGB(0xFF, r, g, b);
}
static void drawARGB(JNIEnv* env, jobject, jlong canvasHandle,
jint a, jint r, jint g, jint b) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
canvas->drawARGB(a, r, g, b);
}
static void drawColor__I(JNIEnv* env, jobject, jlong canvasHandle,
jint color) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
canvas->drawColor(color);
}
static void drawColor__II(JNIEnv* env, jobject, jlong canvasHandle,
jint color, jint modeHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
}
static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle,
jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
canvas->drawPaint(*paint);
}
@@ -461,14 +504,14 @@ public:
static void drawLine__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
jfloat startX, jfloat startY, jfloat stopX,
jfloat stopY, jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
canvas->drawLine(startX, startY, stopX, stopY, *paint);
}
static void drawRect__RectFPaint(JNIEnv* env, jobject, jlong canvasHandle,
jobject rect, jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
SkRect rect_;
GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
@@ -478,14 +521,14 @@ public:
static void drawRect__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
jfloat left, jfloat top, jfloat right,
jfloat bottom, jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
canvas->drawRectCoords(left, top, right, bottom, *paint);
}
static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jobject joval,
jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
SkRect oval;
GraphicsJNI::jrectf_to_rect(env, joval, &oval);
@@ -494,7 +537,7 @@ public:
static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx,
jfloat cy, jfloat radius, jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
canvas->drawCircle(cx, cy, radius, *paint);
}
@@ -502,7 +545,7 @@ public:
static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jobject joval,
jfloat startAngle, jfloat sweepAngle,
jboolean useCenter, jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
SkRect oval;
GraphicsJNI::jrectf_to_rect(env, joval, &oval);
@@ -512,7 +555,7 @@ public:
static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle,
jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat rx, jfloat ry,
jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
canvas->drawRoundRect(rect, rx, ry, *paint);
@@ -520,7 +563,7 @@ public:
static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
canvas->drawPath(*path, *paint);
@@ -531,7 +574,7 @@ public:
jfloat left, jfloat top,
jlong paintHandle, jint canvasDensity,
jint screenDensity, jint bitmapDensity) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
@@ -591,7 +634,7 @@ public:
jlong bitmapHandle, jobject srcIRect,
jobject dstRectF, jlong paintHandle,
jint screenDensity, jint bitmapDensity) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
SkRect dst;
@@ -604,7 +647,7 @@ public:
jlong bitmapHandle, jobject srcIRect,
jobject dstRect, jlong paintHandle,
jint screenDensity, jint bitmapDensity) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
SkRect dst;
@@ -616,9 +659,8 @@ public:
static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
jintArray jcolors, jint offset, jint stride,
jfloat x, jfloat y, jint width, jint height,
- jboolean hasAlpha, jlong paintHandle)
- {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ jboolean hasAlpha, jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
SkBitmap bitmap;
bitmap.setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config :
@@ -638,7 +680,7 @@ public:
static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle,
jlong bitmapHandle, jlong matrixHandle,
jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
@@ -649,7 +691,7 @@ public:
jlong bitmapHandle, jint meshWidth, jint meshHeight,
jfloatArray jverts, jint vertIndex, jintArray jcolors,
jint colorIndex, jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
@@ -759,7 +801,7 @@ public:
jintArray jcolors, jint colorIndex,
jshortArray jindices, jint indexIndex,
jint indexCount, jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
@@ -799,7 +841,7 @@ public:
jcharArray text, jint index, jint count,
jfloat x, jfloat y, jint flags,
jlong paintHandle, jlong typefaceHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
jchar* textArray = env->GetCharArrayElements(text, NULL);
@@ -812,7 +854,7 @@ public:
jint start, jint end,
jfloat x, jfloat y, jint flags,
jlong paintHandle, jlong typefaceHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
@@ -939,10 +981,10 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
}
static void drawTextRun___CIIIIFFIPaintTypeface(
- JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
- jint count, jint contextIndex, jint contextCount,
- jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
+ jint count, jint contextIndex, jint contextCount,
+ jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
@@ -953,10 +995,10 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
}
static void drawTextRun__StringIIIIFFIPaintTypeface(
- JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start,
- jint end, jint contextStart, jint contextEnd,
- jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start,
+ jint end, jint contextStart, jint contextEnd,
+ jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
@@ -971,7 +1013,7 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
static void drawPosText___CII_FPaint(JNIEnv* env, jobject, jlong canvasHandle,
jcharArray text, jint index, jint count,
jfloatArray pos, jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
jchar* textArray = text ? env->GetCharArrayElements(text, NULL) : NULL;
jsize textCount = text ? env->GetArrayLength(text) : NULL;
@@ -1002,7 +1044,7 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
jlong canvasHandle, jstring text,
jfloatArray pos,
jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
const void* text_ = text ? env->GetStringChars(text, NULL) : NULL;
int byteLength = text ? env->GetStringLength(text) : 0;
@@ -1032,7 +1074,7 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
jlong canvasHandle, jcharArray text, jint index, jint count,
jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
@@ -1045,7 +1087,7 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
jlong canvasHandle, jstring text, jlong pathHandle,
jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
const jchar* text_ = env->GetStringChars(text, NULL);
@@ -1084,7 +1126,7 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle,
jobject bounds) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkRect r;
SkIRect ir;
bool result = getHardClipBounds(canvas, &r);
@@ -1100,7 +1142,7 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
static void getCTM(JNIEnv* env, jobject, jlong canvasHandle,
jlong matrixHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
*matrix = canvas->getTotalMatrix();
}
@@ -1108,8 +1150,9 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l
static JNINativeMethod gCanvasMethods[] = {
{"finalizer", "(J)V", (void*) SkCanvasGlue::finalizer},
- {"initRaster","(J)J", (void*) SkCanvasGlue::initRaster},
- {"copyNativeCanvasState","(JJ)V", (void*) SkCanvasGlue::copyCanvasState},
+ {"initRaster", "(J)J", (void*) SkCanvasGlue::initRaster},
+ {"initCanvas", "(J)J", (void*) SkCanvasGlue::initCanvas},
+ {"native_setBitmap", "(JJZ)V", (void*) SkCanvasGlue::setBitmap},
{"isOpaque","()Z", (void*) SkCanvasGlue::isOpaque},
{"getWidth","()I", (void*) SkCanvasGlue::getWidth},
{"getHeight","()I", (void*) SkCanvasGlue::getHeight},
@@ -1224,4 +1267,11 @@ int register_android_graphics_Canvas(JNIEnv* env) {
return result;
}
+} // namespace android
+
+// GraphicsJNI helper for external clients.
+// We keep the implementation here to avoid exposing NativeCanvasWrapper
+// externally.
+SkCanvas* GraphicsJNI::getNativeCanvas(jlong nativeHandle) {
+ return android::SkCanvasGlue::getNativeCanvas(nativeHandle);
}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index dce185d..64ad223 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -320,7 +320,7 @@ SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
SkASSERT(canvas);
SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID);
- SkCanvas* c = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* c = getNativeCanvas(canvasHandle);
SkASSERT(c);
return c;
}
@@ -698,7 +698,7 @@ int register_android_graphics_Graphics(JNIEnv* env)
"nativeInt", "I");
gCanvas_class = make_globalref(env, "android/graphics/Canvas");
- gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "J");
+ gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvasWrapper", "J");
gPaint_class = make_globalref(env, "android/graphics/Paint");
gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "J");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index db7b6d9..73dd11b 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -45,6 +45,7 @@ public:
static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point);
static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf);
+ static SkCanvas* getNativeCanvas(jlong nativeHandle);
static SkCanvas* getNativeCanvas(JNIEnv*, jobject canvas);
static SkPaint* getNativePaint(JNIEnv*, jobject paint);
static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint);
diff --git a/core/jni/android/graphics/MaskFilter.cpp b/core/jni/android/graphics/MaskFilter.cpp
index 2113330..b394905 100644
--- a/core/jni/android/graphics/MaskFilter.cpp
+++ b/core/jni/android/graphics/MaskFilter.cpp
@@ -21,8 +21,7 @@ public:
static jlong createBlur(JNIEnv* env, jobject, jfloat radius, jint blurStyle) {
SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
- SkMaskFilter* filter = SkBlurMaskFilter::Create(
- (SkBlurMaskFilter::BlurStyle)blurStyle, sigma);
+ SkMaskFilter* filter = SkBlurMaskFilter::Create((SkBlurStyle)blurStyle, sigma);
ThrowIAE_IfNull(env, filter);
return reinterpret_cast<jlong>(filter);
}
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 855d267..ab5bdb0 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -119,7 +119,7 @@ public:
static void drawF(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRectF,
jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
jint destDensity, jint srcDensity) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle);
const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
@@ -138,7 +138,7 @@ public:
static void drawI(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRect,
jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
jint destDensity, jint srcDensity) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle);
const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index bac8ef7..a8a3dae 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -56,7 +56,7 @@ public:
static void draw(JNIEnv* env, jobject, jlong canvasHandle,
jlong pictureHandle) {
- SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
SkPicture* picture = reinterpret_cast<SkPicture*>(pictureHandle);
SkASSERT(canvas);
SkASSERT(picture);
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index fedb1b2..a2b1ed9 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -23,6 +23,11 @@
#define ENCODING_PCM_16BIT 2
#define ENCODING_PCM_8BIT 3
#define ENCODING_PCM_FLOAT 4
+#define ENCODING_INVALID 0
+#define ENCODING_DEFAULT 1
+
+#define CHANNEL_INVALID 0
+#define CHANNEL_OUT_DEFAULT 1
static inline audio_format_t audioFormatToNative(int audioFormat)
{
@@ -33,9 +38,58 @@ static inline audio_format_t audioFormatToNative(int audioFormat)
return AUDIO_FORMAT_PCM_8_BIT;
case ENCODING_PCM_FLOAT:
return AUDIO_FORMAT_PCM_FLOAT;
+ case ENCODING_DEFAULT:
+ return AUDIO_FORMAT_DEFAULT;
default:
return AUDIO_FORMAT_INVALID;
}
}
+static inline int audioFormatFromNative(audio_format_t nativeFormat)
+{
+ switch (nativeFormat) {
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return ENCODING_PCM_16BIT;
+ case AUDIO_FORMAT_PCM_8_BIT:
+ return ENCODING_PCM_8BIT;
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return ENCODING_PCM_FLOAT;
+ case AUDIO_FORMAT_DEFAULT:
+ return ENCODING_DEFAULT;
+ default:
+ return ENCODING_INVALID;
+ }
+}
+
+static inline audio_channel_mask_t outChannelMaskToNative(int channelMask)
+{
+ switch (channelMask) {
+ case CHANNEL_OUT_DEFAULT:
+ case CHANNEL_INVALID:
+ return AUDIO_CHANNEL_NONE;
+ default:
+ return (audio_channel_mask_t)(channelMask>>2);
+ }
+}
+
+static inline int outChannelMaskFromNative(audio_channel_mask_t nativeMask)
+{
+ switch (nativeMask) {
+ case AUDIO_CHANNEL_NONE:
+ return CHANNEL_OUT_DEFAULT;
+ default:
+ return (int)nativeMask<<2;
+ }
+}
+
+static inline audio_channel_mask_t inChannelMaskToNative(int channelMask)
+{
+ return (audio_channel_mask_t)channelMask;
+}
+
+static inline int inChannelMaskFromNative(audio_channel_mask_t nativeMask)
+{
+ return (int)nativeMask;
+}
+
#endif // ANDROID_MEDIA_AUDIOFORMAT_H
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index a9a62f8..0f7e140 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -15,7 +15,9 @@
** limitations under the License.
*/
-#define LOG_TAG "AudioSystem"
+//#define LOG_NDEBUG 0
+
+#define LOG_TAG "AudioSystem-JNI"
#include <utils/Log.h>
#include <jni.h>
@@ -26,6 +28,8 @@
#include <system/audio.h>
#include <system/audio_policy.h>
+#include "android_media_AudioFormat.h"
+#include "android_media_AudioErrors.h"
// ----------------------------------------------------------------------------
@@ -33,12 +37,160 @@ using namespace android;
static const char* const kClassPathName = "android/media/AudioSystem";
+static jclass gArrayListClass;
+static struct {
+ jmethodID add;
+} gArrayListMethods;
+
+static jclass gAudioHandleClass;
+static jmethodID gAudioHandleCstor;
+static struct {
+ jfieldID mId;
+} gAudioHandleFields;
+
+static jclass gAudioPortClass;
+static jmethodID gAudioPortCstor;
+static struct {
+ jfieldID mHandle;
+ jfieldID mRole;
+ jfieldID mGains;
+ jfieldID mActiveConfig;
+ // other fields unused by JNI
+} gAudioPortFields;
+
+static jclass gAudioPortConfigClass;
+static jmethodID gAudioPortConfigCstor;
+static struct {
+ jfieldID mPort;
+ jfieldID mSamplingRate;
+ jfieldID mChannelMask;
+ jfieldID mFormat;
+ jfieldID mGain;
+ jfieldID mConfigMask;
+} gAudioPortConfigFields;
+
+static jclass gAudioDevicePortClass;
+static jmethodID gAudioDevicePortCstor;
+
+static jclass gAudioDevicePortConfigClass;
+static jmethodID gAudioDevicePortConfigCstor;
+
+static jclass gAudioMixPortClass;
+static jmethodID gAudioMixPortCstor;
+
+static jclass gAudioMixPortConfigClass;
+static jmethodID gAudioMixPortConfigCstor;
+
+static jclass gAudioGainClass;
+static jmethodID gAudioGainCstor;
+
+static jclass gAudioGainConfigClass;
+static jmethodID gAudioGainConfigCstor;
+static struct {
+ jfieldID mIndex;
+ jfieldID mMode;
+ jfieldID mChannelMask;
+ jfieldID mValues;
+ jfieldID mRampDurationMs;
+ // other fields unused by JNI
+} gAudioGainConfigFields;
+
+static jclass gAudioPatchClass;
+static jmethodID gAudioPatchCstor;
+static struct {
+ jfieldID mHandle;
+ // other fields unused by JNI
+} gAudioPatchFields;
+
+static const char* const kEventHandlerClassPathName =
+ "android/media/AudioPortEventHandler";
+static jmethodID gPostEventFromNative;
+
enum AudioError {
kAudioStatusOk = 0,
kAudioStatusError = 1,
kAudioStatusMediaServerDied = 100
};
+enum {
+ AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1,
+ AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2,
+ AUDIOPORT_EVENT_SERVICE_DIED = 3,
+};
+
+#define MAX_PORT_GENERATION_SYNC_ATTEMPTS 5
+
+// ----------------------------------------------------------------------------
+// ref-counted object for callbacks
+class JNIAudioPortCallback: public AudioSystem::AudioPortCallback
+{
+public:
+ JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
+ ~JNIAudioPortCallback();
+
+ virtual void onAudioPortListUpdate();
+ virtual void onAudioPatchListUpdate();
+ virtual void onServiceDied();
+
+private:
+ void sendEvent(int event);
+
+ jclass mClass; // Reference to AudioPortEventHandlerDelegate class
+ jobject mObject; // Weak ref to AudioPortEventHandlerDelegate Java object to call on
+};
+
+JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz)
+{
+
+ // Hold onto the SoundTriggerModule class for use in calling the static method
+ // that posts events to the application thread.
+ jclass clazz = env->GetObjectClass(thiz);
+ if (clazz == NULL) {
+ ALOGE("Can't find class %s", kEventHandlerClassPathName);
+ return;
+ }
+ mClass = (jclass)env->NewGlobalRef(clazz);
+
+ // We use a weak reference so the SoundTriggerModule object can be garbage collected.
+ // The reference is only used as a proxy for callbacks.
+ mObject = env->NewGlobalRef(weak_thiz);
+}
+
+JNIAudioPortCallback::~JNIAudioPortCallback()
+{
+ // remove global references
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->DeleteGlobalRef(mObject);
+ env->DeleteGlobalRef(mClass);
+}
+
+void JNIAudioPortCallback::sendEvent(int event)
+{
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
+ event, 0, 0, NULL);
+ if (env->ExceptionCheck()) {
+ ALOGW("An exception occurred while notifying an event.");
+ env->ExceptionClear();
+ }
+}
+
+void JNIAudioPortCallback::onAudioPortListUpdate()
+{
+ sendEvent(AUDIOPORT_EVENT_PORT_LIST_UPDATED);
+}
+
+void JNIAudioPortCallback::onAudioPatchListUpdate()
+{
+ sendEvent(AUDIOPORT_EVENT_PATCH_LIST_UPDATED);
+}
+
+void JNIAudioPortCallback::onServiceDied()
+{
+ sendEvent(AUDIOPORT_EVENT_SERVICE_DIED);
+}
+
static int check_AudioSystem_Command(status_t status)
{
switch (status) {
@@ -281,6 +433,854 @@ android_media_AudioSystem_checkAudioFlinger(JNIEnv *env, jobject clazz)
return (jint) check_AudioSystem_Command(AudioSystem::checkAudioFlinger());
}
+
+static bool useInChannelMask(audio_port_type_t type, audio_port_role_t role)
+{
+ return ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) ||
+ ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK));
+}
+
+static void convertAudioGainConfigToNative(JNIEnv *env,
+ struct audio_gain_config *nAudioGainConfig,
+ const jobject jAudioGainConfig,
+ bool useInMask)
+{
+ nAudioGainConfig->index = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mIndex);
+ nAudioGainConfig->mode = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode);
+ ALOGV("convertAudioGainConfigToNative got gain index %d", nAudioGainConfig->index);
+ jint jMask = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mChannelMask);
+ audio_channel_mask_t nMask;
+ if (useInMask) {
+ nMask = inChannelMaskToNative(jMask);
+ ALOGV("convertAudioGainConfigToNative IN mask java %x native %x", jMask, nMask);
+ } else {
+ nMask = outChannelMaskToNative(jMask);
+ ALOGV("convertAudioGainConfigToNative OUT mask java %x native %x", jMask, nMask);
+ }
+ nAudioGainConfig->channel_mask = nMask;
+ nAudioGainConfig->ramp_duration_ms = env->GetIntField(jAudioGainConfig,
+ gAudioGainConfigFields.mRampDurationMs);
+ jintArray jValues = (jintArray)env->GetObjectField(jAudioGainConfig,
+ gAudioGainConfigFields.mValues);
+ int *nValues = env->GetIntArrayElements(jValues, NULL);
+ size_t size = env->GetArrayLength(jValues);
+ memcpy(nAudioGainConfig->values, nValues, size * sizeof(int));
+ env->DeleteLocalRef(jValues);
+}
+
+
+static jint convertAudioPortConfigToNative(JNIEnv *env,
+ struct audio_port_config *nAudioPortConfig,
+ const jobject jAudioPortConfig)
+{
+ jobject jAudioPort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort);
+ jobject jHandle = env->GetObjectField(jAudioPort, gAudioPortFields.mHandle);
+ nAudioPortConfig->id = env->GetIntField(jHandle, gAudioHandleFields.mId);
+ nAudioPortConfig->role = (audio_port_role_t)env->GetIntField(jAudioPort,
+ gAudioPortFields.mRole);
+ if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) {
+ nAudioPortConfig->type = AUDIO_PORT_TYPE_DEVICE;
+ } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) {
+ nAudioPortConfig->type = AUDIO_PORT_TYPE_MIX;
+ } else {
+ env->DeleteLocalRef(jAudioPort);
+ env->DeleteLocalRef(jHandle);
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+ ALOGV("convertAudioPortConfigToNative handle %d role %d type %d",
+ nAudioPortConfig->id, nAudioPortConfig->role, nAudioPortConfig->type);
+
+ nAudioPortConfig->sample_rate = env->GetIntField(jAudioPortConfig,
+ gAudioPortConfigFields.mSamplingRate);
+
+ bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role);
+ audio_channel_mask_t nMask;
+ jint jMask = env->GetIntField(jAudioPortConfig,
+ gAudioPortConfigFields.mChannelMask);
+ if (useInMask) {
+ nMask = inChannelMaskToNative(jMask);
+ ALOGV("convertAudioPortConfigToNative IN mask java %x native %x", jMask, nMask);
+ } else {
+ nMask = outChannelMaskToNative(jMask);
+ ALOGV("convertAudioPortConfigToNative OUT mask java %x native %x", jMask, nMask);
+ }
+ nAudioPortConfig->channel_mask = nMask;
+
+ jint jFormat = env->GetIntField(jAudioPortConfig, gAudioPortConfigFields.mFormat);
+ audio_format_t nFormat = audioFormatToNative(jFormat);
+ ALOGV("convertAudioPortConfigToNative format %d native %d", jFormat, nFormat);
+ nAudioPortConfig->format = nFormat;
+ jobject jGain = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mGain);
+ if (jGain != NULL) {
+ convertAudioGainConfigToNative(env, &nAudioPortConfig->gain, jGain, useInMask);
+ env->DeleteLocalRef(jGain);
+ } else {
+ ALOGV("convertAudioPortConfigToNative no gain");
+ nAudioPortConfig->gain.index = -1;
+ }
+ nAudioPortConfig->config_mask = env->GetIntField(jAudioPortConfig,
+ gAudioPortConfigFields.mConfigMask);
+
+ env->DeleteLocalRef(jAudioPort);
+ env->DeleteLocalRef(jHandle);
+ return (jint)AUDIO_JAVA_SUCCESS;
+}
+
+static jint convertAudioPortConfigFromNative(JNIEnv *env,
+ jobject jAudioPort,
+ jobject *jAudioPortConfig,
+ const struct audio_port_config *nAudioPortConfig)
+{
+ jint jStatus = AUDIO_JAVA_SUCCESS;
+ jobject jAudioGainConfig = NULL;
+ jobject jAudioGain = NULL;
+ jintArray jGainValues;
+ bool audioportCreated = false;
+
+ ALOGV("convertAudioPortConfigFromNative jAudioPort %p", jAudioPort);
+
+ if (jAudioPort == NULL) {
+ jobject jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
+ nAudioPortConfig->id);
+
+ ALOGV("convertAudioPortConfigFromNative handle %d is a %s", nAudioPortConfig->id,
+ nAudioPortConfig->type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix");
+
+ if (jHandle == NULL) {
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+ // create dummy port and port config objects with just the correct handle
+ // and configuration data. The actual AudioPortConfig objects will be
+ // constructed by java code with correct class type (device, mix etc...)
+ // and reference to AudioPort instance in this client
+ jAudioPort = env->NewObject(gAudioPortClass, gAudioPortCstor,
+ jHandle,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ env->DeleteLocalRef(jHandle);
+ if (jAudioPort == NULL) {
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+ ALOGV("convertAudioPortConfigFromNative jAudioPort created for handle %d",
+ nAudioPortConfig->id);
+
+ audioportCreated = true;
+ }
+
+ bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role);
+
+ audio_channel_mask_t nMask;
+ jint jMask;
+
+ int gainIndex = nAudioPortConfig->gain.index;
+ if (gainIndex >= 0) {
+ ALOGV("convertAudioPortConfigFromNative gain found with index %d mode %x",
+ gainIndex, nAudioPortConfig->gain.mode);
+ if (audioportCreated) {
+ ALOGV("convertAudioPortConfigFromNative creating gain");
+ jAudioGain = env->NewObject(gAudioGainClass, gAudioGainCstor,
+ gainIndex,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0);
+ if (jAudioGain == NULL) {
+ ALOGV("convertAudioPortConfigFromNative creating gain FAILED");
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ } else {
+ ALOGV("convertAudioPortConfigFromNative reading gain from port");
+ jobjectArray jGains = (jobjectArray)env->GetObjectField(jAudioPort,
+ gAudioPortFields.mGains);
+ if (jGains == NULL) {
+ ALOGV("convertAudioPortConfigFromNative could not get gains from port");
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ jAudioGain = env->GetObjectArrayElement(jGains, gainIndex);
+ env->DeleteLocalRef(jGains);
+ if (jAudioGain == NULL) {
+ ALOGV("convertAudioPortConfigFromNative could not get gain at index %d", gainIndex);
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ }
+ //TODO: replace popcount by audio utils function mask to count
+ int numValues = popcount(nAudioPortConfig->gain.channel_mask);
+ jGainValues = env->NewIntArray(numValues);
+ if (jGainValues == NULL) {
+ ALOGV("convertAudioPortConfigFromNative could not create gain values %d", numValues);
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ env->SetIntArrayRegion(jGainValues, 0, numValues,
+ nAudioPortConfig->gain.values);
+
+ nMask = nAudioPortConfig->gain.channel_mask;
+ if (useInMask) {
+ jMask = inChannelMaskFromNative(nMask);
+ ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
+ } else {
+ jMask = outChannelMaskFromNative(nMask);
+ ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
+ }
+
+ jAudioGainConfig = env->NewObject(gAudioGainConfigClass,
+ gAudioGainConfigCstor,
+ gainIndex,
+ jAudioGain,
+ nAudioPortConfig->gain.mode,
+ jMask,
+ jGainValues,
+ nAudioPortConfig->gain.ramp_duration_ms);
+ env->DeleteLocalRef(jGainValues);
+ if (jAudioGainConfig == NULL) {
+ ALOGV("convertAudioPortConfigFromNative could not create gain config");
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ }
+ jclass clazz;
+ jmethodID methodID;
+ if (audioportCreated) {
+ clazz = gAudioPortConfigClass;
+ methodID = gAudioPortConfigCstor;
+ ALOGV("convertAudioPortConfigFromNative building a generic port config");
+ } else {
+ if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) {
+ clazz = gAudioDevicePortConfigClass;
+ methodID = gAudioDevicePortConfigCstor;
+ ALOGV("convertAudioPortConfigFromNative building a device config");
+ } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) {
+ clazz = gAudioMixPortConfigClass;
+ methodID = gAudioMixPortConfigCstor;
+ ALOGV("convertAudioPortConfigFromNative building a mix config");
+ } else {
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ }
+ nMask = nAudioPortConfig->channel_mask;
+ if (useInMask) {
+ jMask = inChannelMaskFromNative(nMask);
+ ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
+ } else {
+ jMask = outChannelMaskFromNative(nMask);
+ ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
+ }
+
+ *jAudioPortConfig = env->NewObject(clazz, methodID,
+ jAudioPort,
+ nAudioPortConfig->sample_rate,
+ jMask,
+ audioFormatFromNative(nAudioPortConfig->format),
+ jAudioGainConfig);
+ if (*jAudioPortConfig == NULL) {
+ ALOGV("convertAudioPortConfigFromNative could not create new port config");
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ } else {
+ ALOGV("convertAudioPortConfigFromNative OK");
+ }
+
+exit:
+ if (audioportCreated) {
+ env->DeleteLocalRef(jAudioPort);
+ if (jAudioGain != NULL) {
+ env->DeleteLocalRef(jAudioGain);
+ }
+ }
+ if (jAudioGainConfig != NULL) {
+ env->DeleteLocalRef(jAudioGainConfig);
+ }
+ return jStatus;
+}
+
+static jint convertAudioPortFromNative(JNIEnv *env,
+ jobject *jAudioPort, const struct audio_port *nAudioPort)
+{
+ jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
+ jintArray jSamplingRates = NULL;
+ jintArray jChannelMasks = NULL;
+ jintArray jFormats = NULL;
+ jobjectArray jGains = NULL;
+ jobject jHandle = NULL;
+ bool useInMask;
+
+ ALOGV("convertAudioPortFromNative id %d role %d type %d",
+ nAudioPort->id, nAudioPort->role, nAudioPort->type);
+
+ jSamplingRates = env->NewIntArray(nAudioPort->num_sample_rates);
+ if (jSamplingRates == NULL) {
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ if (nAudioPort->num_sample_rates) {
+ env->SetIntArrayRegion(jSamplingRates, 0, nAudioPort->num_sample_rates,
+ (jint *)nAudioPort->sample_rates);
+ }
+
+ jChannelMasks = env->NewIntArray(nAudioPort->num_channel_masks);
+ if (jChannelMasks == NULL) {
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ useInMask = useInChannelMask(nAudioPort->type, nAudioPort->role);
+
+ jint jMask;
+ for (size_t j = 0; j < nAudioPort->num_channel_masks; j++) {
+ if (useInMask) {
+ jMask = inChannelMaskFromNative(nAudioPort->channel_masks[j]);
+ } else {
+ jMask = outChannelMaskFromNative(nAudioPort->channel_masks[j]);
+ }
+ env->SetIntArrayRegion(jChannelMasks, j, 1, &jMask);
+ }
+
+ jFormats = env->NewIntArray(nAudioPort->num_formats);
+ if (jFormats == NULL) {
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ for (size_t j = 0; j < nAudioPort->num_formats; j++) {
+ jint jFormat = audioFormatFromNative(nAudioPort->formats[j]);
+ env->SetIntArrayRegion(jFormats, j, 1, &jFormat);
+ }
+
+ jGains = env->NewObjectArray(nAudioPort->num_gains,
+ gAudioGainClass, NULL);
+ if (jGains == NULL) {
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ for (size_t j = 0; j < nAudioPort->num_gains; j++) {
+ audio_channel_mask_t nMask = nAudioPort->gains[j].channel_mask;
+ if (useInMask) {
+ jMask = inChannelMaskFromNative(nMask);
+ ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
+ } else {
+ jMask = outChannelMaskFromNative(nMask);
+ ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
+ }
+
+ jobject jGain = env->NewObject(gAudioGainClass, gAudioGainCstor,
+ j,
+ nAudioPort->gains[j].mode,
+ jMask,
+ nAudioPort->gains[j].min_value,
+ nAudioPort->gains[j].max_value,
+ nAudioPort->gains[j].default_value,
+ nAudioPort->gains[j].step_value,
+ nAudioPort->gains[j].min_ramp_ms,
+ nAudioPort->gains[j].max_ramp_ms);
+ if (jGain == NULL) {
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ env->SetObjectArrayElement(jGains, j, jGain);
+ env->DeleteLocalRef(jGain);
+ }
+
+ jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
+ nAudioPort->id);
+ if (jHandle == NULL) {
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+
+ if (nAudioPort->type == AUDIO_PORT_TYPE_DEVICE) {
+ ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type);
+ jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address);
+ *jAudioPort = env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor,
+ jHandle, jSamplingRates, jChannelMasks, jFormats, jGains,
+ nAudioPort->ext.device.type, jAddress);
+ env->DeleteLocalRef(jAddress);
+ } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) {
+ ALOGV("convertAudioPortFromNative is a mix");
+ *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor,
+ jHandle, nAudioPort->role, jSamplingRates, jChannelMasks,
+ jFormats, jGains);
+ } else {
+ ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type);
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ if (*jAudioPort == NULL) {
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+
+ jobject jAudioPortConfig;
+ jStatus = convertAudioPortConfigFromNative(env,
+ *jAudioPort,
+ &jAudioPortConfig,
+ &nAudioPort->active_config);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+
+ env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig);
+
+exit:
+ if (jSamplingRates != NULL) {
+ env->DeleteLocalRef(jSamplingRates);
+ }
+ if (jChannelMasks != NULL) {
+ env->DeleteLocalRef(jChannelMasks);
+ }
+ if (jFormats != NULL) {
+ env->DeleteLocalRef(jFormats);
+ }
+ if (jGains != NULL) {
+ env->DeleteLocalRef(jGains);
+ }
+ if (jHandle != NULL) {
+ env->DeleteLocalRef(jHandle);
+ }
+
+ return jStatus;
+}
+
+
+static jint
+android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz,
+ jobject jPorts, jintArray jGeneration)
+{
+ ALOGV("listAudioPorts");
+
+ if (jPorts == NULL) {
+ ALOGE("listAudioPorts NULL AudioPort ArrayList");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jPorts, gArrayListClass)) {
+ ALOGE("listAudioPorts not an arraylist");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ status_t status;
+ unsigned int generation1;
+ unsigned int generation;
+ unsigned int numPorts;
+ jint *nGeneration;
+ struct audio_port *nPorts = NULL;
+ int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS;
+
+ // get the port count and all the ports until they both return the same generation
+ do {
+ if (attempts-- < 0) {
+ status = TIMED_OUT;
+ break;
+ }
+
+ numPorts = 0;
+ status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE,
+ AUDIO_PORT_TYPE_NONE,
+ &numPorts,
+ NULL,
+ &generation1);
+ if (status != NO_ERROR || numPorts == 0) {
+ ALOGE_IF(status != NO_ERROR, "AudioSystem::listAudioPorts error %d", status);
+ break;
+ }
+ nPorts = (struct audio_port *)realloc(nPorts, numPorts * sizeof(struct audio_port));
+
+ status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE,
+ AUDIO_PORT_TYPE_NONE,
+ &numPorts,
+ nPorts,
+ &generation);
+ ALOGV("listAudioPorts AudioSystem::listAudioPorts numPorts %d generation %d generation1 %d",
+ numPorts, generation, generation1);
+ } while (generation1 != generation && status == NO_ERROR);
+
+ jint jStatus = nativeToJavaStatus(status);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ goto exit;
+ }
+
+ nGeneration = env->GetIntArrayElements(jGeneration, NULL);
+ if (nGeneration == NULL) {
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ nGeneration[0] = generation1;
+ env->ReleaseIntArrayElements(jGeneration, nGeneration, 0);
+
+ for (size_t i = 0; i < numPorts; i++) {
+ jobject jAudioPort;
+ jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ goto exit;
+ }
+ env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort);
+ }
+
+exit:
+ free(nPorts);
+ return jStatus;
+}
+
+static int
+android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz,
+ jobjectArray jPatches, jobjectArray jSources, jobjectArray jSinks)
+{
+ status_t status;
+ jint jStatus;
+
+ ALOGV("createAudioPatch");
+ if (jPatches == NULL || jSources == NULL || jSinks == NULL) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ if (env->GetArrayLength(jPatches) != 1) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ jint numSources = env->GetArrayLength(jSources);
+ if (numSources == 0 || numSources > AUDIO_PATCH_PORTS_MAX) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ jint numSinks = env->GetArrayLength(jSinks);
+ if (numSinks == 0 || numSinks > AUDIO_PATCH_PORTS_MAX) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ audio_patch_handle_t handle = (audio_patch_handle_t)0;
+ jobject jPatch = env->GetObjectArrayElement(jPatches, 0);
+ jobject jPatchHandle = NULL;
+ if (jPatch != NULL) {
+ if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle);
+ handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId);
+ }
+
+ struct audio_patch nPatch;
+
+ nPatch.id = handle;
+ nPatch.num_sources = 0;
+ nPatch.num_sinks = 0;
+ jobject jSource = NULL;
+ jobject jSink = NULL;
+
+ for (jint i = 0; i < numSources; i++) {
+ jSource = env->GetObjectArrayElement(jSources, i);
+ if (!env->IsInstanceOf(jSource, gAudioPortConfigClass)) {
+ jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
+ goto exit;
+ }
+ jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource);
+ env->DeleteLocalRef(jSource);
+ jSource = NULL;
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ goto exit;
+ }
+ nPatch.num_sources++;
+ }
+
+ for (jint i = 0; i < numSinks; i++) {
+ jSink = env->GetObjectArrayElement(jSinks, i);
+ if (!env->IsInstanceOf(jSink, gAudioPortConfigClass)) {
+ jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
+ goto exit;
+ }
+ jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink);
+ env->DeleteLocalRef(jSink);
+ jSink = NULL;
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ goto exit;
+ }
+ nPatch.num_sinks++;
+ }
+
+ ALOGV("AudioSystem::createAudioPatch");
+ status = AudioSystem::createAudioPatch(&nPatch, &handle);
+ ALOGV("AudioSystem::createAudioPatch() returned %d hande %d", status, handle);
+
+ jStatus = nativeToJavaStatus(status);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ goto exit;
+ }
+
+ if (jPatchHandle == NULL) {
+ jPatchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
+ handle);
+ if (jPatchHandle == NULL) {
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, jPatchHandle, jSources, jSinks);
+ if (jPatch == NULL) {
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ env->SetObjectArrayElement(jPatches, 0, jPatch);
+ } else {
+ env->SetIntField(jPatchHandle, gAudioHandleFields.mId, handle);
+ }
+
+exit:
+ if (jPatchHandle != NULL) {
+ env->DeleteLocalRef(jPatchHandle);
+ }
+ if (jPatch != NULL) {
+ env->DeleteLocalRef(jPatch);
+ }
+ if (jSource != NULL) {
+ env->DeleteLocalRef(jSource);
+ }
+ if (jSink != NULL) {
+ env->DeleteLocalRef(jSink);
+ }
+ return jStatus;
+}
+
+static int
+android_media_AudioSystem_releaseAudioPatch(JNIEnv *env, jobject clazz,
+ jobject jPatch)
+{
+ ALOGV("releaseAudioPatch");
+ if (jPatch == NULL) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ audio_patch_handle_t handle = (audio_patch_handle_t)0;
+ jobject jPatchHandle = NULL;
+ if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle);
+ handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId);
+ env->DeleteLocalRef(jPatchHandle);
+
+ ALOGV("AudioSystem::releaseAudioPatch");
+ status_t status = AudioSystem::releaseAudioPatch(handle);
+ ALOGV("AudioSystem::releaseAudioPatch() returned %d", status);
+ jint jStatus = nativeToJavaStatus(status);
+ return status;
+}
+
+static jint
+android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz,
+ jobject jPatches, jintArray jGeneration)
+{
+ ALOGV("listAudioPatches");
+ if (jPatches == NULL) {
+ ALOGE("listAudioPatches NULL AudioPatch ArrayList");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jPatches, gArrayListClass)) {
+ ALOGE("listAudioPatches not an arraylist");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ status_t status;
+ unsigned int generation1;
+ unsigned int generation;
+ unsigned int numPatches;
+ jint *nGeneration;
+ struct audio_patch *nPatches = NULL;
+ jobjectArray jSources = NULL;
+ jobject jSource = NULL;
+ jobjectArray jSinks = NULL;
+ jobject jSink = NULL;
+ jobject jPatch = NULL;
+ int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS;
+
+ // get the patch count and all the patches until they both return the same generation
+ do {
+ if (attempts-- < 0) {
+ status = TIMED_OUT;
+ break;
+ }
+
+ numPatches = 0;
+ status = AudioSystem::listAudioPatches(&numPatches,
+ NULL,
+ &generation1);
+ if (status != NO_ERROR || numPatches == 0) {
+ ALOGE_IF(status != NO_ERROR, "listAudioPatches AudioSystem::listAudioPatches error %d",
+ status);
+ break;
+ }
+ nPatches = (struct audio_patch *)realloc(nPatches, numPatches * sizeof(struct audio_patch));
+
+ status = AudioSystem::listAudioPatches(&numPatches,
+ nPatches,
+ &generation);
+ ALOGV("listAudioPatches AudioSystem::listAudioPatches numPatches %d generation %d generation1 %d",
+ numPatches, generation, generation1);
+
+ } while (generation1 != generation && status == NO_ERROR);
+
+ jint jStatus = nativeToJavaStatus(status);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ goto exit;
+ }
+
+ nGeneration = env->GetIntArrayElements(jGeneration, NULL);
+ if (nGeneration == NULL) {
+ jStatus = AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ nGeneration[0] = generation1;
+ env->ReleaseIntArrayElements(jGeneration, nGeneration, 0);
+
+ for (size_t i = 0; i < numPatches; i++) {
+ jobject patchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
+ nPatches[i].id);
+ if (patchHandle == NULL) {
+ jStatus = AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ ALOGV("listAudioPatches patch %d num_sources %d num_sinks %d",
+ i, nPatches[i].num_sources, nPatches[i].num_sinks);
+
+ env->SetIntField(patchHandle, gAudioHandleFields.mId, nPatches[i].id);
+
+ // load sources
+ jSources = env->NewObjectArray(nPatches[i].num_sources,
+ gAudioPortConfigClass, NULL);
+ if (jSources == NULL) {
+ jStatus = AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+
+ for (size_t j = 0; j < nPatches[i].num_sources; j++) {
+ jStatus = convertAudioPortConfigFromNative(env,
+ NULL,
+ &jSource,
+ &nPatches[i].sources[j]);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ goto exit;
+ }
+ env->SetObjectArrayElement(jSources, j, jSource);
+ env->DeleteLocalRef(jSource);
+ jSource = NULL;
+ ALOGV("listAudioPatches patch %d source %d is a %s handle %d",
+ i, j,
+ nPatches[i].sources[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix",
+ nPatches[i].sources[j].id);
+ }
+ // load sinks
+ jSinks = env->NewObjectArray(nPatches[i].num_sinks,
+ gAudioPortConfigClass, NULL);
+ if (jSinks == NULL) {
+ jStatus = AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+
+ for (size_t j = 0; j < nPatches[i].num_sinks; j++) {
+ jStatus = convertAudioPortConfigFromNative(env,
+ NULL,
+ &jSink,
+ &nPatches[i].sinks[j]);
+
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ goto exit;
+ }
+ env->SetObjectArrayElement(jSinks, j, jSink);
+ env->DeleteLocalRef(jSink);
+ jSink = NULL;
+ ALOGV("listAudioPatches patch %d sink %d is a %s handle %d",
+ i, j,
+ nPatches[i].sinks[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix",
+ nPatches[i].sinks[j].id);
+ }
+
+ jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor,
+ patchHandle, jSources, jSinks);
+ env->DeleteLocalRef(jSources);
+ jSources = NULL;
+ env->DeleteLocalRef(jSinks);
+ jSinks = NULL;
+ if (jPatch == NULL) {
+ jStatus = AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch);
+ env->DeleteLocalRef(jPatch);
+ jPatch = NULL;
+ }
+
+exit:
+ if (jSources != NULL) {
+ env->DeleteLocalRef(jSources);
+ }
+ if (jSource != NULL) {
+ env->DeleteLocalRef(jSource);
+ }
+ if (jSinks != NULL) {
+ env->DeleteLocalRef(jSinks);
+ }
+ if (jSink != NULL) {
+ env->DeleteLocalRef(jSink);
+ }
+ if (jPatch != NULL) {
+ env->DeleteLocalRef(jPatch);
+ }
+ free(nPatches);
+ return jStatus;
+}
+
+static jint
+android_media_AudioSystem_setAudioPortConfig(JNIEnv *env, jobject clazz,
+ jobject jAudioPortConfig)
+{
+ ALOGV("setAudioPortConfig");
+ if (jAudioPortConfig == NULL) {
+ return AUDIO_JAVA_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jAudioPortConfig, gAudioPortConfigClass)) {
+ return AUDIO_JAVA_BAD_VALUE;
+ }
+ struct audio_port_config nAudioPortConfig;
+ jint jStatus = convertAudioPortConfigToNative(env, &nAudioPortConfig, jAudioPortConfig);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+ status_t status = AudioSystem::setAudioPortConfig(&nAudioPortConfig);
+ ALOGV("AudioSystem::setAudioPortConfig() returned %d", status);
+ jStatus = nativeToJavaStatus(status);
+ return jStatus;
+}
+
+static void
+android_media_AudioSystem_eventHandlerSetup(JNIEnv *env, jobject thiz, jobject weak_this)
+{
+ ALOGV("eventHandlerSetup");
+
+ sp<JNIAudioPortCallback> callback = new JNIAudioPortCallback(env, thiz, weak_this);
+
+ AudioSystem::setAudioPortCallback(callback);
+}
+
+static void
+android_media_AudioSystem_eventHandlerFinalize(JNIEnv *env, jobject thiz)
+{
+ ALOGV("eventHandlerFinalize");
+
+ sp<JNIAudioPortCallback> callback;
+
+ AudioSystem::setAudioPortCallback(callback);
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
@@ -309,12 +1309,123 @@ static JNINativeMethod gMethods[] = {
{"getOutputLatency", "(I)I", (void *)android_media_AudioSystem_getOutputLatency},
{"setLowRamDevice", "(Z)I", (void *)android_media_AudioSystem_setLowRamDevice},
{"checkAudioFlinger", "()I", (void *)android_media_AudioSystem_checkAudioFlinger},
+ {"listAudioPorts", "(Ljava/util/ArrayList;[I)I",
+ (void *)android_media_AudioSystem_listAudioPorts},
+ {"createAudioPatch", "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)I",
+ (void *)android_media_AudioSystem_createAudioPatch},
+ {"releaseAudioPatch", "(Landroid/media/AudioPatch;)I",
+ (void *)android_media_AudioSystem_releaseAudioPatch},
+ {"listAudioPatches", "(Ljava/util/ArrayList;[I)I",
+ (void *)android_media_AudioSystem_listAudioPatches},
+ {"setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I",
+ (void *)android_media_AudioSystem_setAudioPortConfig},
+};
+
+
+static JNINativeMethod gEventHandlerMethods[] = {
+ {"native_setup",
+ "(Ljava/lang/Object;)V",
+ (void *)android_media_AudioSystem_eventHandlerSetup},
+ {"native_finalize",
+ "()V",
+ (void *)android_media_AudioSystem_eventHandlerFinalize},
};
int register_android_media_AudioSystem(JNIEnv *env)
{
+
+ jclass arrayListClass = env->FindClass("java/util/ArrayList");
+ gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass);
+ gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
+
+ jclass audioHandleClass = env->FindClass("android/media/AudioHandle");
+ gAudioHandleClass = (jclass) env->NewGlobalRef(audioHandleClass);
+ gAudioHandleCstor = env->GetMethodID(audioHandleClass, "<init>", "(I)V");
+ gAudioHandleFields.mId = env->GetFieldID(audioHandleClass, "mId", "I");
+
+ jclass audioPortClass = env->FindClass("android/media/AudioPort");
+ gAudioPortClass = (jclass) env->NewGlobalRef(audioPortClass);
+ gAudioPortCstor = env->GetMethodID(audioPortClass, "<init>",
+ "(Landroid/media/AudioHandle;I[I[I[I[Landroid/media/AudioGain;)V");
+ gAudioPortFields.mHandle = env->GetFieldID(audioPortClass, "mHandle",
+ "Landroid/media/AudioHandle;");
+ gAudioPortFields.mRole = env->GetFieldID(audioPortClass, "mRole", "I");
+ gAudioPortFields.mGains = env->GetFieldID(audioPortClass, "mGains",
+ "[Landroid/media/AudioGain;");
+ gAudioPortFields.mActiveConfig = env->GetFieldID(audioPortClass, "mActiveConfig",
+ "Landroid/media/AudioPortConfig;");
+
+ jclass audioPortConfigClass = env->FindClass("android/media/AudioPortConfig");
+ gAudioPortConfigClass = (jclass) env->NewGlobalRef(audioPortConfigClass);
+ gAudioPortConfigCstor = env->GetMethodID(audioPortConfigClass, "<init>",
+ "(Landroid/media/AudioPort;IIILandroid/media/AudioGainConfig;)V");
+ gAudioPortConfigFields.mPort = env->GetFieldID(audioPortConfigClass, "mPort",
+ "Landroid/media/AudioPort;");
+ gAudioPortConfigFields.mSamplingRate = env->GetFieldID(audioPortConfigClass,
+ "mSamplingRate", "I");
+ gAudioPortConfigFields.mChannelMask = env->GetFieldID(audioPortConfigClass,
+ "mChannelMask", "I");
+ gAudioPortConfigFields.mFormat = env->GetFieldID(audioPortConfigClass, "mFormat", "I");
+ gAudioPortConfigFields.mGain = env->GetFieldID(audioPortConfigClass, "mGain",
+ "Landroid/media/AudioGainConfig;");
+ gAudioPortConfigFields.mConfigMask = env->GetFieldID(audioPortConfigClass, "mConfigMask", "I");
+
+ jclass audioDevicePortConfigClass = env->FindClass("android/media/AudioDevicePortConfig");
+ gAudioDevicePortConfigClass = (jclass) env->NewGlobalRef(audioDevicePortConfigClass);
+ gAudioDevicePortConfigCstor = env->GetMethodID(audioDevicePortConfigClass, "<init>",
+ "(Landroid/media/AudioDevicePort;IIILandroid/media/AudioGainConfig;)V");
+
+ jclass audioMixPortConfigClass = env->FindClass("android/media/AudioMixPortConfig");
+ gAudioMixPortConfigClass = (jclass) env->NewGlobalRef(audioMixPortConfigClass);
+ gAudioMixPortConfigCstor = env->GetMethodID(audioMixPortConfigClass, "<init>",
+ "(Landroid/media/AudioMixPort;IIILandroid/media/AudioGainConfig;)V");
+
+ jclass audioDevicePortClass = env->FindClass("android/media/AudioDevicePort");
+ gAudioDevicePortClass = (jclass) env->NewGlobalRef(audioDevicePortClass);
+ gAudioDevicePortCstor = env->GetMethodID(audioDevicePortClass, "<init>",
+ "(Landroid/media/AudioHandle;[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V");
+
+ jclass audioMixPortClass = env->FindClass("android/media/AudioMixPort");
+ gAudioMixPortClass = (jclass) env->NewGlobalRef(audioMixPortClass);
+ gAudioMixPortCstor = env->GetMethodID(audioMixPortClass, "<init>",
+ "(Landroid/media/AudioHandle;I[I[I[I[Landroid/media/AudioGain;)V");
+
+ jclass audioGainClass = env->FindClass("android/media/AudioGain");
+ gAudioGainClass = (jclass) env->NewGlobalRef(audioGainClass);
+ gAudioGainCstor = env->GetMethodID(audioGainClass, "<init>", "(IIIIIIIII)V");
+
+ jclass audioGainConfigClass = env->FindClass("android/media/AudioGainConfig");
+ gAudioGainConfigClass = (jclass) env->NewGlobalRef(audioGainConfigClass);
+ gAudioGainConfigCstor = env->GetMethodID(audioGainConfigClass, "<init>",
+ "(ILandroid/media/AudioGain;II[II)V");
+ gAudioGainConfigFields.mIndex = env->GetFieldID(gAudioGainConfigClass, "mIndex", "I");
+ gAudioGainConfigFields.mMode = env->GetFieldID(audioGainConfigClass, "mMode", "I");
+ gAudioGainConfigFields.mChannelMask = env->GetFieldID(audioGainConfigClass, "mChannelMask",
+ "I");
+ gAudioGainConfigFields.mValues = env->GetFieldID(audioGainConfigClass, "mValues", "[I");
+ gAudioGainConfigFields.mRampDurationMs = env->GetFieldID(audioGainConfigClass,
+ "mRampDurationMs", "I");
+
+ jclass audioPatchClass = env->FindClass("android/media/AudioPatch");
+ gAudioPatchClass = (jclass) env->NewGlobalRef(audioPatchClass);
+ gAudioPatchCstor = env->GetMethodID(audioPatchClass, "<init>",
+"(Landroid/media/AudioHandle;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)V");
+ gAudioPatchFields.mHandle = env->GetFieldID(audioPatchClass, "mHandle",
+ "Landroid/media/AudioHandle;");
+
+ jclass eventHandlerClass = env->FindClass(kEventHandlerClassPathName);
+ gPostEventFromNative = env->GetStaticMethodID(eventHandlerClass, "postEventFromNative",
+ "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+
+
AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback);
- return AndroidRuntime::registerNativeMethods(env,
+ int status = AndroidRuntime::registerNativeMethods(env,
kClassPathName, gMethods, NELEM(gMethods));
+
+ if (status == 0) {
+ status = AndroidRuntime::registerNativeMethods(env,
+ kEventHandlerClassPathName, gEventHandlerMethods, NELEM(gEventHandlerMethods));
+ }
+ return status;
}
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index c5dd06f..ecdfeb2 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -91,53 +91,9 @@ static struct {
} gRectClassInfo;
// ----------------------------------------------------------------------------
-// Caching
-// ----------------------------------------------------------------------------
-
-static void android_view_GLES20Canvas_flushCaches(JNIEnv* env, jobject clazz,
- jint mode) {
- if (Caches::hasInstance()) {
- Caches::getInstance().flush(static_cast<Caches::FlushMode>(mode));
- }
-}
-
-static jboolean android_view_GLES20Canvas_initCaches(JNIEnv* env, jobject clazz) {
- if (Caches::hasInstance()) {
- return Caches::getInstance().init() ? JNI_TRUE : JNI_FALSE;
- }
- return JNI_FALSE;
-}
-
-static void android_view_GLES20Canvas_terminateCaches(JNIEnv* env, jobject clazz) {
- if (Caches::hasInstance()) {
- Caches::getInstance().terminate();
- }
-}
-
-// ----------------------------------------------------------------------------
-// Caching
-// ----------------------------------------------------------------------------
-
-static void android_view_GLES20Canvas_initAtlas(JNIEnv* env, jobject clazz,
- jobject graphicBuffer, jlongArray atlasMapArray, jint count) {
-
- sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer);
- jlong* jAtlasMap = env->GetLongArrayElements(atlasMapArray, NULL);
- Caches::getInstance().assetAtlas.init(buffer, jAtlasMap, count);
- env->ReleaseLongArrayElements(atlasMapArray, jAtlasMap, 0);
-}
-
-// ----------------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------------
-static jlong android_view_GLES20Canvas_createRenderer(JNIEnv* env, jobject clazz) {
- RENDERER_LOGD("Create OpenGLRenderer");
- OpenGLRenderer* renderer = new OpenGLRenderer();
- renderer->initProperties();
- return reinterpret_cast<jlong>(renderer);
-}
-
static void android_view_GLES20Canvas_destroyRenderer(JNIEnv* env, jobject clazz,
jlong rendererPtr) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
@@ -174,10 +130,6 @@ static void android_view_GLES20Canvas_finish(JNIEnv* env, jobject clazz,
renderer->finish();
}
-static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz) {
- return Stencil::getStencilSize();
-}
-
static void android_view_GLES20Canvas_setProperty(JNIEnv* env,
jobject clazz, jstring name, jstring value) {
if (!Caches::hasInstance()) {
@@ -373,7 +325,7 @@ static void android_view_GLES20Canvas_setMatrix(JNIEnv* env, jobject clazz,
jlong rendererPtr, jlong matrixPtr) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
- renderer->setMatrix(matrix);
+ renderer->setMatrix(matrix ? *matrix : SkMatrix::I());
}
static void android_view_GLES20Canvas_getMatrix(JNIEnv* env, jobject clazz,
@@ -387,7 +339,7 @@ static void android_view_GLES20Canvas_concatMatrix(JNIEnv* env, jobject clazz,
jlong rendererPtr, jlong matrixPtr) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
- renderer->concatMatrix(matrix);
+ renderer->concatMatrix(*matrix);
}
// ----------------------------------------------------------------------------
@@ -430,7 +382,7 @@ static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject claz
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
- renderer->drawBitmap(bitmap, matrix, paint);
+ renderer->drawBitmap(bitmap, *matrix, paint);
}
static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
@@ -577,15 +529,6 @@ static void android_view_GLES20Canvas_drawRegionAsRects(JNIEnv* env, jobject cla
}
}
-static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject clazz,
- jlong rendererPtr, jfloatArray rects, jint count, jlong paintPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- jfloat* storage = env->GetFloatArrayElements(rects, NULL);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
- renderer->drawRects(storage, count, paint);
- env->ReleaseFloatArrayElements(rects, storage, 0);
-}
-
static void android_view_GLES20Canvas_drawPoints(JNIEnv* env, jobject clazz,
jlong rendererPtr, jfloatArray points, jint offset, jint count, jlong paintPtr) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
@@ -924,39 +867,6 @@ static void android_view_GLES20Canvas_drawLayer(JNIEnv* env, jobject clazz,
renderer->drawLayer(layer, x, y);
}
-static jboolean android_view_GLES20Canvas_copyLayer(JNIEnv* env, jobject clazz,
- jlong layerPtr, jlong bitmapPtr) {
- Layer* layer = reinterpret_cast<Layer*>(layerPtr);
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
- return LayerRenderer::copyLayer(layer, bitmap);
-}
-
-static void android_view_GLES20Canvas_pushLayerUpdate(JNIEnv* env, jobject clazz,
- jlong rendererPtr, jlong layerPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- Layer* layer = reinterpret_cast<Layer*>(layerPtr);
- renderer->pushLayerUpdate(layer);
-}
-
-static void android_view_GLES20Canvas_cancelLayerUpdate(JNIEnv* env, jobject clazz,
- jlong rendererPtr, jlong layerPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- Layer* layer = reinterpret_cast<Layer*>(layerPtr);
- renderer->cancelLayerUpdate(layer);
-}
-
-static void android_view_GLES20Canvas_clearLayerUpdates(JNIEnv* env, jobject clazz,
- jlong rendererPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- renderer->clearLayerUpdates();
-}
-
-static void android_view_GLES20Canvas_flushLayerUpdates(JNIEnv* env, jobject clazz,
- jlong rendererPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- renderer->flushLayerUpdates();
-}
-
#endif // USE_OPENGL_RENDERER
// ----------------------------------------------------------------------------
@@ -1001,14 +911,7 @@ static JNINativeMethod gMethods[] = {
{ "nIsAvailable", "()Z", (void*) android_view_GLES20Canvas_isAvailable },
#ifdef USE_OPENGL_RENDERER
- { "nFlushCaches", "(I)V", (void*) android_view_GLES20Canvas_flushCaches },
- { "nInitCaches", "()Z", (void*) android_view_GLES20Canvas_initCaches },
- { "nTerminateCaches", "()V", (void*) android_view_GLES20Canvas_terminateCaches },
- { "nInitAtlas", "(Landroid/view/GraphicBuffer;[JI)V",
- (void*) android_view_GLES20Canvas_initAtlas },
-
- { "nCreateRenderer", "()J", (void*) android_view_GLES20Canvas_createRenderer },
{ "nDestroyRenderer", "(J)V", (void*) android_view_GLES20Canvas_destroyRenderer },
{ "nSetViewport", "(JII)V", (void*) android_view_GLES20Canvas_setViewport },
{ "nPrepare", "(JZ)I", (void*) android_view_GLES20Canvas_prepare },
@@ -1017,9 +920,6 @@ static JNINativeMethod gMethods[] = {
{ "nSetProperty", "(Ljava/lang/String;Ljava/lang/String;)V",
(void*) android_view_GLES20Canvas_setProperty },
-
- { "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize },
-
{ "nCallDrawGLFunction", "(JJ)I", (void*) android_view_GLES20Canvas_callDrawGLFunction },
{ "nSave", "(JI)I", (void*) android_view_GLES20Canvas_save },
@@ -1059,7 +959,6 @@ static JNINativeMethod gMethods[] = {
{ "nDrawColor", "(JII)V", (void*) android_view_GLES20Canvas_drawColor },
{ "nDrawRect", "(JFFFFJ)V", (void*) android_view_GLES20Canvas_drawRect },
{ "nDrawRects", "(JJJ)V", (void*) android_view_GLES20Canvas_drawRegionAsRects },
- { "nDrawRects", "(J[FIJ)V", (void*) android_view_GLES20Canvas_drawRects },
{ "nDrawRoundRect", "(JFFFFFFJ)V", (void*) android_view_GLES20Canvas_drawRoundRect },
{ "nDrawCircle", "(JFFFJ)V", (void*) android_view_GLES20Canvas_drawCircle },
{ "nDrawCircle", "(JJJJJ)V", (void*) android_view_GLES20Canvas_drawCircleProps },
@@ -1099,11 +998,6 @@ static JNINativeMethod gMethods[] = {
{ "nCreateDisplayListRenderer", "()J", (void*) android_view_GLES20Canvas_createDisplayListRenderer },
{ "nDrawLayer", "(JJFF)V", (void*) android_view_GLES20Canvas_drawLayer },
- { "nCopyLayer", "(JJ)Z", (void*) android_view_GLES20Canvas_copyLayer },
- { "nClearLayerUpdates", "(J)V", (void*) android_view_GLES20Canvas_clearLayerUpdates },
- { "nFlushLayerUpdates", "(J)V", (void*) android_view_GLES20Canvas_flushLayerUpdates },
- { "nPushLayerUpdate", "(JJ)V", (void*) android_view_GLES20Canvas_pushLayerUpdate },
- { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_GLES20Canvas_cancelLayerUpdate },
{ "nGetMaximumTextureWidth", "()I", (void*) android_view_GLES20Canvas_getMaxTextureWidth },
{ "nGetMaximumTextureHeight", "()I", (void*) android_view_GLES20Canvas_getMaxTextureHeight },
diff --git a/core/jni/android_view_GLRenderer.cpp b/core/jni/android_view_GLRenderer.cpp
deleted file mode 100644
index d0269a3..0000000
--- a/core/jni/android_view_GLRenderer.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#define LOG_TAG "GLRenderer"
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <EGL/egl_cache.h>
-
-#include <utils/Timers.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
-#include <Caches.h>
-#include <Extensions.h>
-#include <LayerRenderer.h>
-#include <RenderNode.h>
-
-#ifdef USE_OPENGL_RENDERER
- EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
-#endif
-
-namespace android {
-
-/**
- * Note: OpenGLRenderer JNI layer is generated and compiled only on supported
- * devices. This means all the logic must be compiled only when the
- * preprocessor variable USE_OPENGL_RENDERER is defined.
- */
-#ifdef USE_OPENGL_RENDERER
-
-// ----------------------------------------------------------------------------
-// Defines
-// ----------------------------------------------------------------------------
-
-// Debug
-#define DEBUG_RENDERER 0
-
-// Debug
-#if DEBUG_RENDERER
- #define RENDERER_LOGD(...) ALOGD(__VA_ARGS__)
-#else
- #define RENDERER_LOGD(...)
-#endif
-
-// ----------------------------------------------------------------------------
-// Surface and display management
-// ----------------------------------------------------------------------------
-
-static jboolean android_view_GLRenderer_preserveBackBuffer(JNIEnv* env, jobject clazz) {
- EGLDisplay display = eglGetCurrentDisplay();
- EGLSurface surface = eglGetCurrentSurface(EGL_DRAW);
-
- eglGetError();
- eglSurfaceAttrib(display, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
-
- EGLint error = eglGetError();
- if (error != EGL_SUCCESS) {
- RENDERER_LOGD("Could not enable buffer preserved swap behavior (%x)", error);
- }
-
- return error == EGL_SUCCESS;
-}
-
-static jboolean android_view_GLRenderer_isBackBufferPreserved(JNIEnv* env, jobject clazz) {
- EGLDisplay display = eglGetCurrentDisplay();
- EGLSurface surface = eglGetCurrentSurface(EGL_DRAW);
- EGLint value;
-
- eglGetError();
- eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &value);
-
- EGLint error = eglGetError();
- if (error != EGL_SUCCESS) {
- RENDERER_LOGD("Could not query buffer preserved swap behavior (%x)", error);
- }
-
- return error == EGL_SUCCESS && value == EGL_BUFFER_PRESERVED;
-}
-
-// ----------------------------------------------------------------------------
-// Tracing and debugging
-// ----------------------------------------------------------------------------
-
-static bool android_view_GLRenderer_loadProperties(JNIEnv* env, jobject clazz) {
- if (uirenderer::Caches::hasInstance()) {
- return uirenderer::Caches::getInstance().initProperties();
- }
- return false;
-}
-
-static void android_view_GLRenderer_beginFrame(JNIEnv* env, jobject clazz,
- jintArray size) {
-
- EGLDisplay display = eglGetCurrentDisplay();
- EGLSurface surface = eglGetCurrentSurface(EGL_DRAW);
-
- if (size) {
- EGLint value;
- jint* storage = env->GetIntArrayElements(size, NULL);
-
- eglQuerySurface(display, surface, EGL_WIDTH, &value);
- storage[0] = value;
-
- eglQuerySurface(display, surface, EGL_HEIGHT, &value);
- storage[1] = value;
-
- env->ReleaseIntArrayElements(size, storage, 0);
- }
-
- eglBeginFrame(display, surface);
-}
-
-static jlong android_view_GLRenderer_getSystemTime(JNIEnv* env, jobject clazz) {
- if (uirenderer::Extensions::getInstance().hasNvSystemTime()) {
- return eglGetSystemTimeNV();
- }
- return systemTime(SYSTEM_TIME_MONOTONIC);
-}
-
-static void android_view_GLRenderer_destroyLayer(JNIEnv* env, jobject clazz,
- jlong layerPtr) {
- using namespace android::uirenderer;
- Layer* layer = reinterpret_cast<Layer*>(layerPtr);
- LayerRenderer::destroyLayer(layer);
-}
-
-static void android_view_GLRenderer_prepareTree(JNIEnv* env, jobject clazz,
- jlong renderNodePtr) {
- using namespace android::uirenderer;
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- TreeInfo ignoredInfo;
- renderNode->prepareTree(ignoredInfo);
-}
-
-static void android_view_GLRenderer_invokeFunctor(JNIEnv* env, jobject clazz,
- jlong functorPtr, jboolean hasContext) {
- using namespace android::uirenderer;
- Functor* functor = reinterpret_cast<Functor*>(functorPtr);
- DrawGlInfo::Mode mode = hasContext ? DrawGlInfo::kModeProcess : DrawGlInfo::kModeProcessNoContext;
- (*functor)(mode, NULL);
-}
-
-#endif // USE_OPENGL_RENDERER
-
-// ----------------------------------------------------------------------------
-// Shaders
-// ----------------------------------------------------------------------------
-
-static void android_view_GLRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
- jstring diskCachePath) {
-
- const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
- egl_cache_t::get()->setCacheFilename(cacheArray);
- env->ReleaseStringUTFChars(diskCachePath, cacheArray);
-}
-
-// ----------------------------------------------------------------------------
-// JNI Glue
-// ----------------------------------------------------------------------------
-
-const char* const kClassPathName = "android/view/GLRenderer";
-
-static JNINativeMethod gMethods[] = {
-#ifdef USE_OPENGL_RENDERER
- { "isBackBufferPreserved", "()Z", (void*) android_view_GLRenderer_isBackBufferPreserved },
- { "preserveBackBuffer", "()Z", (void*) android_view_GLRenderer_preserveBackBuffer },
- { "loadProperties", "()Z", (void*) android_view_GLRenderer_loadProperties },
-
- { "beginFrame", "([I)V", (void*) android_view_GLRenderer_beginFrame },
-
- { "getSystemTime", "()J", (void*) android_view_GLRenderer_getSystemTime },
- { "nDestroyLayer", "(J)V", (void*) android_view_GLRenderer_destroyLayer },
- { "nPrepareTree", "(J)V", (void*) android_view_GLRenderer_prepareTree },
- { "nInvokeFunctor", "(JZ)V", (void*) android_view_GLRenderer_invokeFunctor },
-#endif
-
- { "setupShadersDiskCache", "(Ljava/lang/String;)V",
- (void*) android_view_GLRenderer_setupShadersDiskCache },
-};
-
-int register_android_view_GLRenderer(JNIEnv* env) {
- return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
-}
-
-};
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 8a426ac..0210bd9 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -21,6 +21,7 @@
#include "android_os_Parcel.h"
#include "android_view_GraphicBuffer.h"
+#include "android/graphics/GraphicsJNI.h"
#include <android_runtime/AndroidRuntime.h>
@@ -75,7 +76,7 @@ static struct {
static struct {
jfieldID mSurfaceFormat;
- jmethodID safeCanvasSwap;
+ jmethodID setNativeBitmap;
} gCanvasClassInfo;
#define GET_INT(object, field) \
@@ -197,12 +198,11 @@ static jboolean android_view_GraphicBuffer_lockCanvas(JNIEnv* env, jobject,
}
SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer->getPixelFormat());
-
- SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
- INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+ INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(&bitmap));
SkRect clipRect;
clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
+ SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
nativeCanvas->clipRect(clipRect);
if (dirtyRect) {
@@ -218,8 +218,7 @@ static jboolean android_view_GraphicBuffer_unlockCanvasAndPost(JNIEnv* env, jobj
GraphicBufferWrapper* wrapper =
reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
- SkCanvas* nativeCanvas = SkNEW(SkCanvas);
- INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+ INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
if (wrapper) {
status_t status = wrapper->buffer->unlock();
@@ -319,7 +318,7 @@ int register_android_view_GraphicBuffer(JNIEnv* env) {
FIND_CLASS(clazz, "android/graphics/Canvas");
GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I");
- GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V");
+ GET_METHOD_ID(gCanvasClassInfo.setNativeBitmap, clazz, "setNativeBitmap", "(J)V");
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index b2f17de..879836d 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -43,41 +43,12 @@ using namespace uirenderer;
#ifdef USE_OPENGL_RENDERER
-static jlong android_view_HardwareLayer_createTextureLayer(JNIEnv* env, jobject clazz) {
- Layer* layer = LayerRenderer::createTextureLayer();
- if (!layer) return 0;
-
- return reinterpret_cast<jlong>( new DeferredLayerUpdater(layer) );
-}
-
-static jlong android_view_HardwareLayer_createRenderLayer(JNIEnv* env, jobject clazz,
- jint width, jint height) {
- Layer* layer = LayerRenderer::createRenderLayer(width, height);
- if (!layer) return 0;
-
- OpenGLRenderer* renderer = new LayerRenderer(layer);
- renderer->initProperties();
- return reinterpret_cast<jlong>( new DeferredLayerUpdater(layer, renderer) );
-}
-
static void android_view_HardwareLayer_onTextureDestroyed(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
layer->backingLayer()->clearTexture();
}
-static jlong android_view_HardwareLayer_detachBackingLayer(JNIEnv* env, jobject clazz,
- jlong layerUpdaterPtr) {
- DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
- return reinterpret_cast<jlong>( layer->detachBackingLayer() );
-}
-
-static void android_view_HardwareLayer_destroyLayerUpdater(JNIEnv* env, jobject clazz,
- jlong layerUpdaterPtr) {
- DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
- delete layer;
-}
-
static jboolean android_view_HardwareLayer_prepare(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr, jint width, jint height, jboolean isOpaque) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
@@ -154,11 +125,7 @@ const char* const kClassPathName = "android/view/HardwareLayer";
static JNINativeMethod gMethods[] = {
#ifdef USE_OPENGL_RENDERER
- { "nCreateTextureLayer", "()J", (void*) android_view_HardwareLayer_createTextureLayer },
- { "nCreateRenderLayer", "(II)J", (void*) android_view_HardwareLayer_createRenderLayer },
{ "nOnTextureDestroyed", "(J)V", (void*) android_view_HardwareLayer_onTextureDestroyed },
- { "nDetachBackingLayer", "(J)J", (void*) android_view_HardwareLayer_detachBackingLayer },
- { "nDestroyLayerUpdater", "(J)V", (void*) android_view_HardwareLayer_destroyLayerUpdater },
{ "nPrepare", "(JIIZ)Z", (void*) android_view_HardwareLayer_prepare },
{ "nSetLayerPaint", "(JJ)V", (void*) android_view_HardwareLayer_setLayerPaint },
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index e19ce36..d689864 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -116,6 +116,11 @@ static jlong createCanvasPropertyPaintAnimator(JNIEnv* env, jobject clazz,
return reinterpret_cast<jlong>( animator );
}
+static void setStartValue(JNIEnv* env, jobject clazz, jlong animatorPtr, jfloat startValue) {
+ BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+ animator->setStartValue(startValue);
+}
+
static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong duration) {
LOG_ALWAYS_FATAL_IF(duration < 0, "Duration cannot be negative");
BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
@@ -157,6 +162,7 @@ static JNINativeMethod gMethods[] = {
{ "nCreateAnimator", "(Ljava/lang/ref/WeakReference;IF)J", (void*) createAnimator },
{ "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JF)J", (void*) createCanvasPropertyFloatAnimator },
{ "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyPaintAnimator },
+ { "nSetStartValue", "(JF)V", (void*) setStartValue },
{ "nSetDuration", "(JJ)V", (void*) setDuration },
{ "nGetDuration", "(J)J", (void*) getDuration },
{ "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 6c9d060..11f87cc 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -70,7 +70,7 @@ static struct {
static struct {
jfieldID mSurfaceFormat;
- jmethodID safeCanvasSwap;
+ jmethodID setNativeBitmap;
} gCanvasClassInfo;
// ----------------------------------------------------------------------------
@@ -232,10 +232,11 @@ static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
bitmap.setPixels(NULL);
}
- SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
- env->CallVoidMethod(canvasObj, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+ env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap,
+ reinterpret_cast<jlong>(&bitmap));
if (dirtyRectPtr) {
+ SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
nativeCanvas->clipRect( SkRect::Make(reinterpret_cast<const SkIRect&>(dirtyRect)) );
}
@@ -262,8 +263,7 @@ static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
}
// detach the canvas from the surface
- SkCanvas* nativeCanvas = SkNEW(SkCanvas);
- env->CallVoidMethod(canvasObj, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+ env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap, (jlong)0);
// unlock surface
status_t err = surface->unlockAndPost();
@@ -375,7 +375,7 @@ int register_android_view_Surface(JNIEnv* env)
clazz = env->FindClass("android/graphics/Canvas");
gCanvasClassInfo.mSurfaceFormat = env->GetFieldID(clazz, "mSurfaceFormat", "I");
- gCanvasClassInfo.safeCanvasSwap = env->GetMethodID(clazz, "safeCanvasSwap", "(JZ)V");
+ gCanvasClassInfo.setNativeBitmap = env->GetMethodID(clazz, "setNativeBitmap", "(J)V");
clazz = env->FindClass("android/graphics/Rect");
gRectClassInfo.left = env->GetFieldID(clazz, "left", "I");
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 9258543..c1ab515 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -29,6 +29,8 @@
#include <SkCanvas.h>
#include <SkImage.h>
+#include "android/graphics/GraphicsJNI.h"
+
namespace android {
// ----------------------------------------------------------------------------
@@ -45,7 +47,7 @@ static struct {
static struct {
jfieldID mSurfaceFormat;
- jmethodID safeCanvasSwap;
+ jmethodID setNativeBitmap;
} gCanvasClassInfo;
static struct {
@@ -159,12 +161,11 @@ static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject,
}
SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer.format);
-
- SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
- INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+ INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(&bitmap));
SkRect clipRect;
clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
+ SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
nativeCanvas->clipRect(clipRect);
if (dirtyRect) {
@@ -178,8 +179,7 @@ static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject,
static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject,
jlong nativeWindow, jobject canvas) {
- SkCanvas* nativeCanvas = SkNEW(SkCanvas);
- INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+ INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
if (nativeWindow) {
sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
@@ -228,7 +228,7 @@ int register_android_view_TextureView(JNIEnv* env) {
FIND_CLASS(clazz, "android/graphics/Canvas");
GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I");
- GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V");
+ GET_METHOD_ID(gCanvasClassInfo.setNativeBitmap, clazz, "setNativeBitmap", "(J)V");
FIND_CLASS(clazz, "android/view/TextureView");
GET_FIELD_ID(gTextureViewClassInfo.nativeWindow, clazz, "mNativeWindow", "J");
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index bd016fd..a550649 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -22,6 +22,10 @@
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <EGL/egl_cache.h>
+
#include <utils/StrongPointer.h>
#include <android_runtime/android_view_Surface.h>
#include <system/window.h>
@@ -287,11 +291,18 @@ static jboolean android_view_ThreadedRenderer_copyLayerInto(JNIEnv* env, jobject
return proxy->copyLayerInto(layer, bitmap);
}
-static void android_view_ThreadedRenderer_destroyLayer(JNIEnv* env, jobject clazz,
+static void android_view_ThreadedRenderer_pushLayerUpdate(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jlong layerPtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
+ proxy->pushLayerUpdate(layer);
+}
+
+static void android_view_ThreadedRenderer_cancelLayerUpdate(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlong layerPtr) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
- proxy->destroyLayer(layer);
+ proxy->cancelLayerUpdate(layer);
}
static void android_view_ThreadedRenderer_flushCaches(JNIEnv* env, jobject clazz,
@@ -322,6 +333,18 @@ static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject c
#endif
// ----------------------------------------------------------------------------
+// Shaders
+// ----------------------------------------------------------------------------
+
+static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
+ jstring diskCachePath) {
+
+ const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
+ egl_cache_t::get()->setCacheFilename(cacheArray);
+ env->ReleaseStringUTFChars(diskCachePath, cacheArray);
+}
+
+// ----------------------------------------------------------------------------
// JNI Glue
// ----------------------------------------------------------------------------
@@ -347,12 +370,15 @@ static JNINativeMethod gMethods[] = {
{ "nCreateDisplayListLayer", "(JII)J", (void*) android_view_ThreadedRenderer_createDisplayListLayer },
{ "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer },
{ "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto },
- { "nDestroyLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_destroyLayer },
+ { "nPushLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_pushLayerUpdate },
+ { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_cancelLayerUpdate },
{ "nFlushCaches", "(JI)V", (void*) android_view_ThreadedRenderer_flushCaches },
{ "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
{ "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
{ "nDumpProfileInfo", "(JLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
#endif
+ { "setupShadersDiskCache", "(Ljava/lang/String;)V",
+ (void*) android_view_ThreadedRenderer_setupShadersDiskCache },
};
int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 230658f..e55e4ea 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -46,6 +46,9 @@
#define LIB_SUFFIX ".so"
#define LIB_SUFFIX_LEN (sizeof(LIB_SUFFIX) - 1)
+#define RS_BITCODE_SUFFIX ".bc"
+#define RS_BITCODE_SUFFIX_LEN (sizeof(RS_BITCODE_SUFFIX) -1)
+
#define GDBSERVER "gdbserver"
#define GDBSERVER_LEN (sizeof(GDBSERVER) - 1)
@@ -486,6 +489,42 @@ com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, j
return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch);
}
+enum bitcode_scan_result_t {
+ APK_SCAN_ERROR = -1,
+ NO_BITCODE_PRESENT = 0,
+ BITCODE_PRESENT = 1,
+};
+
+static jint
+com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode(JNIEnv *env, jclass clazz,
+ jlong apkHandle) {
+ ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
+ void* cookie = NULL;
+ if (!zipFile->startIteration(&cookie)) {
+ return APK_SCAN_ERROR;
+ }
+
+ char fileName[PATH_MAX];
+ ZipEntryRO next = NULL;
+ while ((next = zipFile->nextEntry(cookie)) != NULL) {
+ if (zipFile->getEntryFileName(next, fileName, sizeof(fileName))) {
+ continue;
+ }
+
+ const size_t fileNameLen = strlen(fileName);
+ const char* lastSlash = strrchr(fileName, '/');
+ const char* baseName = (lastSlash == NULL) ? fileName : fileName + 1;
+ if (!strncmp(fileName + fileNameLen - RS_BITCODE_SUFFIX_LEN, RS_BITCODE_SUFFIX,
+ RS_BITCODE_SUFFIX_LEN) && isFilenameSafe(baseName)) {
+ zipFile->endIteration(cookie);
+ return BITCODE_PRESENT;
+ }
+ }
+
+ zipFile->endIteration(cookie);
+ return NO_BITCODE_PRESENT;
+}
+
static jlong
com_android_internal_content_NativeLibraryHelper_openApk(JNIEnv *env, jclass, jstring apkPath)
{
@@ -517,6 +556,8 @@ static JNINativeMethod gMethods[] = {
{"nativeFindSupportedAbi",
"(J[Ljava/lang/String;)I",
(void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi},
+ {"hasRenderscriptBitcode", "(J)I",
+ (void *)com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode},
};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 14141d7..bb6a1cb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -258,6 +258,12 @@
<protected-broadcast
android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
+ <!-- Defined in RestrictionsManager -->
+ <protected-broadcast
+ android:name="android.intent.action.PERMISSION_RESPONSE_RECEIVED" />
+ <!-- Defined in RestrictionsManager -->
+ <protected-broadcast android:name="android.intent.action.REQUEST_PERMISSION" />
+
<!-- ====================================== -->
<!-- Permissions for things that cost money -->
<!-- ====================================== -->
diff --git a/core/res/res/anim/input_method_exit.xml b/core/res/res/anim/input_method_exit.xml
index 4c4f6a4..117774a 100644
--- a/core/res/res/anim/input_method_exit.xml
+++ b/core/res/res/anim/input_method_exit.xml
@@ -17,7 +17,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
- <translate android:fromYDelta="0" android:toYDelta="-20%"
+ <translate android:fromYDelta="0" android:toYDelta="10%"
android:interpolator="@interpolator/accelerate_quint"
android:duration="@android:integer/config_shortAnimTime"/>
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
diff --git a/core/res/res/anim/lock_screen_behind_enter.xml b/core/res/res/anim/lock_screen_behind_enter.xml
index 4a956d7..7e212be 100644
--- a/core/res/res/anim/lock_screen_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_behind_enter.xml
@@ -18,10 +18,17 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false">
+ android:detachWallpaper="true" android:shareInterpolator="false" android:startOffset="60">
<alpha
- android:fromAlpha="1.0" android:toAlpha="1.0"
+ android:fromAlpha="0.0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true"
- android:interpolator="@interpolator/decelerate_quint"
- android:duration="0"/>
+ android:interpolator="@interpolator/linear_out_slow_in"
+ android:duration="@integer/config_shortAnimTime"/>
+ <scale
+ android:fromXScale="0.95" android:toXScale="1.0"
+ android:fromYScale="0.95" android:toYScale="1.0"
+ android:pivotX="50%" android:pivotY="50%"
+ android:fillEnabled="true" android:fillBefore="true"
+ android:interpolator="@interpolator/linear_out_slow_in"
+ android:duration="@integer/config_shortAnimTime" />
</set> \ No newline at end of file
diff --git a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml b/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
deleted file mode 100644
index f7a6a65..0000000
--- a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:detachWallpaper="true" android:shareInterpolator="false">
- <alpha
- android:fromAlpha="0.0" android:toAlpha="1.0"
- android:fillEnabled="true" android:fillBefore="true"
- android:interpolator="@interpolator/decelerate_quad"
- android:startOffset="@android:integer/config_mediumAnimTime"
- android:duration="@android:integer/config_shortAnimTime"/>
-</set>
diff --git a/core/res/res/anim/voice_activity_open_enter.xml b/core/res/res/anim/voice_activity_open_enter.xml
index 57fba2a..ce7a4f9 100644
--- a/core/res/res/anim/voice_activity_open_enter.xml
+++ b/core/res/res/anim/voice_activity_open_enter.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* //device/apps/common/res/anim/fade_in.xml
**
** Copyright 2007, The Android Open Source Project
**
diff --git a/core/res/res/anim/voice_layer_enter.xml b/core/res/res/anim/voice_layer_enter.xml
index 57fba2a..ce7a4f9 100644
--- a/core/res/res/anim/voice_layer_enter.xml
+++ b/core/res/res/anim/voice_layer_enter.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* //device/apps/common/res/anim/fade_in.xml
**
** Copyright 2007, The Android Open Source Project
**
diff --git a/core/res/res/color/btn_default_quantum_dark.xml b/core/res/res/color/btn_default_quantum_dark.xml
index f2e772d..ec0f140 100644
--- a/core/res/res/color/btn_default_quantum_dark.xml
+++ b/core/res/res/color/btn_default_quantum_dark.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:alpha="0.5" android:color="@color/quantum_grey_700"/>
- <item android:color="@color/quantum_grey_700"/>
+ <item android:state_enabled="false" android:alpha="0.5" android:color="@color/button_quantum_dark"/>
+ <item android:color="@color/button_quantum_dark"/>
</selector>
diff --git a/core/res/res/color/btn_default_quantum_light.xml b/core/res/res/color/btn_default_quantum_light.xml
index de1bd2c..9536d24 100644
--- a/core/res/res/color/btn_default_quantum_light.xml
+++ b/core/res/res/color/btn_default_quantum_light.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:alpha="0.5" android:color="@color/quantum_grey_300"/>
- <item android:color="@color/quantum_grey_300"/>
+ <item android:state_enabled="false" android:alpha="0.5" android:color="@color/button_quantum_light"/>
+ <item android:color="@color/button_quantum_light"/>
</selector>
diff --git a/core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.png
deleted file mode 100644
index ba5bd01..0000000
--- a/core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.png
deleted file mode 100644
index 4e2612d..0000000
--- a/core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.png
deleted file mode 100644
index e6ca1ea..0000000
--- a/core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.png
deleted file mode 100644
index d6018dd..0000000
--- a/core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/ic_lock_bugreport.xml b/core/res/res/drawable/ic_lock_bugreport.xml
index a3f82ce..b93a09a 100644
--- a/core/res/res/drawable/ic_lock_bugreport.xml
+++ b/core/res/res/drawable/ic_lock_bugreport.xml
@@ -1,19 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+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
+ 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
+ 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.
+ 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.
-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="32dp"
+ android:height="32dp"/>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_lock_bugreport_alpha"
- android:tint="?attr/colorControlNormal" />
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="?attr/colorControlNormal"
+ android:pathData="M20.0,8.0l-2.8,0.0c-0.5,-0.8 -1.1,-1.5 -1.8,-2.0L17.0,4.4L15.6,3.0l-2.2,2.2C13.0,5.1 12.5,5.0 12.0,5.0s-1.0,0.1 -1.4,0.2L8.4,3.0L7.0,4.4L8.6,6.0C7.9,6.5 7.3,7.2 6.8,8.0L4.0,8.0l0.0,2.0l2.1,0.0C6.0,10.3 6.0,10.7 6.0,11.0l0.0,1.0L4.0,12.0l0.0,2.0l2.0,0.0l0.0,1.0c0.0,0.3 0.0,0.7 0.1,1.0L4.0,16.0l0.0,2.0l2.8,0.0c1.0,1.8 3.0,3.0 5.2,3.0s4.2,-1.2 5.2,-3.0L20.0,18.0l0.0,-2.0l-2.1,0.0c0.1,-0.3 0.1,-0.7 0.1,-1.0l0.0,-1.0l2.0,0.0l0.0,-2.0l-2.0,0.0l0.0,-1.0c0.0,-0.3 0.0,-0.7 -0.1,-1.0L20.0,10.0L20.0,8.0zM14.0,16.0l-4.0,0.0l0.0,-2.0l4.0,0.0L14.0,16.0zM14.0,12.0l-4.0,0.0l0.0,-2.0l4.0,0.0L14.0,12.0z"/>
+</vector>
diff --git a/core/res/res/drawable/list_selector_quantum.xml b/core/res/res/drawable/item_background_borderless_quantum.xml
index 6cd59e5..c2a1c127 100644
--- a/core/res/res/drawable/list_selector_quantum.xml
+++ b/core/res/res/drawable/item_background_borderless_quantum.xml
@@ -15,8 +15,5 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:tint="?attr/colorControlHighlight">
- <item android:id="@id/mask">
- <color android:color="@color/white" />
- </item>
-</ripple>
+ android:tint="?attr/colorControlHighlight"
+ android:pinned="true" />
diff --git a/core/res/res/drawable/item_background_quantum.xml b/core/res/res/drawable/item_background_quantum.xml
index c2a1c127..039ca51 100644
--- a/core/res/res/drawable/item_background_quantum.xml
+++ b/core/res/res/drawable/item_background_quantum.xml
@@ -15,5 +15,8 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:tint="?attr/colorControlHighlight"
- android:pinned="true" />
+ android:tint="?attr/colorControlHighlight">
+ <item android:id="@id/mask">
+ <color android:color="@color/white" />
+ </item>
+</ripple> \ No newline at end of file
diff --git a/core/res/res/layout/alert_dialog_quantum.xml b/core/res/res/layout/alert_dialog_quantum.xml
index e109425..7fd22ad 100644
--- a/core/res/res/layout/alert_dialog_quantum.xml
+++ b/core/res/res/layout/alert_dialog_quantum.xml
@@ -23,7 +23,10 @@
android:orientation="vertical"
android:background="@drawable/dialog_background_quantum"
android:translationZ="@dimen/floating_window_z"
- android:layout_margin="@dimen/floating_window_margin">
+ android:layout_marginLeft="@dimen/floating_window_margin_left"
+ android:layout_marginTop="@dimen/floating_window_margin_top"
+ android:layout_marginRight="@dimen/floating_window_margin_right"
+ android:layout_marginBottom="@dimen/floating_window_margin_bottom">
<LinearLayout android:id="@+id/topPanel"
android:layout_width="match_parent"
@@ -44,7 +47,7 @@
android:scaleType="fitCenter"
android:src="@null" />
<TextView android:id="@+id/alertTitle"
- style="?android:attr/windowTitleStyle"
+ style="?attr/windowTitleStyle"
android:singleLine="true"
android:ellipsize="end"
android:layout_width="match_parent"
@@ -65,7 +68,7 @@
android:layout_height="wrap_content"
android:clipToPadding="false">
<TextView android:id="@+id/message"
- style="?android:attr/textAppearanceMedium"
+ style="?attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dip"
@@ -92,26 +95,24 @@
android:gravity="end"
android:padding="16dip">
<LinearLayout
- style="?android:attr/buttonBarStyle"
+ style="?attr/buttonBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layoutDirection="locale">
<Button android:id="@+id/button3"
- style="?android:attr/buttonBarButtonStyle"
+ style="?attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginRight="8dip"
android:maxLines="2"
android:minHeight="@dimen/alert_dialog_button_bar_height" />
<Button android:id="@+id/button2"
- style="?android:attr/buttonBarButtonStyle"
+ style="?attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:minHeight="@dimen/alert_dialog_button_bar_height" />
<Button android:id="@+id/button1"
- style="?android:attr/buttonBarButtonStyle"
- android:layout_marginLeft="8dip"
+ style="?attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3bf777c..489adb4 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -890,9 +890,12 @@
with the appearance of a singel button broken into segments. -->
<attr name="segmentedButtonStyle" format="reference" />
- <!-- Background drawable for standalone items that need focus/pressed states. -->
+ <!-- Background drawable for bordered standalone items that need focus/pressed states. -->
<attr name="selectableItemBackground" format="reference" />
+ <!-- Background drawable for borderless standalone items that need focus/pressed states. -->
+ <attr name="selectableItemBackgroundBorderless" format="reference" />
+
<!-- Style for buttons without an explicit border, often used in groups. -->
<attr name="borderlessButtonStyle" format="reference" />
@@ -4664,11 +4667,11 @@
<!-- Drawable used to show animated touch feedback. -->
<declare-styleable name="RippleDrawable">
- <!-- The tint to use for feedback ripples. This attribute is required. -->
+ <!-- The tint to use for ripple effects. This attribute is required. -->
<attr name="tint" />
- <!-- Specifies the Porter-Duff blending mode used to apply the tint. The default vlaue is src_atop, which draws over the opaque parts of the drawable. -->
+ <!-- Specifies the Porter-Duff blending mode used to apply the tint. The default value is src_atop, which draws over the opaque parts of the drawable. -->
<attr name="tintMode" />
- <!-- Whether to pin feedback ripples to the center of the drawable. Default value is false. -->
+ <!-- Whether to pin ripple effects to the center of the drawable. Default value is false. -->
<attr name="pinned" format="boolean" />
</declare-styleable>
diff --git a/core/res/res/values/colors_quantum.xml b/core/res/res/values/colors_quantum.xml
index 556463e..976930c 100644
--- a/core/res/res/values/colors_quantum.xml
+++ b/core/res/res/values/colors_quantum.xml
@@ -16,8 +16,14 @@
<!-- Colors specific to Quantum themes. -->
<resources>
- <color name="background_quantum_dark">#ff303030</color>
- <color name="background_quantum_light">@color/white</color>
+ <color name="background_quantum_dark">#ff414042</color>
+ <color name="background_quantum_light">#fff1f2f2</color>
+
+ <color name="ripple_quantum_dark">#30ffffff</color>
+ <color name="ripple_quantum_light">#30000000</color>
+
+ <color name="button_quantum_dark">#ff5a595b</color>
+ <color name="button_quantum_light">#ffd6d7d7</color>
<color name="bright_foreground_quantum_dark">@color/white</color>
<color name="bright_foreground_quantum_light">@color/black</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e9d8ccc..f6732d3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1538,6 +1538,7 @@
-->
<string-array translatable="false" name="config_globalActionsList">
<item>power</item>
+ <item>bugreport</item>
<item>users</item>
</string-array>
diff --git a/core/res/res/values/dimens_quantum.xml b/core/res/res/values/dimens_quantum.xml
index 2defee2..b5ba1ca 100644
--- a/core/res/res/values/dimens_quantum.xml
+++ b/core/res/res/values/dimens_quantum.xml
@@ -52,7 +52,10 @@
<dimen name="text_size_small_quantum">14sp</dimen>
<dimen name="floating_window_z">16dp</dimen>
- <dimen name="floating_window_margin">32dp</dimen>
+ <dimen name="floating_window_margin_left">16dp</dimen>
+ <dimen name="floating_window_margin_top">8dp</dimen>
+ <dimen name="floating_window_margin_right">16dp</dimen>
+ <dimen name="floating_window_margin_bottom">32dp</dimen>
<!-- the amount of elevation for pressed button state-->
<dimen name="button_pressed_z">2dp</dimen>
diff --git a/core/res/res/values/donottranslate_quantum.xml b/core/res/res/values/donottranslate_quantum.xml
index 83cc4e5..e53c40e 100644
--- a/core/res/res/values/donottranslate_quantum.xml
+++ b/core/res/res/values/donottranslate_quantum.xml
@@ -27,6 +27,6 @@
<string name="font_family_body_1_quantum">sans-serif</string>
<string name="font_family_caption_quantum">sans-serif</string>
<string name="font_family_menu_quantum">sans-serif-medium</string>
- <string name="font_family_button_quantum">sans-serif</string>
+ <string name="font_family_button_quantum">sans-serif-medium</string>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9c33d80..7fc2f49 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2182,6 +2182,7 @@
<public type="attr" name="layout_columnWeight" />
<public type="attr" name="translateX" />
<public type="attr" name="translateY" />
+ <public type="attr" name="selectableItemBackgroundBorderless" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
@@ -2256,6 +2257,7 @@
<public type="style" name="Theme.Quantum.NoActionBar.Overscan" />
<public type="style" name="Theme.Quantum.NoActionBar.TranslucentDecor" />
<public type="style" name="Theme.Quantum.Panel" />
+ <public type="style" name="Theme.Quantum.Voice" />
<public type="style" name="Theme.Quantum.Wallpaper" />
<public type="style" name="Theme.Quantum.Wallpaper.NoTitleBar" />
@@ -2272,6 +2274,7 @@
<public type="style" name="Theme.Quantum.Light.NoActionBar.Overscan" />
<public type="style" name="Theme.Quantum.Light.NoActionBar.TranslucentDecor" />
<public type="style" name="Theme.Quantum.Light.Panel" />
+ <public type="style" name="Theme.Quantum.Light.Voice" />
<public type="style" name="ThemeOverlay" />
<public type="style" name="ThemeOverlay.Quantum" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6ae35e1..9ff67b4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -419,6 +419,8 @@
current device state, to send as an e-mail message. It will take a little
time from starting the bug report until it is ready to be sent; please be
patient.</string>
+ <!-- Format for build summary info [CHAR LIMIT=NONE] -->
+ <string name="bugreport_status" translatable="false">%s (%s)</string>
<!-- label for item that enables silent mode in phone options dialog -->
<string name="global_action_toggle_silent_mode">Silent mode</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 63f2674..59bd667 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -169,6 +169,12 @@ please see styles_device_defaults.xml.
<item name="windowExitAnimation">@anim/input_method_exit</item>
</style>
+ <!-- Window animations that are applied to voice activity windows. -->
+ <style name="Animation.VoiceActivity">
+ <item name="windowEnterAnimation">@anim/voice_activity_open_enter</item>
+ <item name="windowExitAnimation">@anim/voice_activity_close_exit</item>
+ </style>
+
<!-- Window animations that are applied to voice interaction overlay windows. -->
<style name="Animation.VoiceInteractionSession">
<item name="windowEnterAnimation">@anim/voice_layer_enter</item>
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index 6943533..108334f 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -404,8 +404,10 @@ please see styles_device_defaults.xml.
<item name="textAppearance">?attr/textAppearanceButton</item>
<item name="textColor">?attr/textColorPrimary</item>
<item name="minHeight">48dip</item>
- <item name="minWidth">96dip</item>
- <item name="stateListAnimator">@anim/button_state_list_anim_quantum</item>
+ <item name="minWidth">88dip</item>
+
+ <!-- TODO: Turn this back on when we support inset drawable outlines. -->
+ <!-- <item name="stateListAnimator">@anim/button_state_list_anim_quantum</item> -->
</style>
<!-- Small bordered ink button -->
@@ -434,7 +436,6 @@ please see styles_device_defaults.xml.
<item name="background">@drawable/btn_toggle_quantum</item>
<item name="textOn">@string/capital_on</item>
<item name="textOff">@string/capital_off</item>
- <item name="textAppearance">?attr/textAppearanceSmall</item>
<item name="minHeight">48dip</item>
</style>
@@ -468,34 +469,29 @@ please see styles_device_defaults.xml.
<item name="paddingEnd">8dp</item>
</style>
- <style name="Widget.Quantum.CheckedTextView" parent="Widget.CheckedTextView">
- <item name="drawablePadding">4dip</item>
- </style>
-
+ <style name="Widget.Quantum.CheckedTextView" parent="Widget.CheckedTextView" />
<style name="Widget.Quantum.TextSelectHandle" parent="Widget.TextSelectHandle"/>
<style name="Widget.Quantum.TextSuggestionsPopupWindow" parent="Widget.TextSuggestionsPopupWindow"/>
<style name="Widget.Quantum.AbsListView" parent="Widget.AbsListView"/>
<style name="Widget.Quantum.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
- <item name="dropDownSelector">@drawable/list_selector_quantum</item>
+ <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_quantum</item>
</style>
<style name="Widget.Quantum.CompoundButton" parent="Widget.CompoundButton"/>
<style name="Widget.Quantum.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">
- <item name="background">?attr/selectableItemBackground</item>
- <item name="drawablePadding">4dip</item>
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
</style>
<style name="Widget.Quantum.CompoundButton.RadioButton" parent="Widget.CompoundButton.RadioButton">
- <item name="background">?attr/selectableItemBackground</item>
- <item name="drawablePadding">4dip</item>
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
</style>
<style name="Widget.Quantum.CompoundButton.Star" parent="Widget.CompoundButton.Star">
<item name="button">@drawable/btn_star_quantum</item>
- <item name="background">?attr/selectableItemBackground</item>
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
</style>
<style name="Widget.Quantum.CompoundButton.Switch">
@@ -507,7 +503,7 @@ please see styles_device_defaults.xml.
<item name="textOff"></item>
<item name="switchMinWidth">4dip</item>
<item name="switchPadding">4dip</item>
- <item name="background">?attr/selectableItemBackground</item>
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
</style>
<style name="Widget.Quantum.EditText" parent="Widget.EditText"/>
@@ -522,7 +518,10 @@ please see styles_device_defaults.xml.
<style name="Widget.Quantum.ExpandableListView.White"/>
<style name="Widget.Quantum.Gallery" parent="Widget.Gallery"/>
<style name="Widget.Quantum.GestureOverlayView" parent="Widget.GestureOverlayView"/>
- <style name="Widget.Quantum.GridView" parent="Widget.GridView"/>
+
+ <style name="Widget.Quantum.GridView" parent="Widget.GridView">
+ <item name="android:listSelector">?attr/selectableItemBackground</item>
+ </style>
<style name="Widget.Quantum.CalendarView" parent="Widget.CalendarView">
<item name="selectedWeekBackgroundColor">#330099FF</item>
@@ -586,7 +585,7 @@ please see styles_device_defaults.xml.
<style name="Widget.Quantum.PopupWindow" parent="Widget.PopupWindow"/>
<style name="Widget.Quantum.PopupWindow.ActionMode">
- <item name="popupBackground">@color/black</item>
+ <item name="popupBackground">@drawable/popup_background_quantum</item>
<item name="popupAnimationStyle">@style/Animation.PopupWindow.ActionMode</item>
</style>
@@ -626,7 +625,7 @@ please see styles_device_defaults.xml.
<item name="paddingStart">16dip</item>
<item name="paddingEnd">16dip</item>
<item name="mirrorForRtl">true</item>
- <item name="background">?attr/selectableItemBackground</item>
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
</style>
<style name="Widget.Quantum.RatingBar" parent="Widget.RatingBar">
@@ -653,7 +652,7 @@ please see styles_device_defaults.xml.
<style name="Widget.Quantum.Spinner" parent="Widget.Spinner.DropDown">
<item name="background">@drawable/spinner_background_quantum</item>
- <item name="dropDownSelector">@drawable/list_selector_quantum</item>
+ <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_quantum</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
@@ -712,7 +711,7 @@ please see styles_device_defaults.xml.
<style name="Widget.Quantum.QuickContactBadgeSmall.WindowLarge" parent="Widget.QuickContactBadgeSmall.WindowLarge"/>
<style name="Widget.Quantum.ListPopupWindow" parent="Widget.ListPopupWindow">
- <item name="dropDownSelector">@drawable/list_selector_quantum</item>
+ <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_quantum</item>
<item name="popupAnimationStyle">@style/Animation.Quantum.Popup</item>
<item name="dropDownVerticalOffset">0dip</item>
@@ -809,7 +808,7 @@ please see styles_device_defaults.xml.
</style>
<style name="Widget.Quantum.MediaRouteButton">
- <item name="background">?attr/selectableItemBackground</item>
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
<item name="externalRouteEnabledDrawable">@drawable/ic_media_route_quantum</item>
<item name="minWidth">56dp</item>
<item name="minHeight">48dp</item>
@@ -879,12 +878,7 @@ please see styles_device_defaults.xml.
<style name="Widget.Quantum.Light.ListView" parent="Widget.Quantum.ListView"/>
<style name="Widget.Quantum.Light.ListView.White" parent="Widget.Quantum.ListView.White"/>
<style name="Widget.Quantum.Light.PopupWindow" parent="Widget.Quantum.PopupWindow"/>
-
- <style name="Widget.Quantum.Light.PopupWindow.ActionMode">
- <item name="popupBackground">@color/white</item>
- <item name="popupAnimationStyle">@style/Animation.PopupWindow.ActionMode</item>
- </style>
-
+ <style name="Widget.Quantum.Light.PopupWindow.ActionMode" parent="Widget.Quantum.PopupWindow.ActionMode"/>
<style name="Widget.Quantum.Light.ProgressBar" parent="Widget.Quantum.ProgressBar"/>
<style name="Widget.Quantum.Light.ProgressBar.Horizontal" parent="Widget.Quantum.ProgressBar.Horizontal"/>
<style name="Widget.Quantum.Light.ProgressBar.Small" parent="Widget.Quantum.ProgressBar.Small"/>
@@ -894,7 +888,6 @@ please see styles_device_defaults.xml.
<style name="Widget.Quantum.Light.ProgressBar.Small.Inverse" parent="Widget.Quantum.ProgressBar.Small.Inverse"/>
<style name="Widget.Quantum.Light.ProgressBar.Large.Inverse" parent="Widget.Quantum.ProgressBar.Large.Inverse"/>
<style name="Widget.Quantum.Light.SeekBar" parent="Widget.Quantum.SeekBar"/>
-
<style name="Widget.Quantum.Light.RatingBar" parent="Widget.Quantum.RatingBar" />
<style name="Widget.Quantum.Light.RatingBar.Indicator" parent="Widget.RatingBar.Indicator">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6e1629b..8b1ca31 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1388,6 +1388,7 @@
<java-symbol type="string" name="android_upgrading_title" />
<java-symbol type="string" name="bugreport_title" />
<java-symbol type="string" name="bugreport_message" />
+ <java-symbol type="string" name="bugreport_status" />
<java-symbol type="string" name="faceunlock_multiple_failures" />
<java-symbol type="string" name="global_action_power_off" />
<java-symbol type="string" name="global_actions_airplane_mode_off_status" />
@@ -1679,7 +1680,6 @@
<java-symbol type="anim" name="push_down_out" />
<java-symbol type="anim" name="push_up_in" />
<java-symbol type="anim" name="push_up_out" />
- <java-symbol type="anim" name="lock_screen_wallpaper_behind_enter" />
<java-symbol type="anim" name="lock_screen_behind_enter" />
<java-symbol type="bool" name="config_alwaysUseCdmaRssi" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 41f4ff8..648660b 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -124,6 +124,7 @@ please see themes_device_defaults.xml.
<item name="buttonStyleToggle">@android:style/Widget.Button.Toggle</item>
<item name="selectableItemBackground">@android:drawable/item_background</item>
+ <item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackground</item>
<item name="borderlessButtonStyle">?android:attr/buttonStyle</item>
<item name="homeAsUpIndicator">@android:drawable/ic_ab_back_holo_dark</item>
@@ -1032,6 +1033,7 @@ please see themes_device_defaults.xml.
<item name="mediaRouteButtonStyle">@android:style/Widget.Holo.MediaRouteButton</item>
<item name="selectableItemBackground">@android:drawable/item_background_holo_dark</item>
+ <item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackground</item>
<item name="borderlessButtonStyle">@android:style/Widget.Holo.Button.Borderless</item>
<item name="homeAsUpIndicator">@android:drawable/ic_ab_back_holo_dark</item>
@@ -1372,6 +1374,7 @@ please see themes_device_defaults.xml.
<item name="mediaRouteButtonStyle">@android:style/Widget.Holo.Light.MediaRouteButton</item>
<item name="selectableItemBackground">@android:drawable/item_background_holo_light</item>
+ <item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackground</item>
<item name="borderlessButtonStyle">@android:style/Widget.Holo.Light.Button.Borderless</item>
<item name="homeAsUpIndicator">@android:drawable/ic_ab_back_holo_light</item>
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index 484c694..cdbd771 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -101,6 +101,7 @@ please see themes_device_defaults.xml.
<item name="mediaRouteButtonStyle">@style/Widget.Quantum.MediaRouteButton</item>
<item name="selectableItemBackground">@drawable/item_background_quantum</item>
+ <item name="selectableItemBackgroundBorderless">@drawable/item_background_borderless_quantum</item>
<item name="borderlessButtonStyle">@style/Widget.Quantum.Button.Borderless</item>
<item name="homeAsUpIndicator">@drawable/ic_ab_back_quantum</item>
@@ -125,8 +126,7 @@ please see themes_device_defaults.xml.
<item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum_anim</item>
<item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
- <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
-
+ <item name="listChoiceBackgroundIndicator">?attr/selectableItemBackground</item>
<item name="activatedBackgroundIndicator">@drawable/activated_background_quantum</item>
<item name="listDividerAlertDialog">@drawable/list_divider_quantum</item>
@@ -308,7 +308,7 @@ please see themes_device_defaults.xml.
<item name="actionModePopupWindowStyle">@style/Widget.Quantum.PopupWindow.ActionMode</item>
<item name="actionBarWidgetTheme">@style/ThemeOverlay.Quantum.ActionBarWidget</item>
<item name="actionBarTheme">@null</item>
- <item name="actionBarItemBackground">@drawable/item_background_quantum</item>
+ <item name="actionBarItemBackground">?attr/selectableItemBackgroundBorderless</item>
<item name="actionModeCutDrawable">@drawable/ic_menu_cut_quantum</item>
<item name="actionModeCopyDrawable">@drawable/ic_menu_copy_quantum</item>
@@ -378,8 +378,8 @@ please see themes_device_defaults.xml.
<item name="colorControlNormal">?attr/textColorSecondary</item>
<item name="colorControlActivated">?attr/colorPrimary</item>
- <item name="colorControlHighlight">#30ffffff</item>
+ <item name="colorControlHighlight">@color/ripple_quantum_dark</item>
<item name="colorButtonNormal">@color/btn_default_quantum_dark</item>
</style>
@@ -446,6 +446,7 @@ please see themes_device_defaults.xml.
<item name="mediaRouteButtonStyle">@style/Widget.Quantum.Light.MediaRouteButton</item>
<item name="selectableItemBackground">@drawable/item_background_quantum</item>
+ <item name="selectableItemBackgroundBorderless">@drawable/item_background_borderless_quantum</item>
<item name="borderlessButtonStyle">@style/Widget.Quantum.Light.Button.Borderless</item>
<item name="homeAsUpIndicator">@drawable/ic_ab_back_quantum</item>
@@ -470,8 +471,7 @@ please see themes_device_defaults.xml.
<item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum_anim</item>
<item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
- <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
-
+ <item name="listChoiceBackgroundIndicator">?attr/selectableItemBackground</item>
<item name="activatedBackgroundIndicator">@drawable/activated_background_quantum</item>
<item name="expandableListPreferredItemPaddingLeft">40dip</item>
@@ -655,7 +655,7 @@ please see themes_device_defaults.xml.
<item name="actionModePopupWindowStyle">@style/Widget.Quantum.Light.PopupWindow.ActionMode</item>
<item name="actionBarWidgetTheme">@style/ThemeOverlay.Quantum.ActionBarWidget</item>
<item name="actionBarTheme">@null</item>
- <item name="actionBarItemBackground">@drawable/item_background_quantum</item>
+ <item name="actionBarItemBackground">?attr/selectableItemBackgroundBorderless</item>
<item name="actionModeCutDrawable">@drawable/ic_menu_cut_quantum</item>
<item name="actionModeCopyDrawable">@drawable/ic_menu_copy_quantum</item>
@@ -668,7 +668,7 @@ please see themes_device_defaults.xml.
<item name="dividerVertical">?attr/listDivider</item>
<item name="dividerHorizontal">?attr/listDivider</item>
<item name="buttonBarStyle">@style/Widget.Quantum.Light.ButtonBar</item>
- <item name="buttonBarButtonStyle">@style/Widget.Quantum.Light.Button.Borderless.Small</item>
+ <item name="buttonBarButtonStyle">@style/Widget.Quantum.Light.Button.Borderless</item>
<item name="segmentedButtonStyle">@style/Widget.Quantum.Light.SegmentedButton</item>
<!-- SearchView attributes -->
@@ -721,8 +721,8 @@ please see themes_device_defaults.xml.
<item name="colorControlNormal">?attr/textColorSecondary</item>
<item name="colorControlActivated">?attr/colorPrimary</item>
- <item name="colorControlHighlight">#30000000</item>
+ <item name="colorControlHighlight">@color/ripple_quantum_light</item>
<item name="colorButtonNormal">@color/btn_default_quantum_light</item>
</style>
@@ -770,7 +770,8 @@ please see themes_device_defaults.xml.
<item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_light</item>
<item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_light</item>
- <item name="colorButtonNormal">@color/quantum_grey_100</item>
+ <item name="colorControlHighlight">@color/ripple_quantum_light</item>
+ <item name="colorButtonNormal">@color/btn_default_quantum_light</item>
</style>
<!-- Theme overlay that replaces colors with their dark versions but preserves
@@ -805,7 +806,8 @@ please see themes_device_defaults.xml.
<item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_dark</item>
<item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_dark</item>
- <item name="colorButtonNormal">@color/quantum_grey_700</item>
+ <item name="colorControlHighlight">@color/ripple_quantum_dark</item>
+ <item name="colorButtonNormal">@color/btn_default_quantum_dark</item>
</style>
<!-- Theme overlay that replaces the activated control color (which by default
@@ -913,6 +915,22 @@ please see themes_device_defaults.xml.
<item name="windowNoTitle">true</item>
</style>
+ <!-- Quantum theme for an activity that is to be used for voice interaction.
+ This gives the activity a floating dialog style, to incorporate with the
+ system voice experience. -->
+ <style name="Theme.Quantum.Voice" parent="@style/Theme.Quantum.Dialog">
+ <item name="windowAnimationStyle">@style/Animation.VoiceActivity</item>
+ <item name="backgroundDimEnabled">false</item>
+ </style>
+
+ <!-- Quantum light theme for an activity that is to be used for voice interaction.
+ This gives the activity a floating dialog style, to incorporate with the
+ system voice experience. -->
+ <style name="Theme.Quantum.Light.Voice" parent="@style/Theme.Quantum.Light.Dialog">
+ <item name="windowAnimationStyle">@style/Animation.VoiceActivity</item>
+ <item name="backgroundDimEnabled">false</item>
+ </style>
+
<!-- Default theme for quantum style input methods, which is used by the
{@link android.inputmethodservice.InputMethodService} class.
this inherits from Theme.Panel, but sets up IME appropriate animations
@@ -929,7 +947,7 @@ please see themes_device_defaults.xml.
this inherits from Theme.Panel, but sets up appropriate animations
and a few custom attributes. -->
<style name="Theme.Quantum.VoiceInteractionSession" parent="Theme.Quantum.Light.Panel">
- <item name="android:windowAnimationStyle">@android:style/Animation.VoiceInteractionSession</item>
+ <item name="windowAnimationStyle">@style/Animation.VoiceInteractionSession</item>
</style>
<!-- Theme for the search input bar. -->
@@ -986,7 +1004,7 @@ please see themes_device_defaults.xml.
<item name="colorBackgroundCacheHint">@null</item>
<item name="buttonBarStyle">@style/Widget.Quantum.ButtonBar.AlertDialog</item>
- <item name="borderlessButtonStyle">@style/Widget.Quantum.Button.Borderless.Small</item>
+ <item name="borderlessButtonStyle">@style/Widget.Quantum.Button.Borderless</item>
<item name="textAppearance">@style/TextAppearance.Quantum</item>
<item name="textAppearanceInverse">@style/TextAppearance.Quantum.Inverse</item>
@@ -1105,7 +1123,7 @@ please see themes_device_defaults.xml.
<item name="colorBackgroundCacheHint">@null</item>
<item name="buttonBarStyle">@style/Widget.Quantum.Light.ButtonBar.AlertDialog</item>
- <item name="borderlessButtonStyle">@style/Widget.Quantum.Light.Button.Borderless.Small</item>
+ <item name="borderlessButtonStyle">@style/Widget.Quantum.Light.Button.Borderless</item>
<item name="textAppearance">@style/TextAppearance.Quantum</item>
<item name="textAppearanceInverse">@style/TextAppearance.Quantum.Inverse</item>
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScanFilterTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
index ec35d85..bf34f1d 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScanFilterTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package android.bluetooth;
+package android.bluetooth.le;
-import android.bluetooth.BluetoothLeScanner.ScanResult;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.test.suitebuilder.annotation.SmallTest;
@@ -26,22 +27,21 @@ import junit.framework.TestCase;
/**
* Unit test cases for Bluetooth LE scan filters.
* <p>
- * To run this test, use adb shell am instrument -e class
- * 'android.bluetooth.BluetoothLeScanFilterTest' -w
+ * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanFilterTest' -w
* 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
*/
-public class BluetoothLeScanFilterTest extends TestCase {
+public class ScanFilterTest extends TestCase {
private static final String DEVICE_MAC = "01:02:03:04:05:AB";
private ScanResult mScanResult;
- private BluetoothLeScanFilter.Builder mFilterBuilder;
+ private ScanFilter.Builder mFilterBuilder;
@Override
protected void setUp() throws Exception {
byte[] scanRecord = new byte[] {
0x02, 0x01, 0x1a, // advertising flags
0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids
- 0x04, 0x09, 0x50, 0x65, 0x64, // name
+ 0x04, 0x09, 0x50, 0x65, 0x64, // setName
0x02, 0x0A, (byte) 0xec, // tx power level
0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data
0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
@@ -51,134 +51,135 @@ public class BluetoothLeScanFilterTest extends TestCase {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice device = adapter.getRemoteDevice(DEVICE_MAC);
mScanResult = new ScanResult(device, scanRecord, -10, 1397545200000000L);
- mFilterBuilder = BluetoothLeScanFilter.newBuilder();
+ mFilterBuilder = new ScanFilter.Builder();
}
@SmallTest
- public void testNameFilter() {
- BluetoothLeScanFilter filter = mFilterBuilder.name("Ped").build();
- assertTrue("name filter fails", filter.matches(mScanResult));
+ public void testsetNameFilter() {
+ ScanFilter filter = mFilterBuilder.setName("Ped").build();
+ assertTrue("setName filter fails", filter.matches(mScanResult));
- filter = mFilterBuilder.name("Pem").build();
- assertFalse("name filter fails", filter.matches(mScanResult));
+ filter = mFilterBuilder.setName("Pem").build();
+ assertFalse("setName filter fails", filter.matches(mScanResult));
}
@SmallTest
public void testDeviceFilter() {
- BluetoothLeScanFilter filter = mFilterBuilder.macAddress(DEVICE_MAC).build();
+ ScanFilter filter = mFilterBuilder.setMacAddress(DEVICE_MAC).build();
assertTrue("device filter fails", filter.matches(mScanResult));
- filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build();
+ filter = mFilterBuilder.setMacAddress("11:22:33:44:55:66").build();
assertFalse("device filter fails", filter.matches(mScanResult));
}
@SmallTest
- public void testServiceUuidFilter() {
- BluetoothLeScanFilter filter = mFilterBuilder.serviceUuid(
+ public void testsetServiceUuidFilter() {
+ ScanFilter filter = mFilterBuilder.setServiceUuid(
ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB")).build();
assertTrue("uuid filter fails", filter.matches(mScanResult));
- filter = mFilterBuilder.serviceUuid(
+ filter = mFilterBuilder.setServiceUuid(
ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build();
assertFalse("uuid filter fails", filter.matches(mScanResult));
filter = mFilterBuilder
- .serviceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"))
- .serviceUuidMask(ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF"))
+ .setServiceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"),
+ ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF"))
.build();
assertTrue("uuid filter fails", filter.matches(mScanResult));
}
@SmallTest
- public void testServiceDataFilter() {
- byte[] serviceData = new byte[] {
+ public void testsetServiceDataFilter() {
+ byte[] setServiceData = new byte[] {
0x0b, 0x11, 0x50, 0x64 };
- BluetoothLeScanFilter filter = mFilterBuilder.serviceData(serviceData).build();
+ ScanFilter filter = mFilterBuilder.setServiceData(setServiceData).build();
assertTrue("service data filter fails", filter.matches(mScanResult));
byte[] nonMatchData = new byte[] {
0x0b, 0x01, 0x50, 0x64 };
- filter = mFilterBuilder.serviceData(nonMatchData).build();
+ filter = mFilterBuilder.setServiceData(nonMatchData).build();
assertFalse("service data filter fails", filter.matches(mScanResult));
byte[] mask = new byte[] {
(byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF };
- filter = mFilterBuilder.serviceData(nonMatchData).serviceDataMask(mask).build();
+ filter = mFilterBuilder.setServiceData(nonMatchData, mask).build();
assertTrue("partial service data filter fails", filter.matches(mScanResult));
}
@SmallTest
public void testManufacturerSpecificData() {
- byte[] manufacturerData = new byte[] {
+ byte[] setManufacturerData = new byte[] {
(byte) 0xE0, 0x00, 0x02, 0x15 };
int manufacturerId = 224;
- BluetoothLeScanFilter filter =
- mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build();
- assertTrue("manufacturerData filter fails", filter.matches(mScanResult));
+ ScanFilter filter =
+ mFilterBuilder.setManufacturerData(manufacturerId, setManufacturerData).build();
+ assertTrue("setManufacturerData filter fails", filter.matches(mScanResult));
byte[] nonMatchData = new byte[] {
(byte) 0xF0, 0x00, 0x02, 0x15 };
- filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData).build();
- assertFalse("manufacturerData filter fails", filter.matches(mScanResult));
+ filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData).build();
+ assertFalse("setManufacturerData filter fails", filter.matches(mScanResult));
byte[] mask = new byte[] {
(byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
};
- filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData)
- .manufacturerDataMask(mask).build();
- assertTrue("partial manufacturerData filter fails", filter.matches(mScanResult));
+ filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData, mask).build();
+ assertTrue("partial setManufacturerData filter fails", filter.matches(mScanResult));
}
@SmallTest
public void testReadWriteParcel() {
- BluetoothLeScanFilter filter = mFilterBuilder.build();
+ ScanFilter filter = mFilterBuilder.build();
testReadWriteParcelForFilter(filter);
- filter = mFilterBuilder.name("Ped").build();
+ filter = mFilterBuilder.setName("Ped").build();
testReadWriteParcelForFilter(filter);
- filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build();
+ filter = mFilterBuilder.setMacAddress("11:22:33:44:55:66").build();
testReadWriteParcelForFilter(filter);
- filter = mFilterBuilder.serviceUuid(
+ filter = mFilterBuilder.setServiceUuid(
ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build();
testReadWriteParcelForFilter(filter);
- filter = mFilterBuilder.serviceUuidMask(
+ filter = mFilterBuilder.setServiceUuid(
+ ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"),
ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build();
testReadWriteParcelForFilter(filter);
- byte[] serviceData = new byte[] {
+ byte[] setServiceData = new byte[] {
0x0b, 0x11, 0x50, 0x64 };
- filter = mFilterBuilder.serviceData(serviceData).build();
+ filter = mFilterBuilder.setServiceData(setServiceData).build();
testReadWriteParcelForFilter(filter);
byte[] serviceDataMask = new byte[] {
(byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF };
- filter = mFilterBuilder.serviceDataMask(serviceDataMask).build();
+ filter = mFilterBuilder.setServiceData(setServiceData, serviceDataMask).build();
testReadWriteParcelForFilter(filter);
byte[] manufacturerData = new byte[] {
(byte) 0xE0, 0x00, 0x02, 0x15 };
int manufacturerId = 224;
- filter = mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build();
+ filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData).build();
testReadWriteParcelForFilter(filter);
byte[] manufacturerDataMask = new byte[] {
(byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
};
- filter = mFilterBuilder.manufacturerDataMask(manufacturerDataMask).build();
+ filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData,
+ manufacturerDataMask).build();
testReadWriteParcelForFilter(filter);
}
- private void testReadWriteParcelForFilter(BluetoothLeScanFilter filter) {
+ private void testReadWriteParcelForFilter(ScanFilter filter) {
Parcel parcel = Parcel.obtain();
filter.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
- BluetoothLeScanFilter filterFromParcel =
- BluetoothLeScanFilter.CREATOR.createFromParcel(parcel);
+ ScanFilter filterFromParcel =
+ ScanFilter.CREATOR.createFromParcel(parcel);
System.out.println(filterFromParcel);
assertEquals(filter, filterFromParcel);
}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
index eb6c419..cece96b 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package android.bluetooth;
+package android.bluetooth.le;
+import android.bluetooth.le.ScanRecord;
import android.os.ParcelUuid;
import android.test.suitebuilder.annotation.SmallTest;
@@ -24,13 +25,13 @@ import junit.framework.TestCase;
import java.util.Arrays;
/**
- * Unit test cases for {@link BluetoothLeAdvertiseScanData}.
+ * Unit test cases for {@link ScanRecord}.
* <p>
* To run this test, use adb shell am instrument -e class
- * 'android.bluetooth.BluetoothLeAdvertiseScanDataTest' -w
+ * 'android.bluetooth.ScanRecordTest' -w
* 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
*/
-public class BluetoothLeAdvertiseScanDataTest extends TestCase {
+public class ScanRecordTest extends TestCase {
@SmallTest
public void testParser() {
@@ -43,8 +44,7 @@ public class BluetoothLeAdvertiseScanDataTest extends TestCase {
0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble
};
- BluetoothLeAdvertiseScanData.ScanRecord data = BluetoothLeAdvertiseScanData.ScanRecord
- .getParser().parseFromScanRecord(scanRecord);
+ ScanRecord data = ScanRecord.parseFromBytes(scanRecord);
assertEquals(0x1a, data.getAdvertiseFlags());
ParcelUuid uuid1 = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScannerTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java
index 8064ba8..241e88f 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScannerTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package android.bluetooth;
+package android.bluetooth.le;
-import android.bluetooth.BluetoothLeScanner.ScanResult;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
@@ -25,17 +26,18 @@ import junit.framework.TestCase;
/**
* Unit test cases for Bluetooth LE scans.
* <p>
- * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothLeScannerTest'
- * -w 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
+ * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanResultTest' -w
+ * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
*/
-public class BluetoothLeScannerTest extends TestCase {
+public class ScanResultTest extends TestCase {
/**
* Test read and write parcel of ScanResult
*/
@SmallTest
public void testScanResultParceling() {
- BluetoothDevice device = new BluetoothDevice("01:02:03:04:05:06");
+ BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
+ "01:02:03:04:05:06");
byte[] scanRecord = new byte[] {
1, 2, 3 };
int rssi = -10;
diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
index 5dc9ef8..433d4d2 100644
--- a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -16,6 +16,9 @@
package com.android.internal.util;
+import android.test.MoreAsserts;
+
+import java.util.Arrays;
import junit.framework.TestCase;
/**
@@ -77,4 +80,79 @@ public class ArrayUtilsTest extends TestCase {
assertFalse(ArrayUtils.containsAll(new Object[] { }, new Object[] { null }));
assertFalse(ArrayUtils.containsAll(new Object[] { A }, new Object[] { null }));
}
+
+ public void testContainsInt() throws Exception {
+ assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 1));
+ assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 2));
+ assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 3));
+
+ assertFalse(ArrayUtils.contains(new int[] { 1, 2, 3 }, 0));
+ assertFalse(ArrayUtils.contains(new int[] { 1, 2, 3 }, 4));
+ assertFalse(ArrayUtils.contains(new int[] { }, 2));
+ }
+
+ public void testAppendInt() throws Exception {
+ MoreAsserts.assertEquals(new int[] { 1 },
+ ArrayUtils.appendInt(null, 1));
+ MoreAsserts.assertEquals(new int[] { 1 },
+ ArrayUtils.appendInt(new int[] { }, 1));
+ MoreAsserts.assertEquals(new int[] { 1, 2 },
+ ArrayUtils.appendInt(new int[] { 1 }, 2));
+ MoreAsserts.assertEquals(new int[] { 1, 2 },
+ ArrayUtils.appendInt(new int[] { 1, 2 }, 1));
+ }
+
+ public void testRemoveInt() throws Exception {
+ assertNull(ArrayUtils.removeInt(null, 1));
+ MoreAsserts.assertEquals(new int[] { },
+ ArrayUtils.removeInt(new int[] { }, 1));
+ MoreAsserts.assertEquals(new int[] { 1, 2, 3, },
+ ArrayUtils.removeInt(new int[] { 1, 2, 3}, 4));
+ MoreAsserts.assertEquals(new int[] { 2, 3, },
+ ArrayUtils.removeInt(new int[] { 1, 2, 3}, 1));
+ MoreAsserts.assertEquals(new int[] { 1, 3, },
+ ArrayUtils.removeInt(new int[] { 1, 2, 3}, 2));
+ MoreAsserts.assertEquals(new int[] { 1, 2, },
+ ArrayUtils.removeInt(new int[] { 1, 2, 3}, 3));
+ MoreAsserts.assertEquals(new int[] { 2, 3, 1 },
+ ArrayUtils.removeInt(new int[] { 1, 2, 3, 1 }, 1));
+ }
+
+ public void testContainsLong() throws Exception {
+ assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 1));
+ assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 2));
+ assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 3));
+
+ assertFalse(ArrayUtils.contains(new long[] { 1, 2, 3 }, 0));
+ assertFalse(ArrayUtils.contains(new long[] { 1, 2, 3 }, 4));
+ assertFalse(ArrayUtils.contains(new long[] { }, 2));
+ }
+
+ public void testAppendLong() throws Exception {
+ MoreAsserts.assertEquals(new long[] { 1 },
+ ArrayUtils.appendLong(null, 1));
+ MoreAsserts.assertEquals(new long[] { 1 },
+ ArrayUtils.appendLong(new long[] { }, 1));
+ MoreAsserts.assertEquals(new long[] { 1, 2 },
+ ArrayUtils.appendLong(new long[] { 1 }, 2));
+ MoreAsserts.assertEquals(new long[] { 1, 2 },
+ ArrayUtils.appendLong(new long[] { 1, 2 }, 1));
+ }
+
+ public void testRemoveLong() throws Exception {
+ assertNull(ArrayUtils.removeLong(null, 1));
+ MoreAsserts.assertEquals(new long[] { },
+ ArrayUtils.removeLong(new long[] { }, 1));
+ MoreAsserts.assertEquals(new long[] { 1, 2, 3, },
+ ArrayUtils.removeLong(new long[] { 1, 2, 3}, 4));
+ MoreAsserts.assertEquals(new long[] { 2, 3, },
+ ArrayUtils.removeLong(new long[] { 1, 2, 3}, 1));
+ MoreAsserts.assertEquals(new long[] { 1, 3, },
+ ArrayUtils.removeLong(new long[] { 1, 2, 3}, 2));
+ MoreAsserts.assertEquals(new long[] { 1, 2, },
+ ArrayUtils.removeLong(new long[] { 1, 2, 3}, 3));
+ MoreAsserts.assertEquals(new long[] { 2, 3, 1 },
+ ArrayUtils.removeLong(new long[] { 1, 2, 3, 1 }, 1));
+ }
+
}
diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
index c0c20e2..ca68e93 100644
--- a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
@@ -172,7 +172,8 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe
final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
- final ControllerImpl controller = new ControllerImpl(enabledItems);
+ final ControllerImpl controller = ControllerImpl.createFrom(
+ null /* currentInstance */, enabledItems);
// switching-aware loop
assertRotationOrder(controller, false /* onlyCurrentIme */,
@@ -214,9 +215,8 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe
disabledSubtypeUnawareIme, null);
}
- // This test is disabled until DynamicRotationList is enabled.
@SmallTest
- public void DISABLED_testControllerImplWithUserAction() throws Exception {
+ public void testControllerImplWithUserAction() throws Exception {
final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
@@ -226,7 +226,8 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe
final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
- final ControllerImpl controller = new ControllerImpl(enabledItems);
+ final ControllerImpl controller = ControllerImpl.createFrom(
+ null /* currentInstance */, enabledItems);
// === switching-aware loop ===
assertRotationOrder(controller, false /* onlyCurrentIme */,
@@ -272,5 +273,26 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe
subtypeUnawareIme, null);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
switchUnawareJapaneseIme_ja_JP, null);
+
+ // Rotation order should be preserved when created with the same subtype list.
+ final List<ImeSubtypeListItem> sameEnabledItems = createEnabledImeSubtypes();
+ final ControllerImpl newController = ControllerImpl.createFrom(controller,
+ sameEnabledItems);
+ assertRotationOrder(newController, false /* onlyCurrentIme */,
+ japaneseIme_ja_JP, latinIme_fr, latinIme_en_US);
+ assertRotationOrder(newController, false /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_JP);
+
+ // Rotation order should be initialized when created with a different subtype list.
+ final List<ImeSubtypeListItem> differentEnabledItems = Arrays.asList(
+ latinIme_en_US, latinIme_fr, switchingUnawarelatinIme_en_UK,
+ switchUnawareJapaneseIme_ja_JP);
+ final ControllerImpl anotherController = ControllerImpl.createFrom(controller,
+ differentEnabledItems);
+ assertRotationOrder(anotherController, false /* onlyCurrentIme */,
+ latinIme_en_US, latinIme_fr);
+ assertRotationOrder(anotherController, false /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchUnawareJapaneseIme_ja_JP);
}
}
diff --git a/data/fonts/Roboto-Black.ttf b/data/fonts/Roboto-Black.ttf
index 2cdbe43..cb905bc 100644
--- a/data/fonts/Roboto-Black.ttf
+++ b/data/fonts/Roboto-Black.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Bold.ttf b/data/fonts/Roboto-Bold.ttf
index 15c9b4e..68822ca 100644
--- a/data/fonts/Roboto-Bold.ttf
+++ b/data/fonts/Roboto-Bold.ttf
Binary files differ
diff --git a/data/fonts/Roboto-BoldItalic.ttf b/data/fonts/Roboto-BoldItalic.ttf
index a0abf30..aebf8eb 100644
--- a/data/fonts/Roboto-BoldItalic.ttf
+++ b/data/fonts/Roboto-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Italic.ttf b/data/fonts/Roboto-Italic.ttf
index 67b5394..2041cbc 100644
--- a/data/fonts/Roboto-Italic.ttf
+++ b/data/fonts/Roboto-Italic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Light.ttf b/data/fonts/Roboto-Light.ttf
index d9fb64a..aa45340 100644
--- a/data/fonts/Roboto-Light.ttf
+++ b/data/fonts/Roboto-Light.ttf
Binary files differ
diff --git a/data/fonts/Roboto-LightItalic.ttf b/data/fonts/Roboto-LightItalic.ttf
index 1fd1d31..a85444f 100644
--- a/data/fonts/Roboto-LightItalic.ttf
+++ b/data/fonts/Roboto-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Medium.ttf b/data/fonts/Roboto-Medium.ttf
index c63c115..a3c1a1f 100644
--- a/data/fonts/Roboto-Medium.ttf
+++ b/data/fonts/Roboto-Medium.ttf
Binary files differ
diff --git a/data/fonts/Roboto-MediumItalic.ttf b/data/fonts/Roboto-MediumItalic.ttf
index cd7c835..a30aa0c 100644
--- a/data/fonts/Roboto-MediumItalic.ttf
+++ b/data/fonts/Roboto-MediumItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Regular.ttf b/data/fonts/Roboto-Regular.ttf
index 9cb4a5a..0e58508 100644
--- a/data/fonts/Roboto-Regular.ttf
+++ b/data/fonts/Roboto-Regular.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Thin.ttf b/data/fonts/Roboto-Thin.ttf
index f02f100..8779333 100644
--- a/data/fonts/Roboto-Thin.ttf
+++ b/data/fonts/Roboto-Thin.ttf
Binary files differ
diff --git a/data/fonts/Roboto-ThinItalic.ttf b/data/fonts/Roboto-ThinItalic.ttf
index 12a2ce0..b79cb26 100644
--- a/data/fonts/Roboto-ThinItalic.ttf
+++ b/data/fonts/Roboto-ThinItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Bold.ttf b/data/fonts/RobotoCondensed-Bold.ttf
index 1079af6..3e06c7c 100644
--- a/data/fonts/RobotoCondensed-Bold.ttf
+++ b/data/fonts/RobotoCondensed-Bold.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-BoldItalic.ttf b/data/fonts/RobotoCondensed-BoldItalic.ttf
index e7f13c2..aaf9fe0 100644
--- a/data/fonts/RobotoCondensed-BoldItalic.ttf
+++ b/data/fonts/RobotoCondensed-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Italic.ttf b/data/fonts/RobotoCondensed-Italic.ttf
index 7fa0448..d2b611f 100644
--- a/data/fonts/RobotoCondensed-Italic.ttf
+++ b/data/fonts/RobotoCondensed-Italic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Light.ttf b/data/fonts/RobotoCondensed-Light.ttf
index 96b75dd..d4eb198 100644
--- a/data/fonts/RobotoCondensed-Light.ttf
+++ b/data/fonts/RobotoCondensed-Light.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-LightItalic.ttf b/data/fonts/RobotoCondensed-LightItalic.ttf
index 7a2c164..a08f3f4 100644
--- a/data/fonts/RobotoCondensed-LightItalic.ttf
+++ b/data/fonts/RobotoCondensed-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Regular.ttf b/data/fonts/RobotoCondensed-Regular.ttf
index 734cc40..b9fc49c 100644
--- a/data/fonts/RobotoCondensed-Regular.ttf
+++ b/data/fonts/RobotoCondensed-Regular.ttf
Binary files differ
diff --git a/data/keyboards/Vendor_2378_Product_1008.kl b/data/keyboards/Vendor_2378_Product_1008.kl
new file mode 100644
index 0000000..478da03
--- /dev/null
+++ b/data/keyboards/Vendor_2378_Product_1008.kl
@@ -0,0 +1,35 @@
+# 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.
+
+# OnLive, Inc. OnLive Wireless Controller, USB adapter
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 315 BUTTON_START
+key 314 BUTTON_SELECT
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x03 Z
+axis 0x04 RZ
+axis 0x05 RTRIGGER
+axis 0x02 LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/docs/html/google/play/billing/billing_admin.jd b/docs/html/google/play/billing/billing_admin.jd
index 46ad905..5904b03 100644
--- a/docs/html/google/play/billing/billing_admin.jd
+++ b/docs/html/google/play/billing/billing_admin.jd
@@ -66,7 +66,8 @@ storing and delivering the digital content that you sell in your applications.</
</p>
</div>
-<p>You can create a product list for any published application or any draft application that's been
+<p>You can create a product list for any published application, or any
+application in the alpha or beta channels, that's been
uploaded and saved to the Developer Console. However, you must have a Google Wallet merchant
account and the application's manifest must include the <code>com.android.vending.BILLING</code>
permission. If an application's manifest does not include this permission, you will be able to edit
@@ -75,6 +76,13 @@ information about this permission, see
<a href="{@docRoot}google/play/billing/billing_integrate.html#billing-permission">Updating Your
Application's Manifest</a>.</p>
+<p class="note"><strong>Note:</strong> Previously you could test an app by
+uploading an unpublished "draft" version. This functionality is no longer
+supported; instead, you must publish it to the alpha or beta distribution
+channel. For more information, see <a
+href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps
+are No Longer Supported</a>.
+
<p>In addition, an application package can have only one product list. If you create a product
list for an application, and you use the <a
href="{@docRoot}google/play/publishing/multiple-apks.html">multiple APK feature</a> to distribute
diff --git a/docs/html/google/play/billing/billing_testing.jd b/docs/html/google/play/billing/billing_testing.jd
index df6c657..8a49433 100644
--- a/docs/html/google/play/billing/billing_testing.jd
+++ b/docs/html/google/play/billing/billing_testing.jd
@@ -8,8 +8,9 @@ parent.link=index.html
<h2>In this document</h2>
<ol>
<li><a href="#testing-purchases">Testing In-app Purchases</a></li>
- <li><a href="#billing-testing-static">Testing with static responses</a></li>
+ <li><a href="#billing-testing-static">Testing with Static Responses</a></li>
<li><a href="#billing-testing-real">Setting Up for Test Purchases</a></li>
+ <li><a href="#draft_apps">Draft Apps are No Longer Supported</a></li>
</ol>
<h2>See also</h2>
<ol>
@@ -79,8 +80,7 @@ method).</p>
<p>First, upload and publish in-app products that you want testers to be able to
purchase. You can upload and publish in-app products in the Developer Console.
Note that you can upload and publish your in-app items before you publish the
-APK itself. For example, you can publish your in-app items while your APK is
-still a draft. </p>
+APK itself.</p>
<p>Next, create license test accounts for authorized users. In the Developer
Console, go to <strong>Settings</strong> &gt; <strong>Account details</strong>,
@@ -149,11 +149,12 @@ license accounts in your alpha and beta distribution groups, those users will
only be able to make test purchases. </p>
-<h2 id="billing-testing-static">Testing with static responses</h2>
+<h2 id="billing-testing-static">Testing with Static Responses</h2>
<p>We recommend that you first test your In-app Billing implementation using static responses from
Google Play. This enables you to verify that your application is handling the primary Google
-Play responses correctly and that your application is able to verify signatures correctly.</p>
+Play responses correctly and that your application is able to verify signatures correctly. You can do this
+even if the app hasn't been published yet.</p>
<p>To test your implementation with static responses, you make an In-app Billing request using a
special item that has a reserved product ID. Each reserved product ID returns a specific static
@@ -173,6 +174,12 @@ the Developer Console to perform static response tests with the reserved product
install your application on a device, log into the device, and make billing requests using the
reserved product IDs.</p>
+<p class="note"><strong>Note:</strong> Previously you could test an app by
+uploading an unpublished "draft" version. This functionality is no longer
+supported. However, you can test your app with static responses even before you
+upload it to the Google Play store. For more information, see <a
+href="#draft_apps">Draft Apps are No Longer Supported</a>.
+
<p>There are four reserved product IDs for testing static In-app Billing responses:</p>
<ul>
@@ -205,67 +212,12 @@ Pricing</a>.</p>
</li>
</ul>
-<p>In some cases, the reserved items may return signed static responses, which lets you test
-signature verification in your application. To test signature verification with the special reserved
-product IDs, you may need to set up <a
-href="{@docRoot}google/play/billing/billing_admin.html#billing-testing-setup">test accounts</a> or
-upload your application as a unpublished draft application. Table 1 shows you the conditions under
-which static responses are signed.</p>
-
-<p class="table-caption" id="static-responses-table"><strong>Table 1.</strong>
-Conditions under which static responses are signed.</p>
-
-<table>
-<tr>
-<th>Application ever been published?</th>
-<th>Draft application uploaded and unpublished?</th>
-<th>User who is running the application</th>
-<th>Static response signature</th>
-</tr>
-
-<tr>
-<td>No</td>
-<td>No</td>
-<td>Any</td>
-<td>Unsigned</td>
-</tr>
-
-<tr>
-<td>No</td>
-<td>No</td>
-<td>Developer</td>
-<td>Signed</td>
-</tr>
-
-<tr>
-<td>Yes</td>
-<td>No</td>
-<td>Any</td>
-<td>Unsigned</td>
-</tr>
-
-<tr>
-<td>Yes</td>
-<td>No</td>
-<td>Developer</td>
-<td>Signed</td>
-</tr>
-
-<tr>
-<td>Yes</td>
-<td>No</td>
-<td>Test account</td>
-<td>Signed</td>
-</tr>
-
-<tr>
-<td>Yes</td>
-<td>Yes</td>
-<td>Any</td>
-<td>Signed</td>
-</tr>
-
-</table>
+<p>In some cases, the reserved items may return signed static responses, which
+lets you test signature verification in your application. The reserved items
+only return signed responses if the user running the application has a <a
+href="{@docRoot}distribute/googleplay/start.html">developer</a> or <a
+href="{@docRoot}google/play/billing/billing_admin.html#billing-testing-setup">test
+account.</a>
<p>To make an In-app Billing request with a reserved product ID, you simply construct a normal
<code>REQUEST_PURCHASE</code> request, but instead of using a real product ID from your
@@ -310,9 +262,11 @@ purchases. Testing real in-app purchases enables you to test the end-to-end In-a
experience, including the actual purchases from Google Play and the actual checkout flow that
users will experience in your application.</p>
-<p class="note"><strong>Note</strong>: You do not need to publish your application to do end-to-end
-testing. You only need to upload your application as a draft application to perform end-to-end
-testing.</p>
+<p class="note"><strong>Note:</strong> You can do end-to-end testing of your app
+ by publishing it to an <a
+ href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">alpha
+ distribution channel</a>. This allows you to publish the app to the Google
+ Play store, but limit its availability to just the testers you designate. </p>
<p>To test your In-app Billing implementation with actual in-app purchases, you will need to
register at least one test account on the Google Play Developer Console. You cannot use your
@@ -327,14 +281,16 @@ application does not need to be published, but the item does need to be publishe
<p>To test your In-app Billing implementation with actual purchases, follow these steps:</p>
<ol>
- <li><strong>Upload your application as a draft application to the Developer Console.</strong>
- <p>You do not need to publish your application to perform end-to-end testing with real product
- IDs; you only need to upload your application as a draft application. However, you must sign
- your application with your release key before you upload it as a draft application. Also, the
- version number of the uploaded application must match the version number of the application you
- load to your device for testing. To learn how to upload an application to Google Play, see
- <a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=113469">Uploading
- applications</a>.</p>
+ <li><strong>Upload your application to the <a
+ href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">alpha
+ distribution channel</a> with the Developer Console.</strong>
+
+ <p class="note"><strong>Note:</strong> Previously you could test an app by
+ uploading an unpublished "draft" version. This functionality is no longer
+ supported; instead, you must publish it to the alpha or beta distribution
+ channel. For more information, see <a href="#draft_apps">Draft Apps are No
+ Longer Supported</a>.
+
</li>
<li><strong>Add items to the application's product list.</strong>
<p>Make sure that you publish the items (the application can remain unpublished). See <a
@@ -370,3 +326,24 @@ href="{@docRoot}tools/publishing/app-signing.html">signing</a>, and <a
href="{@docRoot}distribute/tools/launch-checklist.html">publishing on Google Play</a>.
</p>
+<h2 id="draft_apps">Draft Apps are No Longer Supported</h2>
+
+<p>Previously, you could publish a "draft" version of your app for testing. This
+functionality is no longer supported. Instead, there are two ways you can test
+how a pre-release app functions on the Google Play store:</p>
+
+<ul>
+
+ <li>You can publish an app to the <a
+ href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">alpha
+ or beta distribution channels</a>. This makes the app available on the Google
+ Play store, but only to the testers you put on a "whitelist".</li>
+
+ <li>In a few cases, you can test Google Play functionality with an unpublished
+ app. For example, you can test an unpublished app's in-app billing support by
+ using <a
+ href="{@docRoot}google/play/billing/billing_testing.html#billing-testing-static">static
+ responses</a>, special reserved product IDs that always return a specific
+ result (like "purchased" or "refunded").</li>
+
+</ul>
diff --git a/docs/html/google/play/billing/v2/billing_integrate.jd b/docs/html/google/play/billing/v2/billing_integrate.jd
index ca41e0b..5eb17d5 100644
--- a/docs/html/google/play/billing/v2/billing_integrate.jd
+++ b/docs/html/google/play/billing/v2/billing_integrate.jd
@@ -208,6 +208,14 @@ following:</p>
a draft to the Google Play Developer Console. You also need to create a product list for the in-app
items that are available for purchase in the sample application. The following instructions show you
how to do this.</p>
+
+<p class="caution"><strong>Caution:</strong> Draft applications are no longer
+supported. To test an application, publish it in the <a
+href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">alpha
+or beta channels</a>. For more information, see <a
+href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps
+are No Longer Supported</a>.</p>
+
<ol>
<li><strong>Upload the release version of the sample application to Google Play.</strong>
<p>Do not publish the sample application; leave it as an unpublished draft application. The
@@ -928,10 +936,12 @@ public class BillingReceiver extends BroadcastReceiver {
// Intent actions that we receive in the BillingReceiver from Google Play.
// These are defined by Google Play and cannot be changed.
// The sample application defines these in the Consts.java file.
- public static final String ACTION_NOTIFY = "com.android.vending.billing.IN_APP_NOTIFY";
- public static final String ACTION_RESPONSE_CODE = "com.android.vending.billing.RESPONSE_CODE";
+ public static final String ACTION_NOTIFY =
+ "com.android.vending.billing.IN_APP_NOTIFY";
+ public static final String ACTION_RESPONSE_CODE =
+ "com.android.vending.billing.RESPONSE_CODE";
public static final String ACTION_PURCHASE_STATE_CHANGED =
- "com.android.vending.billing.PURCHASE_STATE_CHANGED";
+ "com.android.vending.billing.PURCHASE_STATE_CHANGED";
// The intent extras that are passed in an intent from Google Play.
// These are defined by Google Play and cannot be changed.
@@ -962,7 +972,8 @@ public class BillingReceiver extends BroadcastReceiver {
Log.w(TAG, "unexpected action: " + action);
}
}
- // Perform other processing here, such as forwarding intent messages to your local service.
+ // Perform other processing here, such as forwarding intent messages
+ // to your local service.
}
</pre>
diff --git a/docs/html/google/play/expansion-files.jd b/docs/html/google/play/expansion-files.jd
index e90f8fa..601ea48 100644
--- a/docs/html/google/play/expansion-files.jd
+++ b/docs/html/google/play/expansion-files.jd
@@ -527,17 +527,21 @@ are:</p>
&lt;!-- Required to download files from Google Play -->
&lt;uses-permission android:name="android.permission.INTERNET" />
- &lt;!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) -->
+ &lt;!-- Required to keep CPU alive while downloading files
+ (NOT to keep screen awake) -->
&lt;uses-permission android:name="android.permission.WAKE_LOCK" />
- &lt;!-- Required to poll the state of the network connection and respond to changes -->
- &lt;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ &lt;!-- Required to poll the state of the network connection
+ and respond to changes -->
+ &lt;uses-permission
+ android:name="android.permission.ACCESS_NETWORK_STATE" />
&lt;!-- Required to check whether Wi-Fi is enabled -->
&lt;uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
&lt;!-- Required to read and write the expansion files on shared storage -->
- &lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ &lt;uses-permission
+ android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
&lt;/manifest>
</pre>
@@ -650,8 +654,8 @@ public class SampleAlarmReceiver extends BroadcastReceiver {
&#64;Override
public void onReceive(Context context, Intent intent) {
try {
- DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent,
- SampleDownloaderService.class);
+ DownloaderClientMarshaller.startDownloadServiceIfRequired(context,
+ intent, SampleDownloaderService.class);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
@@ -693,16 +697,19 @@ versionCode)}</li>
<p>For example, the sample app provided in the Apk Expansion package calls the
following method in the activity's {@link android.app.Activity#onCreate onCreate()} method to check
whether the expansion files already exist on the device:</p>
+
<pre>
boolean expansionFilesDelivered() {
for (XAPKFile xf : xAPKS) {
- String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsBase, xf.mFileVersion);
+ String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsBase,
+ xf.mFileVersion);
if (!Helpers.doesFileExist(this, fileName, xf.mFileSize, false))
return false;
}
return true;
}
</pre>
+
<p>In this case, each {@code XAPKFile} object holds the version number and file size of a known
expansion file and a boolean as to whether it's the main expansion file. (See the sample
application's {@code SampleDownloaderActivity} class for details.)</p>
@@ -740,6 +747,7 @@ the Downloader Library begins the download and you should update your activity U
display the download progress (see the next step). If the response <em>is</em> {@code
NO_DOWNLOAD_REQUIRED}, then the files are available and your application can start.</p>
<p>For example:</p>
+
<pre>
&#64;Override
public void onCreate(Bundle savedInstanceState) {
@@ -754,11 +762,14 @@ public void onCreate(Bundle savedInstanceState) {
notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// Start the download service (if required)
- int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
+ int startResult =
+ DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
pendingIntent, SampleDownloaderService.class);
- // If download has started, initialize this activity to show download progress
+ // If download has started, initialize this activity to show
+ // download progress
if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
- // This is where you do set up to display the download progress (next step)
+ // This is where you do set up to display the download
+ // progress (next step)
...
return;
} // If the download wasn't necessary, fall through to start the app
@@ -766,6 +777,7 @@ public void onCreate(Bundle savedInstanceState) {
startApp(); // Expansion files are available, start the app
}
</pre>
+
</li>
<li>When the {@code startDownloadServiceIfRequired()} method returns anything <em>other
than</em> {@code NO_DOWNLOAD_REQUIRED}, create an instance of {@code IStub} by
@@ -783,9 +795,11 @@ android.app.Activity#onCreate onCreate()} method, after {@code startDownloadServ
starts the download. </p>
<p>For example, in the previous code sample for {@link android.app.Activity#onCreate
onCreate()}, you can respond to the {@code startDownloadServiceIfRequired()} result like this:</p>
+
<pre>
// Start the download service (if required)
- int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
+ int startResult =
+ DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
pendingIntent, SampleDownloaderService.class);
// If download has started, initialize activity to show progress
if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
@@ -892,7 +906,8 @@ others. By default, this flag is <em>not</em> enabled, so the user must be on Wi
expansion files. You might want to provide a user preference to enable downloads over
the cellular network. In which case, you can call:
<pre>
-mRemoteService.setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
+mRemoteService
+ .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
</pre>
</dd>
</dl>
@@ -975,10 +990,12 @@ to both your expansion files:</p>
// The shared path to all app expansion files
private final static String EXP_PATH = "/Android/obb/";
-static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) {
+static String[] getAPKExpansionFiles(Context ctx, int mainVersion,
+ int patchVersion) {
String packageName = ctx.getPackageName();
Vector&lt;String> ret = new Vector&lt;String>();
- if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ if (Environment.getExternalStorageState()
+ .equals(Environment.MEDIA_MOUNTED)) {
// Build the full path to the app's expansion files
File root = Environment.getExternalStorageDirectory();
File expPath = new File(root.toString() + EXP_PATH + packageName);
@@ -1102,7 +1119,8 @@ following:</p>
<pre>
// Get a ZipResourceFile representing a merger of both the main and patch files
-ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(appContext,
+ZipResourceFile expansionFile =
+ APKExpansionSupport.getAPKExpansionZipFile(appContext,
mainVersion, patchVersion);
// Get an input stream for a known file inside the expansion file ZIPs
@@ -1190,28 +1208,18 @@ android.content.Context#getExternalFilesDir getExternalFilesDir()}.</li>
opens, it's important that you test this process to be sure your application can successfully query
for the URLs, download the files, and save them to the device.</p>
-<p>To test your application's implementation of the manual download procedure, you must upload
-your application to Google Play as a "draft" to make your expansion files available for
-download:</p>
-
-<ol>
- <li>Upload your APK and corresponding expansion files using the Google Play Developer
-Console.</li>
- <li>Fill in the necessary application details (title, screenshots, etc.). You can come back and
-finalize these details before publishing your application.
- <p>Click the <strong>Save</strong> button. <em>Do not click Publish.</em> This saves
-the application as a draft, such that your application is not published for Google Play users,
-but the expansion files are available for you to test the download process.</p></li>
- <li>Install the application on your test device using the Eclipse tools or <a
-href="{@docRoot}tools/help/adb.html">{@code adb}</a>.</li>
- <li>Launch the app.</li>
-</ol>
-
-<p>If everything works as expected, your application should begin downloading the expansion
+<p>To test your application's implementation of the manual download procedure,
+you can publish it to the alpha or beta channel, so it will only be available to
+authorized testers.
+If everything works as expected, your application should begin downloading the expansion
files as soon as the main activity starts.</p>
-
-
+<p class="note"><strong>Note:</strong> Previously you could test an app by
+uploading an unpublished "draft" version. This functionality is no longer
+supported; instead, you must publish it to the alpha or beta distribution
+channel. For more information, see <a
+href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps
+are No Longer Supported</a>.
<h2 id="Updating">Updating Your Application</h2>
diff --git a/docs/html/google/play/licensing/licensing-reference.jd b/docs/html/google/play/licensing/licensing-reference.jd
index 7bfa61a..d4ca79a 100644
--- a/docs/html/google/play/licensing/licensing-reference.jd
+++ b/docs/html/google/play/licensing/licensing-reference.jd
@@ -151,7 +151,8 @@ returned by the Google Play server in a license response.</p>
<tr>
<td>{@code LICENSED}</td>
<td>The application is licensed to the user. The user has purchased the
-application or the application only exists as a draft.</td>
+application, or is authorized to download and install the alpha or beta version
+of the application.</td>
<td>Yes</td>
<td><code>VT</code>,&nbsp;<code>GT</code>, <code>GR</code></td>
<td><em>Allow access according to {@code Policy} constraints.</em></td>
@@ -233,16 +234,14 @@ implementation.</p>
href="{@docRoot}google/play/licensing/setting-up.html#test-env">
Setting Up The Testing Environment</a>, the response code can be manually
overridden for the application developer and any registered test users via the
-Google Play Developer Console.
-<br/><br/>
-Additionally, as noted above, applications that are in draft mode (in other
-words, applications that have been uploaded but have <em>never</em> been
-published) will return {@code LICENSED} for all users, even if not listed as a test
-user. Since the application has never been offered for download, it is assumed
-that any users running it must have obtained it from an authorized channel for
-testing purposes.</p>
-
+Google Play Developer Console.</p>
+<p class="note"><strong>Note:</strong> Previously you could test an app by
+uploading an unpublished "draft" version. This functionality is no longer
+supported; instead, you must publish it to the alpha or beta distribution
+channel. For more information, see <a
+href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps
+are No Longer Supported</a>.
<h2 id="extras">Server Response Extras</h2>
@@ -430,8 +429,8 @@ public boolean allowAccess() {
}
} else if (mLastResponse == LicenseResponse.RETRY &amp;&amp;
ts &lt; mLastResponseTime + MILLIS_PER_MINUTE) {
- // Only allow access if we are within the retry period or we haven't used up our
- // max retries.
+ // Only allow access if we are within the retry period
+ // or we haven't used up our max retries.
return (ts &lt;= mRetryUntil || mRetryCount &lt;= mMaxRetries);
}
return false;
diff --git a/docs/html/google/play/licensing/overview.jd b/docs/html/google/play/licensing/overview.jd
index 4e1a9c9..a2d5379 100644
--- a/docs/html/google/play/licensing/overview.jd
+++ b/docs/html/google/play/licensing/overview.jd
@@ -38,12 +38,11 @@ the licensing server and receives the result. The Google Play application sends
the result to your application, which can allow or disallow further use of the
application as needed.</p>
-<p class="note"><strong>Note:</strong> If a paid application has been uploaded
-to Google Play, but saved only as a draft application (the app is
-unpublished), the licensing server considers all users to be licensed users of
-the application (because it's not even possible to purchase the app). This
-exception is necessary in order for you to perform testing of your licensing
-implementation.</p>
+<p class="note"><strong>Note:</strong> If a version of an app is in the alpha or
+beta channel, all users who are authorized to download and install that app are
+considered to be licensed users of the app. For more information, see <a
+href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">Alpha
+and Beta Testing</a>.</p>
<div class="figure" style="width:469px">
<img src="{@docRoot}images/licensing_arch.png" alt=""/>
@@ -52,6 +51,12 @@ license check through the License Verification Library and the Google Play
client, which handles communication with the Google Play server.</p>
</div>
+<p class="note"><strong>Note:</strong> Previously you could test an app by
+uploading an unpublished "draft" version. This functionality is no longer
+supported; instead, you must publish it to the alpha or beta distribution
+channel. For more information, see <a
+href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps
+are No Longer Supported</a>.
<p>To properly identify the user and determine the license status, the licensing server requires
information about the application and user&mdash;your application and the Google Play client work
diff --git a/docs/html/tools/device.jd b/docs/html/tools/device.jd
index e9caa44..e748b12 100644
--- a/docs/html/tools/device.jd
+++ b/docs/html/tools/device.jd
@@ -280,6 +280,10 @@ above.</p>
<td><code>0fce</code></td>
</tr>
<tr>
+ <td>Sony Mobile Communications</td>
+ <td><code>0fce</code></td>
+ </tr>
+ <tr>
<td>Teleepoch</td>
<td><code>2340</code></td>
</tr>
diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java
index a40085b..57e0f27 100644
--- a/graphics/java/android/graphics/Camera.java
+++ b/graphics/java/android/graphics/Camera.java
@@ -154,7 +154,7 @@ public class Camera {
getMatrix(mMatrix);
canvas.concat(mMatrix);
} else {
- nativeApplyToCanvas(canvas.getNativeCanvas());
+ nativeApplyToCanvas(canvas.getNativeCanvasWrapper());
}
}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 5e4e42b..bd868f2 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -39,11 +39,11 @@ import javax.microedition.khronos.opengles.GL;
public class Canvas {
// assigned in constructors or setBitmap, freed in finalizer
- private long mNativeCanvas;
+ private long mNativeCanvasWrapper;
/** @hide */
- public long getNativeCanvas() {
- return mNativeCanvas;
+ public long getNativeCanvasWrapper() {
+ return mNativeCanvasWrapper;
}
// may be null
@@ -88,10 +88,10 @@ public class Canvas {
private final CanvasFinalizer mFinalizer;
private static final class CanvasFinalizer {
- private long mNativeCanvas;
+ private long mNativeCanvasWrapper;
public CanvasFinalizer(long nativeCanvas) {
- mNativeCanvas = nativeCanvas;
+ mNativeCanvasWrapper = nativeCanvas;
}
@Override
@@ -104,9 +104,9 @@ public class Canvas {
}
public void dispose() {
- if (mNativeCanvas != 0) {
- finalizer(mNativeCanvas);
- mNativeCanvas = 0;
+ if (mNativeCanvasWrapper != 0) {
+ finalizer(mNativeCanvasWrapper);
+ mNativeCanvasWrapper = 0;
}
}
}
@@ -120,8 +120,8 @@ public class Canvas {
public Canvas() {
if (!isHardwareAccelerated()) {
// 0 means no native bitmap
- mNativeCanvas = initRaster(0);
- mFinalizer = new CanvasFinalizer(mNativeCanvas);
+ mNativeCanvasWrapper = initRaster(0);
+ mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
} else {
mFinalizer = null;
}
@@ -141,8 +141,8 @@ public class Canvas {
throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
}
throwIfCannotDraw(bitmap);
- mNativeCanvas = initRaster(bitmap.ni());
- mFinalizer = new CanvasFinalizer(mNativeCanvas);
+ mNativeCanvasWrapper = initRaster(bitmap.ni());
+ mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
mBitmap = bitmap;
mDensity = bitmap.mDensity;
}
@@ -152,26 +152,12 @@ public class Canvas {
if (nativeCanvas == 0) {
throw new IllegalStateException();
}
- mNativeCanvas = nativeCanvas;
- mFinalizer = new CanvasFinalizer(mNativeCanvas);
+ mNativeCanvasWrapper = initCanvas(nativeCanvas);
+ mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
mDensity = Bitmap.getDefaultDensity();
}
/**
- * Replace existing canvas while ensuring that the swap has occurred before
- * the previous native canvas is unreferenced.
- */
- private void safeCanvasSwap(long nativeCanvas, boolean copyState) {
- final long oldCanvas = mNativeCanvas;
- mNativeCanvas = nativeCanvas;
- mFinalizer.mNativeCanvas = nativeCanvas;
- if (copyState) {
- copyNativeCanvasState(oldCanvas, mNativeCanvas);
- }
- finalizer(oldCanvas);
- }
-
- /**
* Returns null.
*
* @deprecated This method is not supported and should not be invoked.
@@ -212,7 +198,7 @@ public class Canvas {
}
if (bitmap == null) {
- safeCanvasSwap(initRaster(0), false);
+ native_setBitmap(mNativeCanvasWrapper, 0, false);
mDensity = Bitmap.DENSITY_NONE;
} else {
if (!bitmap.isMutable()) {
@@ -220,7 +206,7 @@ public class Canvas {
}
throwIfCannotDraw(bitmap);
- safeCanvasSwap(initRaster(bitmap.ni()), true);
+ native_setBitmap(mNativeCanvasWrapper, bitmap.ni(), true);
mDensity = bitmap.mDensity;
}
@@ -228,6 +214,13 @@ public class Canvas {
}
/**
+ * setBitmap() variant for native callers with a raw bitmap handle.
+ */
+ private void setNativeBitmap(long bitmapHandle) {
+ native_setBitmap(mNativeCanvasWrapper, bitmapHandle, false);
+ }
+
+ /**
* Set the viewport dimensions if this canvas is GL based. If it is not,
* this method is ignored and no exception is thrown.
*
@@ -382,7 +375,7 @@ public class Canvas {
* @return value to pass to restoreToCount() to balance this save()
*/
public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
- return native_saveLayer(mNativeCanvas, bounds,
+ return native_saveLayer(mNativeCanvasWrapper, bounds,
paint != null ? paint.mNativePaint : 0,
saveFlags);
}
@@ -399,7 +392,7 @@ public class Canvas {
*/
public int saveLayer(float left, float top, float right, float bottom, Paint paint,
int saveFlags) {
- return native_saveLayer(mNativeCanvas, left, top, right, bottom,
+ return native_saveLayer(mNativeCanvasWrapper, left, top, right, bottom,
paint != null ? paint.mNativePaint : 0,
saveFlags);
}
@@ -429,7 +422,7 @@ public class Canvas {
*/
public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) {
alpha = Math.min(255, Math.max(0, alpha));
- return native_saveLayerAlpha(mNativeCanvas, bounds, alpha, saveFlags);
+ return native_saveLayerAlpha(mNativeCanvasWrapper, bounds, alpha, saveFlags);
}
/**
@@ -444,7 +437,7 @@ public class Canvas {
*/
public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
int saveFlags) {
- return native_saveLayerAlpha(mNativeCanvas, left, top, right, bottom,
+ return native_saveLayerAlpha(mNativeCanvasWrapper, left, top, right, bottom,
alpha, saveFlags);
}
@@ -548,7 +541,7 @@ public class Canvas {
* @param matrix The matrix to preconcatenate with the current matrix
*/
public void concat(Matrix matrix) {
- if (matrix != null) native_concat(mNativeCanvas, matrix.native_instance);
+ if (matrix != null) native_concat(mNativeCanvasWrapper, matrix.native_instance);
}
/**
@@ -565,7 +558,7 @@ public class Canvas {
* @see #concat(Matrix)
*/
public void setMatrix(Matrix matrix) {
- native_setMatrix(mNativeCanvas,
+ native_setMatrix(mNativeCanvasWrapper,
matrix == null ? 0 : matrix.native_instance);
}
@@ -575,7 +568,7 @@ public class Canvas {
*/
@Deprecated
public void getMatrix(Matrix ctm) {
- native_getCTM(mNativeCanvas, ctm.native_instance);
+ native_getCTM(mNativeCanvasWrapper, ctm.native_instance);
}
/**
@@ -598,7 +591,7 @@ public class Canvas {
* @return true if the resulting clip is non-empty
*/
public boolean clipRect(RectF rect, Region.Op op) {
- return native_clipRect(mNativeCanvas, rect.left, rect.top, rect.right, rect.bottom,
+ return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
op.nativeInt);
}
@@ -611,7 +604,7 @@ public class Canvas {
* @return true if the resulting clip is non-empty
*/
public boolean clipRect(Rect rect, Region.Op op) {
- return native_clipRect(mNativeCanvas, rect.left, rect.top, rect.right, rect.bottom,
+ return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
op.nativeInt);
}
@@ -649,7 +642,7 @@ public class Canvas {
* @return true if the resulting clip is non-empty
*/
public boolean clipRect(float left, float top, float right, float bottom, Region.Op op) {
- return native_clipRect(mNativeCanvas, left, top, right, bottom, op.nativeInt);
+ return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom, op.nativeInt);
}
/**
@@ -690,7 +683,7 @@ public class Canvas {
* @return true if the resulting is non-empty
*/
public boolean clipPath(Path path, Region.Op op) {
- return native_clipPath(mNativeCanvas, path.ni(), op.nativeInt);
+ return native_clipPath(mNativeCanvasWrapper, path.ni(), op.nativeInt);
}
/**
@@ -718,7 +711,7 @@ public class Canvas {
* current matrix. Use {@link #clipRect(Rect)} as an alternative.
*/
public boolean clipRegion(Region region, Region.Op op) {
- return native_clipRegion(mNativeCanvas, region.ni(), op.nativeInt);
+ return native_clipRegion(mNativeCanvasWrapper, region.ni(), op.nativeInt);
}
/**
@@ -748,7 +741,7 @@ public class Canvas {
nativeFilter = filter.mNativeInt;
}
mDrawFilter = filter;
- nativeSetDrawFilter(mNativeCanvas, nativeFilter);
+ nativeSetDrawFilter(mNativeCanvasWrapper, nativeFilter);
}
public enum EdgeType {
@@ -787,7 +780,7 @@ public class Canvas {
* does not intersect with the canvas' clip
*/
public boolean quickReject(RectF rect, EdgeType type) {
- return native_quickReject(mNativeCanvas, rect);
+ return native_quickReject(mNativeCanvasWrapper, rect);
}
/**
@@ -806,7 +799,7 @@ public class Canvas {
* does not intersect with the canvas' clip
*/
public boolean quickReject(Path path, EdgeType type) {
- return native_quickReject(mNativeCanvas, path.ni());
+ return native_quickReject(mNativeCanvasWrapper, path.ni());
}
/**
@@ -831,7 +824,7 @@ public class Canvas {
*/
public boolean quickReject(float left, float top, float right, float bottom,
EdgeType type) {
- return native_quickReject(mNativeCanvas, left, top, right, bottom);
+ return native_quickReject(mNativeCanvasWrapper, left, top, right, bottom);
}
/**
@@ -845,7 +838,7 @@ public class Canvas {
* @return true if the current clip is non-empty.
*/
public boolean getClipBounds(Rect bounds) {
- return native_getClipBounds(mNativeCanvas, bounds);
+ return native_getClipBounds(mNativeCanvasWrapper, bounds);
}
/**
@@ -868,7 +861,7 @@ public class Canvas {
* @param b blue component (0..255) of the color to draw onto the canvas
*/
public void drawRGB(int r, int g, int b) {
- native_drawRGB(mNativeCanvas, r, g, b);
+ native_drawRGB(mNativeCanvasWrapper, r, g, b);
}
/**
@@ -881,7 +874,7 @@ public class Canvas {
* @param b blue component (0..255) of the color to draw onto the canvas
*/
public void drawARGB(int a, int r, int g, int b) {
- native_drawARGB(mNativeCanvas, a, r, g, b);
+ native_drawARGB(mNativeCanvasWrapper, a, r, g, b);
}
/**
@@ -891,7 +884,7 @@ public class Canvas {
* @param color the color to draw onto the canvas
*/
public void drawColor(int color) {
- native_drawColor(mNativeCanvas, color);
+ native_drawColor(mNativeCanvasWrapper, color);
}
/**
@@ -902,7 +895,7 @@ public class Canvas {
* @param mode the porter-duff mode to apply to the color
*/
public void drawColor(int color, PorterDuff.Mode mode) {
- native_drawColor(mNativeCanvas, color, mode.nativeInt);
+ native_drawColor(mNativeCanvasWrapper, color, mode.nativeInt);
}
/**
@@ -913,7 +906,7 @@ public class Canvas {
* @param paint The paint used to draw onto the canvas
*/
public void drawPaint(Paint paint) {
- native_drawPaint(mNativeCanvas, paint.mNativePaint);
+ native_drawPaint(mNativeCanvasWrapper, paint.mNativePaint);
}
/**
@@ -959,7 +952,7 @@ public class Canvas {
* @param paint The paint used to draw the line
*/
public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
- native_drawLine(mNativeCanvas, startX, startY, stopX, stopY, paint.mNativePaint);
+ native_drawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.mNativePaint);
}
/**
@@ -991,7 +984,7 @@ public class Canvas {
* @param paint The paint used to draw the rect
*/
public void drawRect(RectF rect, Paint paint) {
- native_drawRect(mNativeCanvas, rect, paint.mNativePaint);
+ native_drawRect(mNativeCanvasWrapper, rect, paint.mNativePaint);
}
/**
@@ -1017,7 +1010,7 @@ public class Canvas {
* @param paint The paint used to draw the rect
*/
public void drawRect(float left, float top, float right, float bottom, Paint paint) {
- native_drawRect(mNativeCanvas, left, top, right, bottom, paint.mNativePaint);
+ native_drawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.mNativePaint);
}
/**
@@ -1030,7 +1023,7 @@ public class Canvas {
if (oval == null) {
throw new NullPointerException();
}
- native_drawOval(mNativeCanvas, oval, paint.mNativePaint);
+ native_drawOval(mNativeCanvasWrapper, oval, paint.mNativePaint);
}
/**
@@ -1044,7 +1037,7 @@ public class Canvas {
* @param paint The paint used to draw the circle
*/
public void drawCircle(float cx, float cy, float radius, Paint paint) {
- native_drawCircle(mNativeCanvas, cx, cy, radius, paint.mNativePaint);
+ native_drawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.mNativePaint);
}
/**
@@ -1075,7 +1068,7 @@ public class Canvas {
if (oval == null) {
throw new NullPointerException();
}
- native_drawArc(mNativeCanvas, oval, startAngle, sweepAngle,
+ native_drawArc(mNativeCanvasWrapper, oval, startAngle, sweepAngle,
useCenter, paint.mNativePaint);
}
@@ -1102,7 +1095,7 @@ public class Canvas {
*/
public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
Paint paint) {
- native_drawRoundRect(mNativeCanvas, left, top, right, bottom, rx, ry, paint.mNativePaint);
+ native_drawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, paint.mNativePaint);
}
/**
@@ -1113,7 +1106,7 @@ public class Canvas {
* @param paint The paint used to draw the path
*/
public void drawPath(Path path, Paint paint) {
- native_drawPath(mNativeCanvas, path.ni(), paint.mNativePaint);
+ native_drawPath(mNativeCanvasWrapper, path.ni(), paint.mNativePaint);
}
/**
@@ -1177,7 +1170,7 @@ public class Canvas {
*/
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
throwIfCannotDraw(bitmap);
- native_drawBitmap(mNativeCanvas, bitmap.ni(), left, top,
+ native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), left, top,
paint != null ? paint.mNativePaint : 0, mDensity, mScreenDensity, bitmap.mDensity);
}
@@ -1208,7 +1201,7 @@ public class Canvas {
throw new NullPointerException();
}
throwIfCannotDraw(bitmap);
- native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst,
+ native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
}
@@ -1239,7 +1232,7 @@ public class Canvas {
throw new NullPointerException();
}
throwIfCannotDraw(bitmap);
- native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst,
+ native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
}
@@ -1291,7 +1284,7 @@ public class Canvas {
return;
}
// punch down to native for the actual draw
- native_drawBitmap(mNativeCanvas, colors, offset, stride, x, y, width, height, hasAlpha,
+ native_drawBitmap(mNativeCanvasWrapper, colors, offset, stride, x, y, width, height, hasAlpha,
paint != null ? paint.mNativePaint : 0);
}
@@ -1319,7 +1312,7 @@ public class Canvas {
* @param paint May be null. The paint used to draw the bitmap
*/
public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
- nativeDrawBitmapMatrix(mNativeCanvas, bitmap.ni(), matrix.ni(),
+ nativeDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.ni(), matrix.ni(),
paint != null ? paint.mNativePaint : 0);
}
@@ -1373,7 +1366,7 @@ public class Canvas {
// no mul by 2, since we need only 1 color per vertex
checkRange(colors.length, colorOffset, count);
}
- nativeDrawBitmapMesh(mNativeCanvas, bitmap.ni(), meshWidth, meshHeight,
+ nativeDrawBitmapMesh(mNativeCanvasWrapper, bitmap.ni(), meshWidth, meshHeight,
verts, vertOffset, colors, colorOffset,
paint != null ? paint.mNativePaint : 0);
}
@@ -1436,7 +1429,7 @@ public class Canvas {
if (indices != null) {
checkRange(indices.length, indexOffset, indexCount);
}
- nativeDrawVertices(mNativeCanvas, mode.nativeInt, vertexCount, verts,
+ nativeDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts,
vertOffset, texs, texOffset, colors, colorOffset,
indices, indexOffset, indexCount, paint.mNativePaint);
}
@@ -1455,7 +1448,7 @@ public class Canvas {
(text.length - index - count)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_drawText(mNativeCanvas, text, index, count, x, y, paint.mBidiFlags,
+ native_drawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
paint.mNativePaint, paint.mNativeTypeface);
}
@@ -1469,7 +1462,7 @@ public class Canvas {
* @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawText(String text, float x, float y, Paint paint) {
- native_drawText(mNativeCanvas, text, 0, text.length(), x, y, paint.mBidiFlags,
+ native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
paint.mNativePaint, paint.mNativeTypeface);
}
@@ -1488,7 +1481,7 @@ public class Canvas {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_drawText(mNativeCanvas, text, start, end, x, y, paint.mBidiFlags,
+ native_drawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
paint.mNativePaint, paint.mNativeTypeface);
}
@@ -1508,7 +1501,7 @@ public class Canvas {
public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
- native_drawText(mNativeCanvas, text.toString(), start, end, x, y,
+ native_drawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
} else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawText(this, start, end, x, y,
@@ -1516,7 +1509,7 @@ public class Canvas {
} else {
char[] buf = TemporaryBuffer.obtain(end - start);
TextUtils.getChars(text, start, end, buf, 0);
- native_drawText(mNativeCanvas, buf, 0, end - start, x, y,
+ native_drawText(mNativeCanvasWrapper, buf, 0, end - start, x, y,
paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
TemporaryBuffer.recycle(buf);
}
@@ -1559,7 +1552,7 @@ public class Canvas {
throw new IllegalArgumentException("unknown dir: " + dir);
}
- native_drawTextRun(mNativeCanvas, text, index, count,
+ native_drawTextRun(mNativeCanvasWrapper, text, index, count,
contextIndex, contextCount, x, y, dir, paint.mNativePaint, paint.mNativeTypeface);
}
@@ -1597,7 +1590,7 @@ public class Canvas {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
- native_drawTextRun(mNativeCanvas, text.toString(), start, end,
+ native_drawTextRun(mNativeCanvasWrapper, text.toString(), start, end,
contextStart, contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
} else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawTextRun(this, start, end,
@@ -1607,7 +1600,7 @@ public class Canvas {
int len = end - start;
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
- native_drawTextRun(mNativeCanvas, buf, start - contextStart, len,
+ native_drawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
0, contextLen, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
TemporaryBuffer.recycle(buf);
}
@@ -1632,7 +1625,7 @@ public class Canvas {
if (index < 0 || index + count > text.length || count*2 > pos.length) {
throw new IndexOutOfBoundsException();
}
- native_drawPosText(mNativeCanvas, text, index, count, pos,
+ native_drawPosText(mNativeCanvasWrapper, text, index, count, pos,
paint.mNativePaint);
}
@@ -1652,7 +1645,7 @@ public class Canvas {
if (text.length()*2 > pos.length) {
throw new ArrayIndexOutOfBoundsException();
}
- native_drawPosText(mNativeCanvas, text, pos, paint.mNativePaint);
+ native_drawPosText(mNativeCanvasWrapper, text, pos, paint.mNativePaint);
}
/**
@@ -1673,7 +1666,7 @@ public class Canvas {
if (index < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
- native_drawTextOnPath(mNativeCanvas, text, index, count,
+ native_drawTextOnPath(mNativeCanvasWrapper, text, index, count,
path.ni(), hOffset, vOffset,
paint.mBidiFlags, paint.mNativePaint);
}
@@ -1693,7 +1686,7 @@ public class Canvas {
*/
public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
if (text.length() > 0) {
- native_drawTextOnPath(mNativeCanvas, text, path.ni(), hOffset, vOffset,
+ native_drawTextOnPath(mNativeCanvasWrapper, text, path.ni(), hOffset, vOffset,
paint.mBidiFlags, paint.mNativePaint);
}
}
@@ -1767,8 +1760,10 @@ public class Canvas {
public static native void freeTextLayoutCaches();
private static native long initRaster(long nativeBitmapOrZero);
- private static native void copyNativeCanvasState(long nativeSrcCanvas,
- long nativeDstCanvas);
+ private static native long initCanvas(long canvasHandle);
+ private static native void native_setBitmap(long canvasHandle,
+ long bitmapHandle,
+ boolean copyState);
private static native int native_saveLayer(long nativeCanvas,
RectF bounds,
long nativePaint,
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 6ff5f4f..befac92 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -164,12 +164,12 @@ public class NinePatch {
}
void drawSoftware(Canvas canvas, RectF location, Paint paint) {
- nativeDraw(canvas.getNativeCanvas(), location, mBitmap.ni(), mNativeChunk,
+ nativeDraw(canvas.getNativeCanvasWrapper(), location, mBitmap.ni(), mNativeChunk,
paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity);
}
void drawSoftware(Canvas canvas, Rect location, Paint paint) {
- nativeDraw(canvas.getNativeCanvas(), location, mBitmap.ni(), mNativeChunk,
+ nativeDraw(canvas.getNativeCanvasWrapper(), location, mBitmap.ni(), mNativeChunk,
paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity);
}
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index a16c099..de458af 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -107,7 +107,7 @@ public class Picture {
if (mRecordingCanvas != null) {
endRecording();
}
- nativeDraw(canvas.getNativeCanvas(), mNativePicture);
+ nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
}
/**
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index f82acc3..1512da5 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -612,7 +612,9 @@ public class GradientDrawable extends Drawable {
case LINE: {
RectF r = mRect;
float y = r.centerY();
- canvas.drawLine(r.left, y, r.right, y, mStrokePaint);
+ if (haveStroke) {
+ canvas.drawLine(r.left, y, r.right, y, mStrokePaint);
+ }
break;
}
case RING:
@@ -1153,6 +1155,10 @@ public class GradientDrawable extends Drawable {
// Extract the theme attributes, if any.
st.mAttrPadding = a.extractThemeAttrs();
+ if (st.mPadding == null) {
+ st.mPadding = new Rect();
+ }
+
final Rect pad = st.mPadding;
pad.set(a.getDimensionPixelOffset(R.styleable.GradientDrawablePadding_left, pad.left),
a.getDimensionPixelOffset(R.styleable.GradientDrawablePadding_top, pad.top),
@@ -1424,12 +1430,10 @@ public class GradientDrawable extends Drawable {
}
final static class GradientState extends ConstantState {
- public final Rect mPadding = new Rect();
-
public int mChangingConfigurations;
public int mShape = RECTANGLE;
public int mGradient = LINEAR_GRADIENT;
- public int mAngle;
+ public int mAngle = 0;
public Orientation mOrientation;
public ColorStateList mColorStateList;
public ColorStateList mStrokeColorStateList;
@@ -1437,11 +1441,12 @@ public class GradientDrawable extends Drawable {
public int[] mTempColors; // no need to copy
public float[] mTempPositions; // no need to copy
public float[] mPositions;
- public int mStrokeWidth = -1; // if >= 0 use stroking.
- public float mStrokeDashWidth;
- public float mStrokeDashGap;
- public float mRadius; // use this if mRadiusArray is null
- public float[] mRadiusArray;
+ public int mStrokeWidth = -1; // if >= 0 use stroking.
+ public float mStrokeDashWidth = 0.0f;
+ public float mStrokeDashGap = 0.0f;
+ public float mRadius = 0.0f; // use this if mRadiusArray is null
+ public float[] mRadiusArray = null;
+ public Rect mPadding = null;
public int mWidth = -1;
public int mHeight = -1;
public float mInnerRadiusRatio = DEFAULT_INNER_RADIUS_RATIO;
@@ -1491,7 +1496,7 @@ public class GradientDrawable extends Drawable {
mRadiusArray = state.mRadiusArray.clone();
}
if (state.mPadding != null) {
- mPadding.set(state.mPadding);
+ mPadding = new Rect(state.mPadding);
}
mWidth = state.mWidth;
mHeight = state.mHeight;
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 2e47d3a..75cb0a0 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -304,7 +304,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
* @param right The right padding of the new layer.
* @param bottom The bottom padding of the new layer.
*/
- private void addLayer(Drawable layer, int[] themeAttrs, int id, int left, int top, int right,
+ void addLayer(Drawable layer, int[] themeAttrs, int id, int left, int top, int right,
int bottom) {
final LayerState st = mLayerState;
final int N = st.mChildren != null ? st.mChildren.length : 0;
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 24e8de6..ada741b 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -25,9 +25,10 @@ import android.graphics.CanvasProperty;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
+import android.util.MathUtils;
import android.view.HardwareCanvas;
import android.view.RenderNodeAnimator;
-import android.view.animation.AccelerateInterpolator;
+import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
@@ -35,7 +36,7 @@ import java.util.ArrayList;
* Draws a Quantum Paper ripple.
*/
class Ripple {
- private static final TimeInterpolator INTERPOLATOR = new AccelerateInterpolator();
+ private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
private static final float GLOBAL_SPEED = 1.0f;
private static final float WAVE_TOUCH_DOWN_ACCELERATION = 512.0f * GLOBAL_SPEED;
@@ -47,17 +48,23 @@ class Ripple {
private final ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<>();
private final ArrayList<RenderNodeAnimator> mPendingAnimations = new ArrayList<>();
- private final Drawable mOwner;
+ private final RippleDrawable mOwner;
/** Bounds used for computing max radius. */
private final Rect mBounds;
/** Full-opacity color for drawing this ripple. */
- private final int mColor;
+ private int mColor;
/** Maximum ripple radius. */
private float mOuterRadius;
+ /** Screen density used to adjust pixel-based velocities. */
+ private float mDensity;
+
+ private float mStartingX;
+ private float mStartingY;
+
// Hardware rendering properties.
private CanvasProperty<Paint> mPropPaint;
private CanvasProperty<Float> mPropRadius;
@@ -78,13 +85,13 @@ class Ripple {
// Software rendering properties.
private float mOuterOpacity = 0;
private float mOpacity = 1;
- private float mRadius = 0;
private float mOuterX;
private float mOuterY;
- private float mX;
- private float mY;
- private boolean mFinished;
+ // Values used to tween between the start and end positions.
+ private float mTweenRadius = 0;
+ private float mTweenX = 0;
+ private float mTweenY = 0;
/** Whether we should be drawing hardware animations. */
private boolean mHardwareAnimating;
@@ -92,28 +99,42 @@ class Ripple {
/** Whether we can use hardware acceleration for the exit animation. */
private boolean mCanUseHardware;
+ /** Whether we have an explicit maximum radius. */
+ private boolean mHasMaxRadius;
+
/**
* Creates a new ripple.
*/
- public Ripple(Drawable owner, Rect bounds, int color) {
+ public Ripple(RippleDrawable owner, Rect bounds, float startingX, float startingY) {
mOwner = owner;
mBounds = bounds;
+ mStartingX = startingX;
+ mStartingY = startingY;
+ }
+
+ public void setup(int maxRadius, int color, float density) {
mColor = color | 0xFF000000;
- final float halfWidth = bounds.width() / 2.0f;
- final float halfHeight = bounds.height() / 2.0f;
- mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
+ if (maxRadius != RippleDrawable.RADIUS_AUTO) {
+ mHasMaxRadius = true;
+ mOuterRadius = maxRadius;
+ } else {
+ final float halfWidth = mBounds.width() / 2.0f;
+ final float halfHeight = mBounds.height() / 2.0f;
+ mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
+ }
+
mOuterX = 0;
mOuterY = 0;
+ mDensity = density;
}
- public void setRadius(float r) {
- mRadius = r;
- invalidateSelf();
- }
-
- public float getRadius() {
- return mRadius;
+ public void onHotspotBoundsChanged() {
+ if (!mHasMaxRadius) {
+ final float halfWidth = mBounds.width() / 2.0f;
+ final float halfHeight = mBounds.height() / 2.0f;
+ mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
+ }
}
public void setOpacity(float a) {
@@ -134,29 +155,31 @@ class Ripple {
return mOuterOpacity;
}
- public void setX(float x) {
- mX = x;
+ public void setRadiusGravity(float r) {
+ mTweenRadius = r;
invalidateSelf();
}
- public float getX() {
- return mX;
+ public float getRadiusGravity() {
+ return mTweenRadius;
}
- public void setY(float y) {
- mY = y;
+ public void setXGravity(float x) {
+ mTweenX = x;
invalidateSelf();
}
- public float getY() {
- return mY;
+ public float getXGravity() {
+ return mTweenX;
}
- /**
- * Returns whether this ripple has finished exiting.
- */
- public boolean isFinished() {
- return mFinished;
+ public void setYGravity(float y) {
+ mTweenY = y;
+ invalidateSelf();
+ }
+
+ public float getYGravity() {
+ return mTweenY;
}
/**
@@ -204,28 +227,27 @@ class Ripple {
}
private boolean drawSoftware(Canvas c, Paint p) {
- final float radius = mRadius;
- final float opacity = mOpacity;
- final float outerOpacity = mOuterOpacity;
+ boolean hasContent = false;
// Cache the paint alpha so we can restore it later.
final int paintAlpha = p.getAlpha();
- final int alpha = (int) (255 * opacity + 0.5f);
- final int outerAlpha = (int) (255 * outerOpacity + 0.5f);
-
- boolean hasContent = false;
- if (outerAlpha > 0 && alpha > 0) {
- p.setAlpha(Math.min(alpha, outerAlpha));
+ final int outerAlpha = (int) (255 * mOuterOpacity + 0.5f);
+ if (outerAlpha > 0 && mOuterRadius > 0) {
+ p.setAlpha(outerAlpha);
p.setStyle(Style.FILL);
c.drawCircle(mOuterX, mOuterY, mOuterRadius, p);
hasContent = true;
}
- if (opacity > 0 && radius > 0) {
+ final int alpha = (int) (255 * mOpacity + 0.5f);
+ final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
+ if (alpha > 0 && radius > 0) {
+ final float x = MathUtils.lerp(mStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
+ final float y = MathUtils.lerp(mStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
p.setAlpha(alpha);
p.setStyle(Style.FILL);
- c.drawCircle(mX, mY, radius, p);
+ c.drawCircle(x, y, radius, p);
hasContent = true;
}
@@ -235,45 +257,49 @@ class Ripple {
}
/**
- * Returns the maximum bounds for this ripple.
+ * Returns the maximum bounds of the ripple relative to the ripple center.
*/
public void getBounds(Rect bounds) {
final int outerX = (int) mOuterX;
final int outerY = (int) mOuterY;
final int r = (int) mOuterRadius;
bounds.set(outerX - r, outerY - r, outerX + r, outerY + r);
-
- final int x = (int) mX;
- final int y = (int) mY;
- bounds.union(x - r, y - r, x + r, y + r);
}
/**
- * Starts the enter animation at the specified absolute coordinates.
+ * Specifies the starting position relative to the drawable bounds. No-op if
+ * the ripple has already entered.
*/
- public void enter(float x, float y) {
- mX = x - mBounds.exactCenterX();
- mY = y - mBounds.exactCenterY();
+ public void move(float x, float y) {
+ mStartingX = x;
+ mStartingY = y;
+ }
+ /**
+ * Starts the enter animation.
+ */
+ public void enter() {
final int radiusDuration = (int)
- (1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION) + 0.5);
+ (1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_VELOCITY);
- final ObjectAnimator radius = ObjectAnimator.ofFloat(this, "radius", 0, mOuterRadius);
+ final ObjectAnimator radius = ObjectAnimator.ofFloat(this, "radiusGravity", 1);
radius.setAutoCancel(true);
radius.setDuration(radiusDuration);
+ radius.setInterpolator(LINEAR_INTERPOLATOR);
- final ObjectAnimator cX = ObjectAnimator.ofFloat(this, "x", mOuterX);
+ final ObjectAnimator cX = ObjectAnimator.ofFloat(this, "xGravity", 1);
cX.setAutoCancel(true);
cX.setDuration(radiusDuration);
- final ObjectAnimator cY = ObjectAnimator.ofFloat(this, "y", mOuterY);
+ final ObjectAnimator cY = ObjectAnimator.ofFloat(this, "yGravity", 1);
cY.setAutoCancel(true);
cY.setDuration(radiusDuration);
final ObjectAnimator outer = ObjectAnimator.ofFloat(this, "outerOpacity", 0, 1);
outer.setAutoCancel(true);
outer.setDuration(outerDuration);
+ outer.setInterpolator(LINEAR_INTERPOLATOR);
mAnimRadius = radius;
mAnimOuterOpacity = outer;
@@ -295,15 +321,16 @@ class Ripple {
public void exit() {
cancelSoftwareAnimations();
+ final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
final float remaining;
if (mAnimRadius != null && mAnimRadius.isRunning()) {
- remaining = mOuterRadius - mRadius;
+ remaining = mOuterRadius - radius;
} else {
remaining = mOuterRadius;
}
final int radiusDuration = (int) (1000 * Math.sqrt(remaining / (WAVE_TOUCH_UP_ACCELERATION
- + WAVE_TOUCH_DOWN_ACCELERATION)) + 0.5);
+ + WAVE_TOUCH_DOWN_ACCELERATION) * mDensity) + 0.5);
final int opacityDuration = (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
// Determine at what time the inner and outer opacity intersect.
@@ -325,6 +352,8 @@ class Ripple {
int inflectionOpacity) {
mPendingAnimations.clear();
+ final float startX = MathUtils.lerp(mStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
+ final float startY = MathUtils.lerp(mStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
final Paint outerPaint = new Paint();
outerPaint.setAntiAlias(true);
outerPaint.setColor(mColor);
@@ -335,58 +364,69 @@ class Ripple {
mPropOuterX = CanvasProperty.createFloat(mOuterX);
mPropOuterY = CanvasProperty.createFloat(mOuterY);
+ final float startRadius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(mColor);
paint.setAlpha((int) (255 * mOpacity + 0.5f));
paint.setStyle(Style.FILL);
mPropPaint = CanvasProperty.createPaint(paint);
- mPropRadius = CanvasProperty.createFloat(mRadius);
- mPropX = CanvasProperty.createFloat(mX);
- mPropY = CanvasProperty.createFloat(mY);
+ mPropRadius = CanvasProperty.createFloat(startRadius);
+ mPropX = CanvasProperty.createFloat(startX);
+ mPropY = CanvasProperty.createFloat(startY);
- final RenderNodeAnimator radius = new RenderNodeAnimator(mPropRadius, mOuterRadius);
- radius.setDuration(radiusDuration);
+ final RenderNodeAnimator radiusAnim = new RenderNodeAnimator(mPropRadius, mOuterRadius);
+ radiusAnim.setDuration(radiusDuration);
+ radiusAnim.setInterpolator(LINEAR_INTERPOLATOR);
- final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mOuterX);
- x.setDuration(radiusDuration);
+ final RenderNodeAnimator xAnim = new RenderNodeAnimator(mPropX, mOuterX);
+ xAnim.setDuration(radiusDuration);
+ xAnim.setInterpolator(LINEAR_INTERPOLATOR);
- final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mOuterY);
- y.setDuration(radiusDuration);
+ final RenderNodeAnimator yAnim = new RenderNodeAnimator(mPropY, mOuterY);
+ yAnim.setDuration(radiusDuration);
+ yAnim.setInterpolator(LINEAR_INTERPOLATOR);
- final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint,
+ final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPropPaint,
RenderNodeAnimator.PAINT_ALPHA, 0);
- opacity.setDuration(opacityDuration);
- opacity.addListener(mAnimationListener);
+ opacityAnim.setDuration(opacityDuration);
+ opacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
- final RenderNodeAnimator outerOpacity;
+ final RenderNodeAnimator outerOpacityAnim;
if (outerInflection > 0) {
// Outer opacity continues to increase for a bit.
- outerOpacity = new RenderNodeAnimator(
+ outerOpacityAnim = new RenderNodeAnimator(
mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, inflectionOpacity);
- outerOpacity.setDuration(outerInflection);
+ outerOpacityAnim.setDuration(outerInflection);
+ outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
// Chain the outer opacity exit animation.
final int outerDuration = opacityDuration - outerInflection;
if (outerDuration > 0) {
- final RenderNodeAnimator outerFadeOut = new RenderNodeAnimator(
+ final RenderNodeAnimator outerFadeOutAnim = new RenderNodeAnimator(
mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
- outerFadeOut.setDuration(outerDuration);
- outerFadeOut.setStartDelay(outerInflection);
-
- mPendingAnimations.add(outerFadeOut);
+ outerFadeOutAnim.setDuration(outerDuration);
+ outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR);
+ outerFadeOutAnim.setStartDelay(outerInflection);
+ outerFadeOutAnim.addListener(mAnimationListener);
+
+ mPendingAnimations.add(outerFadeOutAnim);
+ } else {
+ outerOpacityAnim.addListener(mAnimationListener);
}
} else {
- outerOpacity = new RenderNodeAnimator(
+ outerOpacityAnim = new RenderNodeAnimator(
mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
- outerOpacity.setDuration(opacityDuration);
+ outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
+ outerOpacityAnim.setDuration(opacityDuration);
+ outerOpacityAnim.addListener(mAnimationListener);
}
- mPendingAnimations.add(radius);
- mPendingAnimations.add(opacity);
- mPendingAnimations.add(outerOpacity);
- mPendingAnimations.add(x);
- mPendingAnimations.add(y);
+ mPendingAnimations.add(radiusAnim);
+ mPendingAnimations.add(opacityAnim);
+ mPendingAnimations.add(outerOpacityAnim);
+ mPendingAnimations.add(xAnim);
+ mPendingAnimations.add(yAnim);
mHardwareAnimating = true;
@@ -394,65 +434,80 @@ class Ripple {
}
private void exitSoftware(int radiusDuration, int opacityDuration, int outerInflection,
- float inflectionOpacity) {
- final ObjectAnimator radius = ObjectAnimator.ofFloat(this, "radius", mOuterRadius);
- radius.setAutoCancel(true);
- radius.setDuration(radiusDuration);
-
- final ObjectAnimator x = ObjectAnimator.ofFloat(this, "x", mOuterX);
- x.setAutoCancel(true);
- x.setDuration(radiusDuration);
-
- final ObjectAnimator y = ObjectAnimator.ofFloat(this, "y", mOuterY);
- y.setAutoCancel(true);
- y.setDuration(radiusDuration);
-
- final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, "opacity", 0);
- opacity.setAutoCancel(true);
- opacity.setDuration(opacityDuration);
- opacity.addListener(mAnimationListener);
-
- final ObjectAnimator outerOpacity;
+ int inflectionOpacity) {
+ final ObjectAnimator radiusAnim = ObjectAnimator.ofFloat(this, "radiusGravity", 1);
+ radiusAnim.setAutoCancel(true);
+ radiusAnim.setDuration(radiusDuration);
+ radiusAnim.setInterpolator(LINEAR_INTERPOLATOR);
+
+ final ObjectAnimator xAnim = ObjectAnimator.ofFloat(this, "xGravity", 1);
+ xAnim.setAutoCancel(true);
+ xAnim.setDuration(radiusDuration);
+ xAnim.setInterpolator(LINEAR_INTERPOLATOR);
+
+ final ObjectAnimator yAnim = ObjectAnimator.ofFloat(this, "yGravity", 1);
+ yAnim.setAutoCancel(true);
+ yAnim.setDuration(radiusDuration);
+ yAnim.setInterpolator(LINEAR_INTERPOLATOR);
+
+ final ObjectAnimator opacityAnim = ObjectAnimator.ofFloat(this, "opacity", 0);
+ opacityAnim.setAutoCancel(true);
+ opacityAnim.setDuration(opacityDuration);
+ opacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
+
+ final ObjectAnimator outerOpacityAnim;
if (outerInflection > 0) {
// Outer opacity continues to increase for a bit.
- outerOpacity = ObjectAnimator.ofFloat(this, "outerOpacity", inflectionOpacity);
- outerOpacity.setDuration(outerInflection);
+ outerOpacityAnim = ObjectAnimator.ofFloat(this,
+ "outerOpacity", inflectionOpacity / 255.0f);
+ outerOpacityAnim.setAutoCancel(true);
+ outerOpacityAnim.setDuration(outerInflection);
+ outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
// Chain the outer opacity exit animation.
final int outerDuration = opacityDuration - outerInflection;
- outerOpacity.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- final ObjectAnimator outerFadeOut = ObjectAnimator.ofFloat(Ripple.this,
- "outerOpacity", 0);
- outerFadeOut.setDuration(outerDuration);
-
- mAnimOuterOpacity = outerFadeOut;
-
- outerFadeOut.start();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- animation.removeListener(this);
- }
- });
+ if (outerDuration > 0) {
+ outerOpacityAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ final ObjectAnimator outerFadeOutAnim = ObjectAnimator.ofFloat(Ripple.this,
+ "outerOpacity", 0);
+ outerFadeOutAnim.setAutoCancel(true);
+ outerFadeOutAnim.setDuration(outerDuration);
+ outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR);
+ outerFadeOutAnim.addListener(mAnimationListener);
+
+ mAnimOuterOpacity = outerFadeOutAnim;
+
+ outerFadeOutAnim.start();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ animation.removeListener(this);
+ }
+ });
+ } else {
+ outerOpacityAnim.addListener(mAnimationListener);
+ }
} else {
- outerOpacity = ObjectAnimator.ofFloat(this, "outerOpacity", 0);
- outerOpacity.setDuration(opacityDuration);
+ outerOpacityAnim = ObjectAnimator.ofFloat(this, "outerOpacity", 0);
+ outerOpacityAnim.setAutoCancel(true);
+ outerOpacityAnim.setDuration(opacityDuration);
+ outerOpacityAnim.addListener(mAnimationListener);
}
- mAnimRadius = radius;
- mAnimOpacity = opacity;
- mAnimOuterOpacity = outerOpacity;
- mAnimX = opacity;
- mAnimY = opacity;
-
- radius.start();
- opacity.start();
- outerOpacity.start();
- x.start();
- y.start();
+ mAnimRadius = radiusAnim;
+ mAnimOpacity = opacityAnim;
+ mAnimOuterOpacity = outerOpacityAnim;
+ mAnimX = opacityAnim;
+ mAnimY = opacityAnim;
+
+ radiusAnim.start();
+ opacityAnim.start();
+ outerOpacityAnim.start();
+ xAnim.start();
+ yAnim.start();
}
/**
@@ -498,6 +553,11 @@ class Ripple {
runningAnimations.clear();
}
+ private void removeSelf() {
+ // The owner will invalidate itself.
+ mOwner.removeRipple(this);
+ }
+
private void invalidateSelf() {
mOwner.invalidateSelf();
}
@@ -505,12 +565,7 @@ class Ripple {
private final AnimatorListenerAdapter mAnimationListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- mFinished = true;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mFinished = true;
+ removeSelf();
}
};
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 1bd7cac..9d7a8b6 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -25,17 +25,14 @@ import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
-import android.graphics.PointF;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.SparseArray;
import com.android.internal.R;
-import com.android.org.bouncycastle.util.Arrays;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -71,10 +68,17 @@ import java.io.IOException;
public class RippleDrawable extends LayerDrawable {
private static final String LOG_TAG = RippleDrawable.class.getSimpleName();
private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
- private static final PorterDuffXfermode DST_ATOP = new PorterDuffXfermode(Mode.DST_ATOP);
private static final PorterDuffXfermode SRC_ATOP = new PorterDuffXfermode(Mode.SRC_ATOP);
private static final PorterDuffXfermode SRC_OVER = new PorterDuffXfermode(Mode.SRC_OVER);
+ /**
+ * Constant for automatically determining the maximum ripple radius.
+ *
+ * @see #setMaxRadius(int)
+ * @hide
+ */
+ public static final int RADIUS_AUTO = -1;
+
/** The maximum number of ripples supported. */
private static final int MAX_RIPPLES = 10;
@@ -91,17 +95,8 @@ public class RippleDrawable extends LayerDrawable {
private final RippleState mState;
- /**
- * Lazily-created map of pending hotspot locations. These may be modified by
- * calls to {@link #setHotspot(float, float)}.
- */
- private SparseArray<PointF> mPendingHotspots;
-
- /**
- * Lazily-created map of active hotspot locations. These may be modified by
- * calls to {@link #setHotspot(float, float)}.
- */
- private SparseArray<Ripple> mActiveHotspots;
+ /** The current hotspot. May be actively animating or pending entry. */
+ private Ripple mHotspot;
/**
* Lazily-created array of actively animating ripples. Inactive ripples are
@@ -122,18 +117,46 @@ public class RippleDrawable extends LayerDrawable {
/** Whether bounds are being overridden. */
private boolean mOverrideBounds;
+ /** Whether the hotspot is currently active (e.g. focused or pressed). */
+ private boolean mActive;
+
RippleDrawable() {
+ this(null, null);
+ }
+
+ /**
+ * Creates a new ripple drawable with the specified content and mask
+ * drawables.
+ *
+ * @param content The content drawable, may be {@code null}
+ * @param mask The mask drawable, may be {@code null}
+ */
+ public RippleDrawable(Drawable content, Drawable mask) {
this(new RippleState(null, null, null), null, null);
+
+ if (content != null) {
+ addLayer(content, null, 0, 0, 0, 0, 0);
+ }
+
+ if (mask != null) {
+ addLayer(content, null, android.R.id.mask, 0, 0, 0, 0);
+ }
+
+ ensurePadding();
}
@Override
public void setAlpha(int alpha) {
-
+ super.setAlpha(alpha);
+
+ // TODO: Should we support this?
}
@Override
public void setColorFilter(ColorFilter cf) {
-
+ super.setColorFilter(cf);
+
+ // TODO: Should we support this?
}
@Override
@@ -146,20 +169,18 @@ public class RippleDrawable extends LayerDrawable {
protected boolean onStateChange(int[] stateSet) {
super.onStateChange(stateSet);
- final boolean pressed = Arrays.contains(stateSet, R.attr.state_pressed);
- if (!pressed) {
- removeHotspot(R.attr.state_pressed);
- } else {
- activateHotspot(R.attr.state_pressed);
- }
-
- final boolean focused = Arrays.contains(stateSet, R.attr.state_focused);
- if (!focused) {
- removeHotspot(R.attr.state_focused);
- } else {
- activateHotspot(R.attr.state_focused);
+ boolean active = false;
+ final int N = stateSet.length;
+ for (int i = 0; i < N; i++) {
+ if (stateSet[i] == R.attr.state_focused
+ || stateSet[i] == R.attr.state_pressed) {
+ active = true;
+ break;
+ }
}
+ setActive(active);
+ // Update the paint color. Only applicable when animated in software.
if (mRipplePaint != null && mState.mTint != null) {
final ColorStateList stateList = mState.mTint;
final int newColor = stateList.getColorForState(stateSet, 0);
@@ -174,12 +195,25 @@ public class RippleDrawable extends LayerDrawable {
return false;
}
+ private void setActive(boolean active) {
+ if (mActive != active) {
+ mActive = active;
+
+ if (active) {
+ activateHotspot();
+ } else {
+ removeHotspot();
+ }
+ }
+ }
+
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
if (!mOverrideBounds) {
mHotspotBounds.set(bounds);
+ onHotspotBoundsChanged();
}
invalidateSelf();
@@ -272,7 +306,7 @@ public class RippleDrawable extends LayerDrawable {
/**
* Initializes the constant state from the values in the typed array.
*/
- private void updateStateFromTypedArray(TypedArray a) {
+ private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
final RippleState state = mState;
// Extract the theme attributes, if any.
@@ -289,6 +323,12 @@ public class RippleDrawable extends LayerDrawable {
}
mState.mPinned = a.getBoolean(R.styleable.RippleDrawable_pinned, mState.mPinned);
+
+ // If we're not waiting on a theme, verify required attributes.
+ if (state.mTouchThemeAttrs == null && mState.mTint == null) {
+ throw new XmlPullParserException(a.getPositionDescription() +
+ ": <ripple> requires a valid tint attribute");
+ }
}
/**
@@ -314,8 +354,13 @@ public class RippleDrawable extends LayerDrawable {
final TypedArray a = t.resolveAttributes(state.mTouchThemeAttrs,
R.styleable.RippleDrawable);
- updateStateFromTypedArray(a);
- a.recycle();
+ try {
+ updateStateFromTypedArray(a);
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException(e);
+ } finally {
+ a.recycle();
+ }
}
@Override
@@ -330,36 +375,15 @@ public class RippleDrawable extends LayerDrawable {
y = mHotspotBounds.exactCenterY();
}
- // TODO: We should only have a single pending/active hotspot.
- final int id = R.attr.state_pressed;
- final int[] stateSet = getState();
- if (!Arrays.contains(stateSet, id)) {
- // The hotspot is not active, so just modify the pending location.
- getOrCreatePendingHotspot(id).set(x, y);
- return;
- }
-
- if (mAnimatingRipplesCount >= MAX_RIPPLES) {
- // This should never happen unless the user is tapping like a maniac
- // or there is a bug that's preventing ripples from being removed.
- Log.d(LOG_TAG, "Max ripple count exceeded", new RuntimeException());
- return;
- }
+ if (mHotspot == null) {
+ mHotspot = new Ripple(this, mHotspotBounds, x, y);
- if (mActiveHotspots == null) {
- mActiveHotspots = new SparseArray<Ripple>();
- mAnimatingRipples = new Ripple[MAX_RIPPLES];
- }
-
- final Ripple ripple = mActiveHotspots.get(id);
- if (ripple != null) {
- // The hotspot is active, but we can't move it because it's probably
- // busy animating the center position.
- return;
+ if (mActive) {
+ activateHotspot();
+ }
+ } else {
+ mHotspot.move(x, y);
}
-
- // The hotspot needs to be made active.
- createActiveHotspot(id, x, y);
}
private boolean circleContains(Rect bounds, float x, float y) {
@@ -374,74 +398,44 @@ public class RippleDrawable extends LayerDrawable {
return pointRadius < boundsRadius;
}
- private PointF getOrCreatePendingHotspot(int id) {
- final PointF p;
- if (mPendingHotspots == null) {
- mPendingHotspots = new SparseArray<>(2);
- p = null;
- } else {
- p = mPendingHotspots.get(id);
- }
-
- if (p == null) {
- final PointF newPoint = new PointF();
- mPendingHotspots.put(id, newPoint);
- return newPoint;
- } else {
- return p;
- }
- }
-
/**
- * Moves a hotspot from pending to active.
+ * Creates an active hotspot at the specified location.
*/
- private void activateHotspot(int id) {
- final SparseArray<PointF> pendingHotspots = mPendingHotspots;
- if (pendingHotspots != null) {
- final int index = pendingHotspots.indexOfKey(id);
- if (index >= 0) {
- final PointF hotspot = pendingHotspots.valueAt(index);
- pendingHotspots.removeAt(index);
- createActiveHotspot(id, hotspot.x, hotspot.y);
- }
+ private void activateHotspot() {
+ if (mAnimatingRipplesCount >= MAX_RIPPLES) {
+ // This should never happen unless the user is tapping like a maniac
+ // or there is a bug that's preventing ripples from being removed.
+ Log.d(LOG_TAG, "Max ripple count exceeded", new RuntimeException());
+ return;
+ }
+
+ if (mHotspot == null) {
+ final float x = mHotspotBounds.exactCenterX();
+ final float y = mHotspotBounds.exactCenterY();
+ mHotspot = new Ripple(this, mHotspotBounds, x, y);
}
- }
- /**
- * Creates an active hotspot at the specified location.
- */
- private void createActiveHotspot(int id, float x, float y) {
final int color = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
- final Ripple newRipple = new Ripple(this, mHotspotBounds, color);
- newRipple.enter(x, y);
+ mHotspot.setup(mState.mMaxRadius, color, mDensity);
+ mHotspot.enter();
if (mAnimatingRipples == null) {
mAnimatingRipples = new Ripple[MAX_RIPPLES];
}
- mAnimatingRipples[mAnimatingRipplesCount++] = newRipple;
-
- if (mActiveHotspots == null) {
- mActiveHotspots = new SparseArray<Ripple>();
- }
- mActiveHotspots.put(id, newRipple);
+ mAnimatingRipples[mAnimatingRipplesCount++] = mHotspot;
}
- private void removeHotspot(int id) {
- if (mActiveHotspots == null) {
- return;
- }
-
- final Ripple ripple = mActiveHotspots.get(id);
- if (ripple != null) {
- ripple.exit();
-
- mActiveHotspots.remove(id);
+ private void removeHotspot() {
+ if (mHotspot != null) {
+ mHotspot.exit();
+ mHotspot = null;
}
}
private void clearHotspots() {
- if (mActiveHotspots != null) {
- mActiveHotspots.clear();
+ if (mHotspot != null) {
+ mHotspot.cancel();
+ mHotspot = null;
}
final int count = mAnimatingRipplesCount;
@@ -459,73 +453,113 @@ public class RippleDrawable extends LayerDrawable {
public void setHotspotBounds(int left, int top, int right, int bottom) {
mOverrideBounds = true;
mHotspotBounds.set(left, top, right, bottom);
+
+ onHotspotBoundsChanged();
+ }
+
+ /**
+ * Notifies all the animating ripples that the hotspot bounds have changed.
+ */
+ private void onHotspotBoundsChanged() {
+ final int count = mAnimatingRipplesCount;
+ final Ripple[] ripples = mAnimatingRipples;
+ for (int i = 0; i < count; i++) {
+ ripples[i].onHotspotBoundsChanged();
+ }
}
@Override
public void draw(Canvas canvas) {
- final int N = mLayerState.mNum;
- final Rect bounds = getBounds();
- final ChildDrawable[] array = mLayerState.mChildren;
- final boolean maskOnly = mState.mMask != null && N == 1;
-
- int restoreToCount = drawRippleLayer(canvas, maskOnly);
-
- if (restoreToCount >= 0) {
- // We have a ripple layer that contains ripples. If we also have an
- // explicit mask drawable, apply it now using DST_IN blending.
- if (mState.mMask != null) {
- canvas.saveLayer(bounds.left, bounds.top, bounds.right,
- bounds.bottom, getMaskingPaint(DST_IN));
- mState.mMask.draw(canvas);
- canvas.restoreToCount(restoreToCount);
- restoreToCount = -1;
+ final Rect bounds = isProjected() ? getDirtyBounds() : getBounds();
+
+ // Draw the content into a layer first.
+ final int contentLayer = drawContentLayer(canvas, bounds, SRC_OVER);
+
+ // Next, draw the ripples into a layer.
+ final int rippleLayer = drawRippleLayer(canvas, bounds, mState.mTintXfermode);
+
+ // If we have ripples, draw the masking layer.
+ if (rippleLayer >= 0) {
+ drawMaskingLayer(canvas, bounds, DST_IN);
+ }
+
+ // Composite the layers if needed.
+ if (contentLayer >= 0) {
+ canvas.restoreToCount(contentLayer);
+ } else if (rippleLayer >= 0) {
+ canvas.restoreToCount(rippleLayer);
+ }
+ }
+
+ /**
+ * Removes a ripple from the animating ripple list.
+ *
+ * @param ripple the ripple to remove
+ */
+ void removeRipple(Ripple ripple) {
+ // Ripple ripple ripple ripple. Ripple ripple.
+ final Ripple[] ripples = mAnimatingRipples;
+ final int count = mAnimatingRipplesCount;
+ final int index = getRippleIndex(ripple);
+ if (index >= 0) {
+ for (int i = index + 1; i < count; i++) {
+ ripples[i - 1] = ripples[i];
}
+ ripples[count - 1] = null;
+ mAnimatingRipplesCount--;
+ invalidateSelf();
+ }
+ }
- // If there's more content, we need an extra masking layer to merge
- // the ripples over the content.
- if (!maskOnly) {
- final PorterDuffXfermode xfermode = mState.getTintXfermodeInverse();
- final int count = canvas.saveLayer(bounds.left, bounds.top,
- bounds.right, bounds.bottom, getMaskingPaint(xfermode));
- if (restoreToCount < 0) {
- restoreToCount = count;
- }
+ private int getRippleIndex(Ripple ripple) {
+ final Ripple[] ripples = mAnimatingRipples;
+ final int count = mAnimatingRipplesCount;
+ for (int i = 0; i < count; i++) {
+ if (ripples[i] == ripple) {
+ return i;
}
}
+ return -1;
+ }
+
+ private int drawContentLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
+ final int count = mLayerState.mNum;
+ if (count == 0 || (mState.mMask != null && count == 1)) {
+ return -1;
+ }
+
+ final Paint maskingPaint = getMaskingPaint(mode);
+ final int restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
+ bounds.right, bounds.bottom, maskingPaint);
// Draw everything except the mask.
- for (int i = 0; i < N; i++) {
+ final ChildDrawable[] array = mLayerState.mChildren;
+ for (int i = 0; i < count; i++) {
if (array[i].mId != R.id.mask) {
array[i].mDrawable.draw(canvas);
}
}
- // Composite the layers if needed.
- if (restoreToCount >= 0) {
- canvas.restoreToCount(restoreToCount);
- }
+ return restoreToCount;
}
- private int drawRippleLayer(Canvas canvas, boolean maskOnly) {
+ private int drawRippleLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
final int count = mAnimatingRipplesCount;
if (count == 0) {
return -1;
}
- final Ripple[] ripples = mAnimatingRipples;
- final boolean projected = isProjected();
- final Rect layerBounds = projected ? getDirtyBounds() : getBounds();
-
// Separate the ripple color and alpha channel. The alpha will be
// applied when we merge the ripples down to the canvas.
- final int rippleColor;
+ final int rippleARGB;
if (mState.mTint != null) {
- rippleColor = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
+ rippleARGB = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
} else {
- rippleColor = Color.TRANSPARENT;
+ rippleARGB = Color.TRANSPARENT;
}
- final int rippleAlpha = Color.alpha(rippleColor);
+ final int rippleAlpha = Color.alpha(rippleARGB);
+ final int rippleColor = rippleARGB | (0xFF << 24);
if (mRipplePaint == null) {
mRipplePaint = new Paint();
mRipplePaint.setAntiAlias(true);
@@ -536,36 +570,20 @@ public class RippleDrawable extends LayerDrawable {
boolean drewRipples = false;
int restoreToCount = -1;
int restoreTranslate = -1;
- int animatingCount = 0;
// Draw ripples and update the animating ripples array.
+ final Ripple[] ripples = mAnimatingRipples;
for (int i = 0; i < count; i++) {
final Ripple ripple = ripples[i];
- // Mark and skip finished ripples.
- if (ripple.isFinished()) {
- ripples[i] = null;
- continue;
- }
-
// If we're masking the ripple layer, make sure we have a layer
// first. This will merge SRC_OVER (directly) onto the canvas.
if (restoreToCount < 0) {
- // If we're projecting or we only have a mask, we want to treat the
- // underlying canvas as our content and merge the ripple layer down
- // using the tint xfermode.
- final PorterDuffXfermode xfermode;
- if (projected || maskOnly) {
- xfermode = mState.getTintXfermode();
- } else {
- xfermode = SRC_OVER;
- }
-
- final Paint layerPaint = getMaskingPaint(xfermode);
- layerPaint.setAlpha(rippleAlpha);
- restoreToCount = canvas.saveLayer(layerBounds.left, layerBounds.top,
- layerBounds.right, layerBounds.bottom, layerPaint);
- layerPaint.setAlpha(255);
+ final Paint maskingPaint = getMaskingPaint(mode);
+ maskingPaint.setAlpha(rippleAlpha);
+ restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
+ bounds.right, bounds.bottom, maskingPaint);
+ maskingPaint.setAlpha(255);
restoreTranslate = canvas.save();
// Translate the canvas to the current hotspot bounds.
@@ -573,13 +591,8 @@ public class RippleDrawable extends LayerDrawable {
}
drewRipples |= ripple.draw(canvas, ripplePaint);
-
- ripples[animatingCount] = ripples[i];
- animatingCount++;
}
- mAnimatingRipplesCount = animatingCount;
-
// Always restore the translation.
if (restoreTranslate >= 0) {
canvas.restoreToCount(restoreTranslate);
@@ -594,6 +607,20 @@ public class RippleDrawable extends LayerDrawable {
return restoreToCount;
}
+ private int drawMaskingLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
+ final Drawable mask = mState.mMask;
+ if (mask == null) {
+ return -1;
+ }
+
+ final int restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
+ bounds.right, bounds.bottom, getMaskingPaint(mode));
+
+ mask.draw(canvas);
+
+ return restoreToCount;
+ }
+
private Paint getMaskingPaint(PorterDuffXfermode xfermode) {
if (mMaskingPaint == null) {
mMaskingPaint = new Paint();
@@ -634,8 +661,8 @@ public class RippleDrawable extends LayerDrawable {
int[] mTouchThemeAttrs;
ColorStateList mTint = null;
PorterDuffXfermode mTintXfermode = SRC_ATOP;
- PorterDuffXfermode mTintXfermodeInverse = DST_ATOP;
Drawable mMask;
+ int mMaxRadius = RADIUS_AUTO;
boolean mPinned = false;
public RippleState(RippleState orig, RippleDrawable owner, Resources res) {
@@ -645,14 +672,12 @@ public class RippleDrawable extends LayerDrawable {
mTouchThemeAttrs = orig.mTouchThemeAttrs;
mTint = orig.mTint;
mTintXfermode = orig.mTintXfermode;
- mTintXfermodeInverse = orig.mTintXfermodeInverse;
+ mMaxRadius = orig.mMaxRadius;
mPinned = orig.mPinned;
}
}
public void setTintMode(Mode mode) {
- final Mode invertedMode = RippleState.invertPorterDuffMode(mode);
- mTintXfermodeInverse = new PorterDuffXfermode(invertedMode);
mTintXfermode = new PorterDuffXfermode(mode);
}
@@ -660,10 +685,6 @@ public class RippleDrawable extends LayerDrawable {
return mTintXfermode;
}
- public PorterDuffXfermode getTintXfermodeInverse() {
- return mTintXfermodeInverse;
- }
-
@Override
public boolean canApplyTheme() {
return mTouchThemeAttrs != null || super.canApplyTheme();
@@ -683,33 +704,36 @@ public class RippleDrawable extends LayerDrawable {
public Drawable newDrawable(Resources res, Theme theme) {
return new RippleDrawable(this, res, theme);
}
+ }
- /**
- * Inverts SRC and DST in PorterDuff blending modes.
- */
- private static Mode invertPorterDuffMode(Mode src) {
- switch (src) {
- case SRC_ATOP:
- return Mode.DST_ATOP;
- case SRC_IN:
- return Mode.DST_IN;
- case SRC_OUT:
- return Mode.DST_OUT;
- case SRC_OVER:
- return Mode.DST_OVER;
- case DST_ATOP:
- return Mode.SRC_ATOP;
- case DST_IN:
- return Mode.SRC_IN;
- case DST_OUT:
- return Mode.SRC_OUT;
- case DST_OVER:
- return Mode.SRC_OVER;
- default:
- // Everything else is agnostic to SRC versus DST.
- return src;
- }
+ /**
+ * Sets the maximum ripple radius in pixels. The default value of
+ * {@link #RADIUS_AUTO} defines the radius as the distance from the center
+ * of the drawable bounds (or hotspot bounds, if specified) to a corner.
+ *
+ * @param maxRadius the maximum ripple radius in pixels or
+ * {@link #RADIUS_AUTO} to automatically determine the maximum
+ * radius based on the bounds
+ * @see #getMaxRadius()
+ * @see #setHotspotBounds(int, int, int, int)
+ * @hide
+ */
+ public void setMaxRadius(int maxRadius) {
+ if (maxRadius != RADIUS_AUTO && maxRadius < 0) {
+ throw new IllegalArgumentException("maxRadius must be RADIUS_AUTO or >= 0");
}
+
+ mState.mMaxRadius = maxRadius;
+ }
+
+ /**
+ * @return the maximum ripple radius in pixels, or {@link #RADIUS_AUTO} if
+ * the radius is determined automatically
+ * @see #setMaxRadius(int)
+ * @hide
+ */
+ public int getMaxRadius() {
+ return mState.mMaxRadius;
}
private RippleDrawable(RippleState state, Resources res, Theme theme) {
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 442f327..e5c8898 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -71,8 +71,6 @@ ifeq ($(USE_OPENGL_RENDERER),true)
$(LOCAL_PATH)/../../include/utils \
external/skia/src/core
- include external/stlport/libstlport.mk
-
LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
@@ -80,16 +78,15 @@ ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_MODULE := libhwui
LOCAL_MODULE_TAGS := optional
+ include external/stlport/libstlport.mk
+
ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT
- LOCAL_SHARED_LIBRARIES += libRS libRScpp libstlport
+ LOCAL_SHARED_LIBRARIES += libRS libRScpp
LOCAL_C_INCLUDES += \
$(intermediates) \
frameworks/rs/cpp \
- frameworks/rs \
- external/stlport/stlport \
- bionic/ \
- bionic/libstdc++/include
+ frameworks/rs
endif
ifndef HWUI_COMPILE_SYMBOLS
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index b80f7e9..eff3011 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -63,7 +63,6 @@ void BaseRenderNodeAnimator::setStartValue(float value) {
void BaseRenderNodeAnimator::setupStartValueIfNecessary(RenderNode* target, TreeInfo& info) {
if (mPlayState == NEEDS_START) {
setStartValue(getValue(target));
- mPlayState = PENDING;
}
}
@@ -154,7 +153,8 @@ RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, float fi
}
void RenderPropertyAnimator::onAttached(RenderNode* target) {
- if (target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
+ if (mPlayState == NEEDS_START
+ && target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
setStartValue((target->stagingProperties().*mPropertyAccess->getter)());
}
(target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 7741617..a0c7c55 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -41,6 +41,7 @@ protected:
class BaseRenderNodeAnimator : public VirtualLightRefBase {
PREVENT_COPY_AND_ASSIGN(BaseRenderNodeAnimator);
public:
+ ANDROID_API void setStartValue(float value);
ANDROID_API void setInterpolator(Interpolator* interpolator);
ANDROID_API void setDuration(nsecs_t durationInMs);
ANDROID_API nsecs_t duration() { return mDuration; }
@@ -64,11 +65,9 @@ protected:
BaseRenderNodeAnimator(float finalValue);
virtual ~BaseRenderNodeAnimator();
- void setStartValue(float value);
virtual float getValue(RenderNode* target) const = 0;
virtual void setValue(RenderNode* target, float value) = 0;
-private:
void callOnFinishedListener(TreeInfo& info);
enum PlayState {
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 285c8c3..97e9bf6 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -22,14 +22,19 @@
namespace android {
namespace uirenderer {
-DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, OpenGLRenderer* renderer)
+static void defaultLayerDestroyer(Layer* layer) {
+ Caches::getInstance().resourceCache.decrementRefcount(layer);
+}
+
+DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, LayerDestroyer destroyer)
: mDisplayList(0)
, mSurfaceTexture(0)
, mTransform(0)
, mNeedsGLContextAttach(false)
, mUpdateTexImage(false)
, mLayer(layer)
- , mCaches(Caches::getInstance()) {
+ , mCaches(Caches::getInstance())
+ , mDestroyer(destroyer) {
mWidth = mLayer->layer.getWidth();
mHeight = mLayer->layer.getHeight();
mBlend = mLayer->isBlend();
@@ -37,14 +42,16 @@ DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, OpenGLRenderer* rendere
mAlpha = mLayer->getAlpha();
mMode = mLayer->getMode();
mDirtyRect.setEmpty();
+
+ if (!mDestroyer) {
+ mDestroyer = defaultLayerDestroyer;
+ }
}
DeferredLayerUpdater::~DeferredLayerUpdater() {
SkSafeUnref(mColorFilter);
setTransform(0);
- if (mLayer) {
- mCaches.resourceCache.decrementRefcount(mLayer);
- }
+ mDestroyer(mLayer);
}
void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index cc62caa..b7cfe80 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -30,13 +30,15 @@
namespace android {
namespace uirenderer {
+typedef void (*LayerDestroyer)(Layer* layer);
+
// Container to hold the properties a layer should be set to at the start
// of a render pass
-class DeferredLayerUpdater {
+class DeferredLayerUpdater : public VirtualLightRefBase {
public:
// Note that DeferredLayerUpdater assumes it is taking ownership of the layer
// and will not call incrementRef on it as a result.
- ANDROID_API DeferredLayerUpdater(Layer* layer, OpenGLRenderer* renderer = 0);
+ ANDROID_API DeferredLayerUpdater(Layer* layer, LayerDestroyer = 0);
ANDROID_API ~DeferredLayerUpdater();
ANDROID_API bool setSize(uint32_t width, uint32_t height) {
@@ -83,12 +85,6 @@ public:
return mLayer;
}
- ANDROID_API Layer* detachBackingLayer() {
- Layer* layer = mLayer;
- mLayer = 0;
- return layer;
- }
-
private:
// Generic properties
uint32_t mWidth;
@@ -111,6 +107,8 @@ private:
Layer* mLayer;
Caches& mCaches;
+ LayerDestroyer mDestroyer;
+
void doUpdateTexImage();
};
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 96c6292..f418c9b 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -80,10 +80,6 @@ void DisplayListData::cleanupResources() {
delete paths.itemAt(i);
}
- for (size_t i = 0; i < matrices.size(); i++) {
- delete matrices.itemAt(i);
- }
-
bitmapResources.clear();
ownedBitmapResources.clear();
patchResources.clear();
@@ -91,7 +87,6 @@ void DisplayListData::cleanupResources() {
paints.clear();
regions.clear();
paths.clear();
- matrices.clear();
layers.clear();
}
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 11e78b0..7b7dc16 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -125,7 +125,6 @@ public:
Vector<const SkPath*> paths;
SortedVector<const SkPath*> sourcePaths;
Vector<const SkRegion*> regions;
- Vector<const SkMatrix*> matrices;
Vector<Layer*> layers;
uint32_t functorCount;
bool hasDrawOps;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index ea3e7a8..9212b9d 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -472,7 +472,7 @@ private:
class SetMatrixOp : public StateOp {
public:
- SetMatrixOp(const SkMatrix* matrix)
+ SetMatrixOp(const SkMatrix& matrix)
: mMatrix(matrix) {}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
@@ -480,22 +480,22 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- if (mMatrix) {
- OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix));
- } else {
+ if (mMatrix.isIdentity()) {
OP_LOGS("SetMatrix (reset)");
+ } else {
+ OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
}
}
virtual const char* name() { return "SetMatrix"; }
private:
- const SkMatrix* mMatrix;
+ const SkMatrix mMatrix;
};
class ConcatMatrixOp : public StateOp {
public:
- ConcatMatrixOp(const SkMatrix* matrix)
+ ConcatMatrixOp(const SkMatrix& matrix)
: mMatrix(matrix) {}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
@@ -503,13 +503,13 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix));
+ OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
}
virtual const char* name() { return "ConcatMatrix"; }
private:
- const SkMatrix* mMatrix;
+ const SkMatrix mMatrix;
};
class ClipOp : public StateOp {
@@ -746,10 +746,10 @@ protected:
class DrawBitmapMatrixOp : public DrawBoundedOp {
public:
- DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint)
+ DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint)
: DrawBoundedOp(paint), mBitmap(bitmap), mMatrix(matrix) {
mLocalBounds.set(0, 0, bitmap->width(), bitmap->height());
- const mat4 transform(*matrix);
+ const mat4 transform(matrix);
transform.mapRect(mLocalBounds);
}
@@ -758,7 +758,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(mMatrix));
+ OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(&mMatrix));
}
virtual const char* name() { return "DrawBitmapMatrix"; }
@@ -770,7 +770,7 @@ public:
private:
const SkBitmap* mBitmap;
- const SkMatrix* mMatrix;
+ const SkMatrix mMatrix;
};
class DrawBitmapRectOp : public DrawBoundedOp {
@@ -788,7 +788,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING,
+ OP_LOG("Draw bitmap %p src=" RECT_STRING ", dst=" RECT_STRING,
mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds));
}
@@ -978,7 +978,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds));
+ OP_LOG("Draw patch " RECT_STRING, RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawPatch"; }
@@ -1060,7 +1060,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds));
+ OP_LOG("Draw Rect " RECT_STRING, RECT_ARGS(mLocalBounds));
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
@@ -1111,7 +1111,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
+ OP_LOG("Draw RoundRect " RECT_STRING ", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
}
virtual const char* name() { return "DrawRoundRect"; }
@@ -1175,7 +1175,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds));
+ OP_LOG("Draw Oval " RECT_STRING, RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawOval"; }
@@ -1195,7 +1195,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d",
+ OP_LOG("Draw Arc " RECT_STRING ", start %f, sweep %f, useCenter %d",
RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter);
}
@@ -1232,7 +1232,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
+ OP_LOG("Draw Path %p in " RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawPath"; }
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 229afdf..0e47c6e 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -141,14 +141,12 @@ void DisplayListRenderer::skew(float sx, float sy) {
StatefulBaseRenderer::skew(sx, sy);
}
-void DisplayListRenderer::setMatrix(const SkMatrix* matrix) {
- matrix = refMatrix(matrix);
+void DisplayListRenderer::setMatrix(const SkMatrix& matrix) {
addStateOp(new (alloc()) SetMatrixOp(matrix));
StatefulBaseRenderer::setMatrix(matrix);
}
-void DisplayListRenderer::concatMatrix(const SkMatrix* matrix) {
- matrix = refMatrix(matrix);
+void DisplayListRenderer::concatMatrix(const SkMatrix& matrix) {
addStateOp(new (alloc()) ConcatMatrixOp(matrix));
StatefulBaseRenderer::concatMatrix(matrix);
}
@@ -203,10 +201,9 @@ status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, float left, flo
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
const SkPaint* paint) {
bitmap = refBitmap(bitmap);
- matrix = refMatrix(matrix);
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawBitmapMatrixOp(bitmap, matrix, paint));
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index f0ae00f..195b00b 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -86,8 +86,8 @@ public:
virtual void scale(float sx, float sy);
virtual void skew(float sx, float sy);
- virtual void setMatrix(const SkMatrix* matrix);
- virtual void concatMatrix(const SkMatrix* matrix);
+ virtual void setMatrix(const SkMatrix& matrix);
+ virtual void concatMatrix(const SkMatrix& matrix);
// Clip
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
@@ -106,7 +106,7 @@ public:
// Bitmap-based
virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top,
const SkPaint* paint);
- virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+ virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
const SkPaint* paint);
virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
@@ -233,17 +233,6 @@ private:
return regionCopy;
}
- inline const SkMatrix* refMatrix(const SkMatrix* matrix) {
- if (matrix) {
- // Copying the matrix is cheap and prevents against the user changing
- // the original matrix before the operation that uses it
- const SkMatrix* copy = new SkMatrix(*matrix);
- mDisplayListData->matrices.add(copy);
- return copy;
- }
- return matrix;
- }
-
inline Layer* refLayer(Layer* layer) {
mDisplayListData->layers.add(layer);
mCaches.resourceCache.incrementRefcount(layer);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 71836dd..cd09f86 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2042,10 +2042,10 @@ status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, float left, float to
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
const SkPaint* paint) {
Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
- const mat4 transform(*matrix);
+ const mat4 transform(matrix);
transform.mapRect(r);
if (quickRejectSetupScissor(r.left, r.top, r.right, r.bottom)) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index fc27947..0f953a5 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -170,7 +170,7 @@ public:
const SkPaint* paint);
status_t drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint);
- virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+ virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
const SkPaint* paint);
virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 5a49f38..d9c06d3 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -346,7 +346,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
float left, top, offset;
uint32_t width, height;
- PathCache::computePathBounds(t->path, t->paint, left, top, offset, width, height);
+ PathCache::computePathBounds(t->path, &t->paint, left, top, offset, width, height);
PathTexture* texture = t->texture;
texture->left = left;
@@ -357,7 +357,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
SkBitmap* bitmap = new SkBitmap();
- drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height);
+ drawPath(t->path, &t->paint, *bitmap, left, top, offset, width, height);
t->setResult(bitmap);
} else {
texture->width = 0;
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 847853a..bcfb367 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -293,7 +293,7 @@ private:
class PathTask: public Task<SkBitmap*> {
public:
PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture):
- path(path), paint(paint), texture(texture) {
+ path(path), paint(*paint), texture(texture) {
}
~PathTask() {
@@ -301,7 +301,8 @@ private:
}
const SkPath* path;
- const SkPaint* paint;
+ //copied, since input paint may not be immutable
+ const SkPaint paint;
PathTexture* texture;
};
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d964efc..baf372a 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -223,9 +223,9 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) {
renderer.translate(properties().getLeft(), properties().getTop());
}
if (properties().getStaticMatrix()) {
- renderer.concatMatrix(properties().getStaticMatrix());
+ renderer.concatMatrix(*properties().getStaticMatrix());
} else if (properties().getAnimationMatrix()) {
- renderer.concatMatrix(properties().getAnimationMatrix());
+ renderer.concatMatrix(*properties().getAnimationMatrix());
}
if (properties().hasTransformMatrix()) {
if (properties().isTransformTranslateOnly()) {
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index 23cab0e..320895c 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -170,8 +170,8 @@ public:
virtual void scale(float sx, float sy) = 0;
virtual void skew(float sx, float sy) = 0;
- virtual void setMatrix(const SkMatrix* matrix) = 0;
- virtual void concatMatrix(const SkMatrix* matrix) = 0;
+ virtual void setMatrix(const SkMatrix& matrix) = 0;
+ virtual void concatMatrix(const SkMatrix& matrix) = 0;
// clip
virtual const Rect& getLocalClipBounds() const = 0;
@@ -193,7 +193,7 @@ public:
// Bitmap-based
virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top,
const SkPaint* paint) = 0;
- virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+ virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
const SkPaint* paint) = 0;
virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
index 90039e9..fae25a6 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -121,20 +121,16 @@ void StatefulBaseRenderer::skew(float sx, float sy) {
mSnapshot->transform->skew(sx, sy);
}
-void StatefulBaseRenderer::setMatrix(const SkMatrix* matrix) {
- if (matrix) {
- mSnapshot->transform->load(*matrix);
- } else {
- mSnapshot->transform->loadIdentity();
- }
+void StatefulBaseRenderer::setMatrix(const SkMatrix& matrix) {
+ mSnapshot->transform->load(matrix);
}
void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) {
mSnapshot->transform->load(matrix);
}
-void StatefulBaseRenderer::concatMatrix(const SkMatrix* matrix) {
- mat4 transform(*matrix);
+void StatefulBaseRenderer::concatMatrix(const SkMatrix& matrix) {
+ mat4 transform(matrix);
mSnapshot->transform->multiply(transform);
}
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
index 057006b..f38c752 100644
--- a/libs/hwui/StatefulBaseRenderer.h
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -75,9 +75,9 @@ public:
virtual void scale(float sx, float sy);
virtual void skew(float sx, float sy);
- virtual void setMatrix(const SkMatrix* matrix);
+ virtual void setMatrix(const SkMatrix& matrix);
void setMatrix(const Matrix4& matrix); // internal only convenience method
- virtual void concatMatrix(const SkMatrix* matrix);
+ virtual void concatMatrix(const SkMatrix& matrix);
void concatMatrix(const Matrix4& matrix); // internal only convenience method
// Clip
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f91e90e..9ebee1d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -427,27 +427,11 @@ void CanvasContext::makeCurrent() {
mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface);
}
-void CanvasContext::prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters,
- TreeInfo& info) {
- LOG_ALWAYS_FATAL_IF(!mCanvas, "Cannot prepareDraw without a canvas!");
- makeCurrent();
-
- processLayerUpdates(layerUpdaters, info);
- if (info.out.hasAnimations) {
- // TODO: Uh... crap?
- }
- prepareTree(info);
-}
-
-void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters,
- TreeInfo& info) {
- for (size_t i = 0; i < layerUpdaters->size(); i++) {
- DeferredLayerUpdater* update = layerUpdaters->itemAt(i);
- bool success = update->apply(info);
- LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
- if (update->backingLayer()->deferredUpdateScheduled) {
- mCanvas->pushLayerUpdate(update->backingLayer());
- }
+void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info) {
+ bool success = layerUpdater->apply(info);
+ LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
+ if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
+ mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
}
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index a04269b..00c5bf0 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -55,7 +55,8 @@ public:
void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
void setOpaque(bool opaque);
void makeCurrent();
- void prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
+ void processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info);
+ void prepareTree(TreeInfo& info);
void draw(Rect* dirty);
void destroyCanvasAndSurface();
@@ -83,9 +84,6 @@ public:
private:
friend class RegisterFrameCallbackTask;
- void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
- void prepareTree(TreeInfo& info);
-
void setSurface(ANativeWindow* window);
void swapBuffers();
void requireSurface();
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 7ea358f..61d67ca 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -21,6 +21,7 @@
#include <utils/Log.h>
#include <utils/Trace.h>
+#include "../DeferredLayerUpdater.h"
#include "../DisplayList.h"
#include "../RenderNode.h"
#include "CanvasContext.h"
@@ -47,17 +48,22 @@ void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) {
mContext = context;
}
-void DrawFrameTask::addLayer(DeferredLayerUpdater* layer) {
- LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to addLayer with!");
+void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
+ LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to pushLayerUpdate with!");
- mLayers.push(layer);
+ for (size_t i = 0; i < mLayers.size(); i++) {
+ if (mLayers[i].get() == layer) {
+ return;
+ }
+ }
+ mLayers.push_back(layer);
}
-void DrawFrameTask::removeLayer(DeferredLayerUpdater* layer) {
+void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) {
for (size_t i = 0; i < mLayers.size(); i++) {
- if (mLayers[i] == layer) {
- mLayers.removeAt(i);
- break;
+ if (mLayers[i].get() == layer) {
+ mLayers.erase(mLayers.begin() + i);
+ return;
}
}
}
@@ -132,7 +138,16 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) {
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse();
initTreeInfo(info);
- mContext->prepareDraw(&mLayers, info);
+
+ for (size_t i = 0; i < mLayers.size(); i++) {
+ mContext->processLayerUpdate(mLayers[i].get(), info);
+ }
+ mLayers.clear();
+ if (info.out.hasAnimations) {
+ // TODO: Uh... crap?
+ }
+ mContext->prepareTree(info);
+
if (info.out.hasAnimations) {
// TODO: dirty calculations, for now just do a full-screen inval
mDirty.setEmpty();
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 30c8880..d4129b6 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,10 +16,11 @@
#ifndef DRAWFRAMETASK_H
#define DRAWFRAMETASK_H
+#include <vector>
+
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/StrongPointer.h>
-#include <utils/Vector.h>
#include "RenderTask.h"
@@ -56,8 +57,8 @@ public:
void setContext(RenderThread* thread, CanvasContext* context);
- void addLayer(DeferredLayerUpdater* layer);
- void removeLayer(DeferredLayerUpdater* layer);
+ void pushLayerUpdate(DeferredLayerUpdater* layer);
+ void removeLayerUpdate(DeferredLayerUpdater* layer);
void setDirty(int left, int top, int right, int bottom);
void setDensity(float density) { mDensity = density; }
@@ -83,13 +84,9 @@ private:
nsecs_t mFrameTimeNanos;
nsecs_t mRecordDurationNanos;
float mDensity;
+ std::vector< sp<DeferredLayerUpdater> > mLayers;
int mSyncResult;
-
- /*********************************************
- * Multi frame data
- *********************************************/
- Vector<DeferredLayerUpdater*> mLayers;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 77c0aa7..0901963 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -229,10 +229,21 @@ void RenderProxy::runWithGlContext(RenderTask* gltask) {
postAndWait(task);
}
+CREATE_BRIDGE1(destroyLayer, Layer* layer) {
+ LayerRenderer::destroyLayer(args->layer);
+ return NULL;
+}
+
+static void enqueueDestroyLayer(Layer* layer) {
+ SETUP_TASK(destroyLayer);
+ args->layer = layer;
+ RenderThread::getInstance().queue(task);
+}
+
CREATE_BRIDGE3(createDisplayListLayer, CanvasContext* context, int width, int height) {
Layer* layer = args->context->createRenderLayer(args->width, args->height);
if (!layer) return 0;
- return new DeferredLayerUpdater(layer);
+ return new DeferredLayerUpdater(layer, enqueueDestroyLayer);
}
DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) {
@@ -242,14 +253,13 @@ DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height)
args->context = mContext;
void* retval = postAndWait(task);
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval);
- mDrawFrameTask.addLayer(layer);
return layer;
}
CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) {
Layer* layer = args->context->createTextureLayer();
if (!layer) return 0;
- return new DeferredLayerUpdater(layer);
+ return new DeferredLayerUpdater(layer, enqueueDestroyLayer);
}
DeferredLayerUpdater* RenderProxy::createTextureLayer() {
@@ -257,15 +267,9 @@ DeferredLayerUpdater* RenderProxy::createTextureLayer() {
args->context = mContext;
void* retval = postAndWait(task);
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval);
- mDrawFrameTask.addLayer(layer);
return layer;
}
-CREATE_BRIDGE1(destroyLayer, Layer* layer) {
- LayerRenderer::destroyLayer(args->layer);
- return NULL;
-}
-
CREATE_BRIDGE3(copyLayerInto, CanvasContext* context, DeferredLayerUpdater* layer,
SkBitmap* bitmap) {
bool success = args->context->copyLayerInto(args->layer, args->bitmap);
@@ -280,11 +284,12 @@ bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
return (bool) postAndWait(task);
}
-void RenderProxy::destroyLayer(DeferredLayerUpdater* layer) {
- mDrawFrameTask.removeLayer(layer);
- SETUP_TASK(destroyLayer);
- args->layer = layer->detachBackingLayer();
- post(task);
+void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
+ mDrawFrameTask.pushLayerUpdate(layer);
+}
+
+void RenderProxy::cancelLayerUpdate(DeferredLayerUpdater* layer) {
+ mDrawFrameTask.removeLayerUpdate(layer);
}
CREATE_BRIDGE2(flushCaches, CanvasContext* context, Caches::FlushMode flushMode) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index c8d42ec..944ff9c 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -80,7 +80,8 @@ public:
ANDROID_API DeferredLayerUpdater* createDisplayListLayer(int width, int height);
ANDROID_API DeferredLayerUpdater* createTextureLayer();
ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
- ANDROID_API void destroyLayer(DeferredLayerUpdater* layer);
+ ANDROID_API void pushLayerUpdate(DeferredLayerUpdater* layer);
+ ANDROID_API void cancelLayerUpdate(DeferredLayerUpdater* layer);
ANDROID_API void flushCaches(Caches::FlushMode flushMode);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 47a8ce5..84d4ab6 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -41,6 +41,7 @@ import android.view.KeyEvent;
import java.util.HashMap;
import java.util.ArrayList;
+
/**
* AudioManager provides access to volume and ringer mode control.
* <p>
@@ -61,6 +62,7 @@ public class AudioManager {
private final boolean mUseVolumeKeySounds;
private final Binder mToken = new Binder();
private static String TAG = "AudioManager";
+ AudioPortEventHandler mAudioPortEventHandler;
/**
* Broadcast intent, a hint for applications that audio is about to become
@@ -337,6 +339,12 @@ public class AudioManager {
public static final int FLAG_BLUETOOTH_ABS_VOLUME = 1 << 6;
/**
+ * Adjusting the volume was prevented due to silent mode, display a hint in the UI.
+ * @hide
+ */
+ public static final int FLAG_SHOW_SILENT_HINT = 1 << 7;
+
+ /**
* Ringer mode that will be silent and will not vibrate. (This overrides the
* vibrate setting.)
*
@@ -438,6 +446,7 @@ public class AudioManager {
com.android.internal.R.bool.config_useMasterVolume);
mUseVolumeKeySounds = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useVolumeKeySounds);
+ mAudioPortEventHandler = new AudioPortEventHandler(this);
}
private static IAudioService getService()
@@ -635,7 +644,12 @@ public class AudioManager {
if (mUseMasterVolume) {
service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
} else {
- service.adjustVolume(direction, flags, mContext.getOpPackageName());
+ if (USE_SESSIONS) {
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
+ } else {
+ service.adjustVolume(direction, flags, mContext.getOpPackageName());
+ }
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustVolume", e);
@@ -665,8 +679,13 @@ public class AudioManager {
if (mUseMasterVolume) {
service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
} else {
- service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags,
- mContext.getOpPackageName());
+ if (USE_SESSIONS) {
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
+ } else {
+ service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags,
+ mContext.getOpPackageName());
+ }
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustSuggestedStreamVolume", e);
@@ -3007,7 +3026,7 @@ public class AudioManager {
* @hide
*/
public int listAudioPorts(ArrayList<AudioPort> ports) {
- return ERROR_INVALID_OPERATION;
+ return updateAudioPortCache(ports, null);
}
/**
@@ -3016,7 +3035,17 @@ public class AudioManager {
* @hide
*/
public int listAudioDevicePorts(ArrayList<AudioPort> devices) {
- return ERROR_INVALID_OPERATION;
+ ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
+ int status = updateAudioPortCache(ports, null);
+ if (status == SUCCESS) {
+ devices.clear();
+ for (int i = 0; i < ports.size(); i++) {
+ if (ports.get(i) instanceof AudioDevicePort) {
+ devices.add(ports.get(i));
+ }
+ }
+ }
+ return status;
}
/**
@@ -3045,7 +3074,7 @@ public class AudioManager {
public int createAudioPatch(AudioPatch[] patch,
AudioPortConfig[] sources,
AudioPortConfig[] sinks) {
- return ERROR_INVALID_OPERATION;
+ return AudioSystem.createAudioPatch(patch, sources, sinks);
}
/**
@@ -3060,7 +3089,7 @@ public class AudioManager {
* @hide
*/
public int releaseAudioPatch(AudioPatch patch) {
- return ERROR_INVALID_OPERATION;
+ return AudioSystem.releaseAudioPatch(patch);
}
/**
@@ -3069,7 +3098,7 @@ public class AudioManager {
* @hide
*/
public int listAudioPatches(ArrayList<AudioPatch> patches) {
- return ERROR_INVALID_OPERATION;
+ return updateAudioPortCache(null, patches);
}
/**
@@ -3078,7 +3107,14 @@ public class AudioManager {
* @hide
*/
public int setAudioPortGain(AudioPort port, AudioGainConfig gain) {
- return ERROR_INVALID_OPERATION;
+ if (port == null || gain == null) {
+ return ERROR_BAD_VALUE;
+ }
+ AudioPortConfig activeConfig = port.activeConfig();
+ AudioPortConfig config = new AudioPortConfig(port, activeConfig.samplingRate(),
+ activeConfig.channelMask(), activeConfig.format(), gain);
+ config.mConfigMask = AudioPortConfig.GAIN;
+ return AudioSystem.setAudioPortConfig(config);
}
/**
@@ -3106,16 +3142,128 @@ public class AudioManager {
}
/**
- * Register an audio port update listener.
+ * Register an audio port list update listener.
* @hide
*/
public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+ mAudioPortEventHandler.registerListener(l);
}
/**
- * Unregister an audio port update listener.
+ * Unregister an audio port list update listener.
* @hide
*/
public void unregisterAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+ mAudioPortEventHandler.unregisterListener(l);
+ }
+
+ //
+ // AudioPort implementation
+ //
+
+ static final int AUDIOPORT_GENERATION_INIT = 0;
+ Integer mAudioPortGeneration = new Integer(AUDIOPORT_GENERATION_INIT);
+ ArrayList<AudioPort> mAudioPortsCached = new ArrayList<AudioPort>();
+ ArrayList<AudioPatch> mAudioPatchesCached = new ArrayList<AudioPatch>();
+
+ int resetAudioPortGeneration() {
+ int generation;
+ synchronized (mAudioPortGeneration) {
+ generation = mAudioPortGeneration;
+ mAudioPortGeneration = AUDIOPORT_GENERATION_INIT;
+ }
+ return generation;
+ }
+
+ int updateAudioPortCache(ArrayList<AudioPort> ports, ArrayList<AudioPatch> patches) {
+ synchronized (mAudioPortGeneration) {
+
+ if (mAudioPortGeneration == AUDIOPORT_GENERATION_INIT) {
+ int[] patchGeneration = new int[1];
+ int[] portGeneration = new int[1];
+ int status;
+ ArrayList<AudioPort> newPorts = new ArrayList<AudioPort>();
+ ArrayList<AudioPatch> newPatches = new ArrayList<AudioPatch>();
+
+ do {
+ newPorts.clear();
+ status = AudioSystem.listAudioPorts(newPorts, portGeneration);
+ Log.i(TAG, "updateAudioPortCache AudioSystem.listAudioPorts() status: "+
+ status+" num ports: "+ newPorts.size() +" portGeneration: "+portGeneration[0]);
+ if (status != SUCCESS) {
+ return status;
+ }
+ newPatches.clear();
+ status = AudioSystem.listAudioPatches(newPatches, patchGeneration);
+ Log.i(TAG, "updateAudioPortCache AudioSystem.listAudioPatches() status: "+
+ status+" num patches: "+ newPatches.size() +" patchGeneration: "+patchGeneration[0]);
+ if (status != SUCCESS) {
+ return status;
+ }
+ } while (patchGeneration[0] != portGeneration[0]);
+
+ for (int i = 0; i < newPatches.size(); i++) {
+ for (int j = 0; j < newPatches.get(i).sources().length; j++) {
+ AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j], newPorts);
+ if (portCfg == null) {
+ return ERROR;
+ }
+ newPatches.get(i).sources()[j] = portCfg;
+ }
+ for (int j = 0; j < newPatches.get(i).sinks().length; j++) {
+ AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j], newPorts);
+ if (portCfg == null) {
+ return ERROR;
+ }
+ newPatches.get(i).sinks()[j] = portCfg;
+ }
+ }
+
+ mAudioPortsCached = newPorts;
+ mAudioPatchesCached = newPatches;
+ mAudioPortGeneration = portGeneration[0];
+ }
+ if (ports != null) {
+ ports.clear();
+ ports.addAll(mAudioPortsCached);
+ }
+ if (patches != null) {
+ patches.clear();
+ patches.addAll(mAudioPatchesCached);
+ }
+ }
+ return SUCCESS;
+ }
+
+ AudioPortConfig updatePortConfig(AudioPortConfig portCfg, ArrayList<AudioPort> ports) {
+ AudioPort port = portCfg.port();
+ int k;
+ for (k = 0; k < ports.size(); k++) {
+ // compare handles because the port returned by JNI is not of the correct
+ // subclass
+ if (ports.get(k).handle().equals(port.handle())) {
+ Log.i(TAG, "updatePortConfig match found for port handle: "+
+ port.handle().id()+" port: "+ k);
+ port = ports.get(k);
+ break;
+ }
+ }
+ if (k == ports.size()) {
+ // this hould never happen
+ Log.e(TAG, "updatePortConfig port not found for handle: "+port.handle().id());
+ return null;
+ }
+ AudioGainConfig gainCfg = portCfg.gain();
+ if (gainCfg != null) {
+ AudioGain gain = port.gain(gainCfg.index());
+ gainCfg = gain.buildConfig(gainCfg.mode(),
+ gainCfg.channelMask(),
+ gainCfg.values(),
+ gainCfg.rampDurationMs());
+ }
+ return port.buildConfig(portCfg.samplingRate(),
+ portCfg.channelMask(),
+ portCfg.format(),
+ gainCfg);
}
}
diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java
index 9aeddef..fbd5022 100644
--- a/media/java/android/media/AudioPort.java
+++ b/media/java/android/media/AudioPort.java
@@ -133,6 +133,9 @@ public class AudioPort {
* Get the gain descriptor at a given index
*/
AudioGain gain(int index) {
+ if (index < mGains.length) {
+ return null;
+ }
return mGains[index];
}
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
new file mode 100644
index 0000000..782ecd8
--- /dev/null
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+package android.media;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.lang.ref.WeakReference;
+
+/**
+ * The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks
+ * posted from JNI
+ * @hide
+ */
+
+class AudioPortEventHandler {
+ private final Handler mHandler;
+ private ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners;
+ private AudioManager mAudioManager;
+
+ private static String TAG = "AudioPortEventHandler";
+
+ private static final int AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1;
+ private static final int AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2;
+ private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3;
+ private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4;
+
+ AudioPortEventHandler(AudioManager audioManager) {
+ mAudioManager = audioManager;
+ mListeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
+
+ // find the looper for our new event handler
+ Looper looper = Looper.myLooper();
+ if (looper == null) {
+ looper = Looper.getMainLooper();
+ }
+
+ if (looper != null) {
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ Log.i(TAG, "handleMessage: "+msg.what);
+ ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
+ synchronized (this) {
+ if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
+ listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
+ if (mListeners.contains(msg.obj)) {
+ listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj);
+ }
+ } else {
+ listeners = mListeners;
+ }
+ }
+ if (listeners.isEmpty()) {
+ return;
+ }
+ // reset audio port cache if the event corresponds to a change coming
+ // from audio policy service or if mediaserver process died.
+ if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED ||
+ msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED ||
+ msg.what == AUDIOPORT_EVENT_SERVICE_DIED) {
+ mAudioManager.resetAudioPortGeneration();
+ }
+ ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
+ ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
+ if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
+ int status = mAudioManager.updateAudioPortCache(ports, patches);
+ if (status != AudioManager.SUCCESS) {
+ return;
+ }
+ }
+
+ switch (msg.what) {
+ case AUDIOPORT_EVENT_NEW_LISTENER:
+ case AUDIOPORT_EVENT_PORT_LIST_UPDATED:
+ AudioPort[] portList = ports.toArray(new AudioPort[0]);
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).OnAudioPortListUpdate(portList);
+ }
+ if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) {
+ break;
+ }
+ // FALL THROUGH
+
+ case AUDIOPORT_EVENT_PATCH_LIST_UPDATED:
+ AudioPatch[] patchList = patches.toArray(new AudioPatch[0]);
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).OnAudioPatchListUpdate(patchList);
+ }
+ break;
+
+ case AUDIOPORT_EVENT_SERVICE_DIED:
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).OnServiceDied();
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ };
+ } else {
+ mHandler = null;
+ }
+
+ native_setup(new WeakReference<AudioPortEventHandler>(this));
+ }
+ private native void native_setup(Object module_this);
+
+ @Override
+ protected void finalize() {
+ native_finalize();
+ }
+ private native void native_finalize();
+
+ void registerListener(AudioManager.OnAudioPortUpdateListener l) {
+ synchronized (this) {
+ mListeners.add(l);
+ }
+ if (mHandler != null) {
+ Message m = mHandler.obtainMessage(AUDIOPORT_EVENT_NEW_LISTENER, 0, 0, l);
+ mHandler.sendMessage(m);
+ }
+ }
+
+ void unregisterListener(AudioManager.OnAudioPortUpdateListener l) {
+ synchronized (this) {
+ mListeners.remove(l);
+ }
+ }
+
+ Handler handler() {
+ return mHandler;
+ }
+
+ @SuppressWarnings("unused")
+ private static void postEventFromNative(Object module_ref,
+ int what, int arg1, int arg2, Object obj) {
+ AudioPortEventHandler eventHandler =
+ (AudioPortEventHandler)((WeakReference)module_ref).get();
+ if (eventHandler == null) {
+ return;
+ }
+
+ if (eventHandler != null) {
+ Handler handler = eventHandler.handler();
+ if (handler != null) {
+ Message m = handler.obtainMessage(what, arg1, arg2, obj);
+ handler.sendMessage(m);
+ }
+ }
+ }
+
+}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 98a7c65..74f39b7 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -115,6 +115,9 @@ public class AudioService extends IAudioService.Stub {
/** Allow volume changes to set ringer mode to silent? */
private static final boolean VOLUME_SETS_RINGER_MODE_SILENT = false;
+ /** In silent mode, are volume adjustments (raises) prevented? */
+ private static final boolean PREVENT_VOLUME_ADJUSTMENT_IF_SILENT = true;
+
/** How long to delay before persisting a change in volume/ringer mode. */
private static final int PERSIST_DELAY = 500;
@@ -126,6 +129,11 @@ public class AudioService extends IAudioService.Stub {
*/
public static final int PLAY_SOUND_DELAY = 300;
+ /**
+ * Only used in the result from {@link #checkForRingerModeChange(int, int, int)}
+ */
+ private static final int FLAG_ADJUST_VOLUME = 1;
+
private final Context mContext;
private final ContentResolver mContentResolver;
private final AppOpsManager mAppOps;
@@ -942,7 +950,12 @@ public class AudioService extends IAudioService.Stub {
}
// Check if the ringer mode changes with this volume adjustment. If
// it does, it will handle adjusting the volume, so we won't below
- adjustVolume = checkForRingerModeChange(aliasIndex, direction, step);
+ final int result = checkForRingerModeChange(aliasIndex, direction, step);
+ adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
+ // If suppressing a volume adjustment in silent mode, display the UI hint
+ if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
+ flags |= AudioManager.FLAG_SHOW_SILENT_HINT;
+ }
}
int oldIndex = mStreamStates[streamType].getIndex(device);
@@ -2564,8 +2577,8 @@ public class AudioService extends IAudioService.Stub {
* adjusting volume. If so, this will set the proper ringer mode and volume
* indices on the stream states.
*/
- private boolean checkForRingerModeChange(int oldIndex, int direction, int step) {
- boolean adjustVolumeIndex = true;
+ private int checkForRingerModeChange(int oldIndex, int direction, int step) {
+ int result = FLAG_ADJUST_VOLUME;
int ringerMode = getRingerMode();
switch (ringerMode) {
@@ -2604,17 +2617,21 @@ public class AudioService extends IAudioService.Stub {
} else if (direction == AudioManager.ADJUST_RAISE) {
ringerMode = RINGER_MODE_NORMAL;
}
- adjustVolumeIndex = false;
+ result &= ~FLAG_ADJUST_VOLUME;
break;
case RINGER_MODE_SILENT:
if (direction == AudioManager.ADJUST_RAISE) {
- if (mHasVibrator) {
- ringerMode = RINGER_MODE_VIBRATE;
+ if (PREVENT_VOLUME_ADJUSTMENT_IF_SILENT) {
+ result |= AudioManager.FLAG_SHOW_SILENT_HINT;
} else {
- ringerMode = RINGER_MODE_NORMAL;
+ if (mHasVibrator) {
+ ringerMode = RINGER_MODE_VIBRATE;
+ } else {
+ ringerMode = RINGER_MODE_NORMAL;
+ }
}
}
- adjustVolumeIndex = false;
+ result &= ~FLAG_ADJUST_VOLUME;
break;
default:
Log.e(TAG, "checkForRingerModeChange() wrong ringer mode: "+ringerMode);
@@ -2625,7 +2642,7 @@ public class AudioService extends IAudioService.Stub {
mPrevVolDirection = direction;
- return adjustVolumeIndex;
+ return result;
}
@Override
@@ -4141,8 +4158,9 @@ public class AudioService extends IAudioService.Stub {
(device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
setBluetoothA2dpOnInt(true);
}
- boolean isUsb = ((device & AudioSystem.DEVICE_OUT_ALL_USB) != 0) ||
- ((device & AudioSystem.DEVICE_IN_ALL_USB) != 0);
+ boolean isUsb = ((device & ~AudioSystem.DEVICE_OUT_ALL_USB) == 0) ||
+ (((device & AudioSystem.DEVICE_BIT_IN) != 0) &&
+ ((device & ~AudioSystem.DEVICE_IN_ALL_USB) == 0));
handleDeviceConnection((state == 1), device, (isUsb ? name : ""));
if (state != 0) {
if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
@@ -4159,7 +4177,7 @@ public class AudioService extends IAudioService.Stub {
MUSIC_ACTIVE_POLL_PERIOD_MS);
}
}
- if (!isUsb) {
+ if (!isUsb && (device != AudioSystem.DEVICE_IN_WIRED_HEADSET)) {
sendDeviceConnectionIntent(device, state, name);
}
}
@@ -4175,7 +4193,8 @@ public class AudioService extends IAudioService.Stub {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- int device;
+ int outDevice;
+ int inDevice;
int state;
if (action.equals(Intent.ACTION_DOCK_EVENT)) {
@@ -4210,7 +4229,8 @@ public class AudioService extends IAudioService.Stub {
} else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
BluetoothProfile.STATE_DISCONNECTED);
- device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
+ outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
+ inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
String address = null;
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
@@ -4224,10 +4244,10 @@ public class AudioService extends IAudioService.Stub {
switch (btClass.getDeviceClass()) {
case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
- device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+ outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
break;
case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
- device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+ outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
break;
}
}
@@ -4237,7 +4257,9 @@ public class AudioService extends IAudioService.Stub {
}
boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
- if (handleDeviceConnection(connected, device, address)) {
+ boolean success = handleDeviceConnection(connected, outDevice, address) &&
+ handleDeviceConnection(connected, inDevice, address);
+ if (success) {
synchronized (mScoClients) {
if (connected) {
mBluetoothHeadsetDevice = btDevice;
@@ -4257,8 +4279,8 @@ public class AudioService extends IAudioService.Stub {
: "card=" + alsaCard + ";device=" + alsaDevice);
// Playback Device
- device = AudioSystem.DEVICE_OUT_USB_ACCESSORY;
- setWiredDeviceConnectionState(device, state, params);
+ outDevice = AudioSystem.DEVICE_OUT_USB_ACCESSORY;
+ setWiredDeviceConnectionState(outDevice, state, params);
} else if (action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
state = intent.getIntExtra("state", 0);
@@ -4273,14 +4295,14 @@ public class AudioService extends IAudioService.Stub {
// Playback Device
if (hasPlayback) {
- device = AudioSystem.DEVICE_OUT_USB_DEVICE;
- setWiredDeviceConnectionState(device, state, params);
+ outDevice = AudioSystem.DEVICE_OUT_USB_DEVICE;
+ setWiredDeviceConnectionState(outDevice, state, params);
}
// Capture Device
if (hasCapture) {
- device = AudioSystem.DEVICE_IN_USB_DEVICE;
- setWiredDeviceConnectionState(device, state, params);
+ inDevice = AudioSystem.DEVICE_IN_USB_DEVICE;
+ setWiredDeviceConnectionState(inDevice, state, params);
}
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
boolean broadcast = false;
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 5abf4b6..c8d64ce 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -16,6 +16,7 @@
package android.media;
+import java.util.ArrayList;
/* IF YOU CHANGE ANY OF THE CONSTANTS IN THIS FILE, DO NOT FORGET
* TO UPDATE THE CORRESPONDING NATIVE GLUE AND AudioManager.java.
@@ -459,4 +460,12 @@ public class AudioSystem
public static native int setLowRamDevice(boolean isLowRamDevice);
public static native int checkAudioFlinger();
+
+ public static native int listAudioPorts(ArrayList<AudioPort> ports, int[] generation);
+ public static native int createAudioPatch(AudioPatch[] patch,
+ AudioPortConfig[] sources, AudioPortConfig[] sinks);
+ public static native int releaseAudioPatch(AudioPatch patch);
+ public static native int listAudioPatches(ArrayList<AudioPatch> patches, int[] generation);
+ public static native int setAudioPortConfig(AudioPortConfig config);
}
+
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index c7b3fc9..f258063 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -744,12 +744,40 @@ final public class MediaCodec {
setParameters(keys, values);
}
+ /**
+ * Sets the codec listener for actionable MediaCodec events.
+ * <p>Call this method with a null listener to stop receiving event notifications.
+ *
+ * @param cb The listener that will run.
+ *
+ * @hide
+ */
public void setNotificationCallback(NotificationCallback cb) {
mNotificationCallback = cb;
}
- public interface NotificationCallback {
- void onCodecNotify(MediaCodec codec);
+ /**
+ * MediaCodec listener interface. Used to notify the user of MediaCodec
+ * when there are available input and/or output buffers, a change in
+ * configuration or when a codec error happened.
+ *
+ * @hide
+ */
+ public static abstract class NotificationCallback {
+ /**
+ * Called on the listener to notify that there is an actionable
+ * MediaCodec event. The application should call {@link #dequeueOutputBuffer}
+ * to receive the configuration change event, codec error or an
+ * available output buffer. It should also call {@link #dequeueInputBuffer}
+ * to receive any available input buffer. For best performance, it
+ * is recommended to exhaust both available input and output buffers in
+ * the handling of a single callback, by calling the dequeue methods
+ * repeatedly with a zero timeout until {@link #INFO_TRY_AGAIN_LATER} is returned.
+ *
+ * @param codec the MediaCodec instance that has an actionable event.
+ *
+ */
+ public abstract void onCodecNotify(MediaCodec codec);
}
private void postEventFromNative(
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index ff73a10..5dc8e1b 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -23,6 +23,8 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
+import java.util.Set;
+
/**
* Contains metadata about an item, such as the title, artist, etc.
*/
@@ -301,6 +303,15 @@ public final class MediaMetadata implements Parcelable {
}
/**
+ * Returns a Set containing the Strings used as keys in this metadata.
+ *
+ * @return a Set of String keys
+ */
+ public Set<String> keySet() {
+ return mBundle.keySet();
+ }
+
+ /**
* Helper for getting the String key used by {@link MediaMetadata} from the
* integer key that {@link MediaMetadataEditor} uses.
*
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 26ae3cc..0caea5f 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -27,7 +27,6 @@ import android.graphics.RectF;
import android.media.session.MediaSessionLegacyHelper;
import android.media.session.PlaybackState;
import android.media.session.MediaSession;
-import android.media.session.TransportPerformer;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -584,7 +583,7 @@ public class RemoteControlClient
// USE_SESSIONS
if (mSession != null && mMetadataBuilder != null) {
- mSession.getTransportPerformer().setMetadata(mMetadataBuilder.build());
+ mSession.setMetadata(mMetadataBuilder.build());
}
mApplied = true;
}
@@ -702,7 +701,7 @@ public class RemoteControlClient
mSessionPlaybackState.setState(pbState, hasPosition ?
mPlaybackPositionMs : PlaybackState.PLAYBACK_POSITION_UNKNOWN,
playbackSpeed);
- mSession.getTransportPerformer().setPlaybackState(mSessionPlaybackState);
+ mSession.setPlaybackState(mSessionPlaybackState);
}
}
}
@@ -789,7 +788,7 @@ public class RemoteControlClient
if (mSession != null) {
mSessionPlaybackState.setActions(PlaybackState
.getActionsFromRccControlFlags(transportControlFlags));
- mSession.getTransportPerformer().setPlaybackState(mSessionPlaybackState);
+ mSession.setPlaybackState(mSessionPlaybackState);
}
}
}
@@ -1317,7 +1316,8 @@ public class RemoteControlClient
}
// USE_SESSIONS
- private TransportPerformer.Listener mTransportListener = new TransportPerformer.Listener() {
+ private MediaSession.TransportControlsCallback mTransportListener
+ = new MediaSession.TransportControlsCallback() {
@Override
public void onSeekTo(long pos) {
@@ -1325,7 +1325,7 @@ public class RemoteControlClient
}
@Override
- public void onRate(Rating rating) {
+ public void onSetRating(Rating rating) {
if ((mTransportControlFlags & FLAG_KEY_MEDIA_RATING) != 0) {
if (mEventHandler != null) {
mEventHandler.sendMessage(mEventHandler.obtainMessage(
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index c4233c3..1cfc5bc 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -47,4 +47,8 @@ interface ISession {
void setMetadata(in MediaMetadata metadata);
void setPlaybackState(in PlaybackState state);
void setRatingType(int type);
+
+ // These commands relate to volume handling
+ void configureVolumeHandling(int type, int arg1, int arg2);
+ void setCurrentVolume(int currentVolume);
} \ No newline at end of file
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 103c3f1..0316d1f 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -45,4 +45,8 @@ oneway interface ISessionCallback {
void onRewind();
void onSeekTo(long pos);
void onRate(in Rating rating);
+
+ // These callbacks are for volume handling
+ void onAdjustVolumeBy(int delta);
+ void onSetVolumeTo(int value);
} \ No newline at end of file
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 5ddb6db..9ce0692 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -30,7 +30,7 @@ import android.view.KeyEvent;
*/
interface ISessionController {
void sendCommand(String command, in Bundle extras, in ResultReceiver cb);
- void sendMediaButton(in KeyEvent mediaButton);
+ boolean sendMediaButton(in KeyEvent mediaButton);
void registerCallbackListener(in ISessionControllerCallback cb);
void unregisterCallbackListener(in ISessionControllerCallback cb);
boolean isTransportControlEnabled();
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 38b9293..6d9888f 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -29,4 +29,5 @@ interface ISessionManager {
ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
List<IBinder> getSessions(in ComponentName compName, int userId);
void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock);
+ void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags);
} \ No newline at end of file
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 642ac2f..caff1ad 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -17,6 +17,7 @@
package android.media.session;
import android.media.MediaMetadata;
+import android.media.Rating;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -45,8 +46,8 @@ public final class MediaController {
private static final String TAG = "SessionController";
private static final int MSG_EVENT = 1;
- private static final int MESSAGE_PLAYBACK_STATE = 2;
- private static final int MESSAGE_METADATA = 3;
+ private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
+ private static final int MSG_UPDATE_METADATA = 3;
private static final int MSG_ROUTE = 4;
private final ISessionController mSessionBinder;
@@ -57,10 +58,11 @@ public final class MediaController {
private boolean mCbRegistered = false;
- private TransportController mTransportController;
+ private TransportControls mTransportController;
private MediaController(ISessionController sessionBinder) {
mSessionBinder = sessionBinder;
+ mTransportController = new TransportControls();
}
/**
@@ -70,9 +72,6 @@ public final class MediaController {
MediaController controller = new MediaController(sessionBinder);
try {
controller.mSessionBinder.registerCallbackListener(controller.mCbStub);
- if (controller.mSessionBinder.isTransportControlEnabled()) {
- controller.mTransportController = new TransportController(sessionBinder);
- }
} catch (RemoteException e) {
Log.wtf(TAG, "MediaController created with expired token", e);
controller = null;
@@ -93,33 +92,84 @@ public final class MediaController {
}
/**
- * Get a TransportController if the session supports it. If it is not
- * supported null will be returned.
+ * Get a {@link TransportControls} instance for this session.
*
- * @return A TransportController or null
+ * @return A controls instance
*/
- public TransportController getTransportController() {
+ public TransportControls getTransportControls() {
return mTransportController;
}
/**
- * Send the specified media button to the session. Only media keys can be
- * sent using this method.
+ * Send the specified media button event to the session. Only media keys can
+ * be sent by this method, other keys will be ignored.
+ *
+ * @param keyEvent The media button event to dispatch.
+ * @return true if the event was sent to the session, false otherwise.
+ */
+ public boolean dispatchMediaButtonEvent(KeyEvent keyEvent) {
+ if (keyEvent == null) {
+ throw new IllegalArgumentException("KeyEvent may not be null");
+ }
+ if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+ return false;
+ }
+ try {
+ return mSessionBinder.sendMediaButton(keyEvent);
+ } catch (RemoteException e) {
+ // System is dead. =(
+ }
+ return false;
+ }
+
+ /**
+ * Get the current playback state for this session.
+ *
+ * @return The current PlaybackState or null
+ */
+ public PlaybackState getPlaybackState() {
+ try {
+ return mSessionBinder.getPlaybackState();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getPlaybackState.", e);
+ return null;
+ }
+ }
+
+ /**
+ * Get the current metadata for this session.
*
- * @param keycode The media button keycode, such as
- * {@link KeyEvent#KEYCODE_MEDIA_PLAY}.
+ * @return The current MediaMetadata or null.
*/
- public void sendMediaButton(int keycode) {
- if (!KeyEvent.isMediaKey(keycode)) {
- throw new IllegalArgumentException("May only send media buttons through "
- + "sendMediaButton");
+ public MediaMetadata getMetadata() {
+ try {
+ return mSessionBinder.getMetadata();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getMetadata.", e);
+ return null;
}
- // TODO do something better than key down/up events
- KeyEvent event = new KeyEvent(KeyEvent.ACTION_UP, keycode);
+ }
+
+ /**
+ * Get the rating type supported by the session. One of:
+ * <ul>
+ * <li>{@link Rating#RATING_NONE}</li>
+ * <li>{@link Rating#RATING_HEART}</li>
+ * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
+ * <li>{@link Rating#RATING_3_STARS}</li>
+ * <li>{@link Rating#RATING_4_STARS}</li>
+ * <li>{@link Rating#RATING_5_STARS}</li>
+ * <li>{@link Rating#RATING_PERCENTAGE}</li>
+ * </ul>
+ *
+ * @return The supported rating type
+ */
+ public int getRatingType() {
try {
- mSessionBinder.sendMediaButton(event);
+ return mSessionBinder.getRatingType();
} catch (RemoteException e) {
- Log.d(TAG, "Dead object in sendMediaButton", e);
+ Log.wtf(TAG, "Error calling getRatingType.", e);
+ return Rating.RATING_NONE;
}
}
@@ -171,7 +221,7 @@ public final class MediaController {
* @param params Any parameters to include with the command
* @param cb The callback to receive the result on
*/
- public void sendCommand(String command, Bundle params, ResultReceiver cb) {
+ public void sendControlCommand(String command, Bundle params, ResultReceiver cb) {
if (TextUtils.isEmpty(command)) {
throw new IllegalArgumentException("command cannot be null or empty");
}
@@ -254,18 +304,10 @@ public final class MediaController {
return null;
}
- private void postEvent(String event, Bundle extras) {
- synchronized (mLock) {
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).post(MSG_EVENT, event, extras);
- }
- }
- }
-
- private void postRouteChanged(RouteInfo route) {
+ private final void postMessage(int what, Object obj, Bundle data) {
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).post(MSG_ROUTE, route, null);
+ mCallbacks.get(i).post(what, obj, data);
}
}
}
@@ -282,7 +324,7 @@ public final class MediaController {
*
* @param event
*/
- public void onEvent(String event, Bundle extras) {
+ public void onSessionEvent(String event, Bundle extras) {
}
/**
@@ -293,6 +335,143 @@ public final class MediaController {
*/
public void onRouteChanged(RouteInfo route) {
}
+
+ /**
+ * Override to handle changes in playback state.
+ *
+ * @param state The new playback state of the session
+ */
+ public void onPlaybackStateChanged(PlaybackState state) {
+ }
+
+ /**
+ * Override to handle changes to the current metadata.
+ *
+ * @see MediaMetadata
+ * @param metadata The current metadata for the session or null
+ */
+ public void onMetadataChanged(MediaMetadata metadata) {
+ }
+ }
+
+ /**
+ * Interface for controlling media playback on a session. This allows an app
+ * to send media transport commands to the session.
+ */
+ public final class TransportControls {
+ private static final String TAG = "TransportController";
+
+ private TransportControls() {
+ }
+
+ /**
+ * Request that the player start its playback at its current position.
+ */
+ public void play() {
+ try {
+ mSessionBinder.play();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling play.", e);
+ }
+ }
+
+ /**
+ * Request that the player pause its playback and stay at its current
+ * position.
+ */
+ public void pause() {
+ try {
+ mSessionBinder.pause();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling pause.", e);
+ }
+ }
+
+ /**
+ * Request that the player stop its playback; it may clear its state in
+ * whatever way is appropriate.
+ */
+ public void stop() {
+ try {
+ mSessionBinder.stop();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling stop.", e);
+ }
+ }
+
+ /**
+ * Move to a new location in the media stream.
+ *
+ * @param pos Position to move to, in milliseconds.
+ */
+ public void seekTo(long pos) {
+ try {
+ mSessionBinder.seekTo(pos);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling seekTo.", e);
+ }
+ }
+
+ /**
+ * Start fast forwarding. If playback is already fast forwarding this
+ * may increase the rate.
+ */
+ public void fastForward() {
+ try {
+ mSessionBinder.fastForward();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling fastForward.", e);
+ }
+ }
+
+ /**
+ * Skip to the next item.
+ */
+ public void skipToNext() {
+ try {
+ mSessionBinder.next();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling next.", e);
+ }
+ }
+
+ /**
+ * Start rewinding. If playback is already rewinding this may increase
+ * the rate.
+ */
+ public void rewind() {
+ try {
+ mSessionBinder.rewind();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling rewind.", e);
+ }
+ }
+
+ /**
+ * Skip to the previous item.
+ */
+ public void skipToPrevious() {
+ try {
+ mSessionBinder.previous();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling previous.", e);
+ }
+ }
+
+ /**
+ * Rate the current content. This will cause the rating to be set for
+ * the current user. The Rating type must match the type returned by
+ * {@link #getRatingType()}.
+ *
+ * @param rating The rating to set for the current content
+ */
+ public void setRating(Rating rating) {
+ try {
+ mSessionBinder.rate(rating);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling rate.", e);
+ }
+ }
}
private final static class CallbackStub extends ISessionControllerCallback.Stub {
@@ -306,7 +485,7 @@ public final class MediaController {
public void onEvent(String event, Bundle extras) {
MediaController controller = mController.get();
if (controller != null) {
- controller.postEvent(event, extras);
+ controller.postMessage(MSG_EVENT, event, extras);
}
}
@@ -314,7 +493,7 @@ public final class MediaController {
public void onRouteChanged(RouteInfo route) {
MediaController controller = mController.get();
if (controller != null) {
- controller.postRouteChanged(route);
+ controller.postMessage(MSG_ROUTE, route, null);
}
}
@@ -322,10 +501,7 @@ public final class MediaController {
public void onPlaybackStateChanged(PlaybackState state) {
MediaController controller = mController.get();
if (controller != null) {
- TransportController tc = controller.getTransportController();
- if (tc != null) {
- tc.postPlaybackStateChanged(state);
- }
+ controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null);
}
}
@@ -333,10 +509,7 @@ public final class MediaController {
public void onMetadataChanged(MediaMetadata metadata) {
MediaController controller = mController.get();
if (controller != null) {
- TransportController tc = controller.getTransportController();
- if (tc != null) {
- tc.postMetadataChanged(metadata);
- }
+ controller.postMessage(MSG_UPDATE_METADATA, metadata, null);
}
}
@@ -354,10 +527,17 @@ public final class MediaController {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_EVENT:
- mCallback.onEvent((String) msg.obj, msg.getData());
+ mCallback.onSessionEvent((String) msg.obj, msg.getData());
break;
case MSG_ROUTE:
mCallback.onRouteChanged((RouteInfo) msg.obj);
+ break;
+ case MSG_UPDATE_PLAYBACK_STATE:
+ mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
+ break;
+ case MSG_UPDATE_METADATA:
+ mCallback.onMetadataChanged((MediaMetadata) msg.obj);
+ break;
}
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 539dc3c..7972639 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -16,10 +16,12 @@
package android.media.session;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Intent;
-import android.media.AudioAttributes;
import android.media.AudioManager;
+import android.media.MediaMetadata;
import android.media.Rating;
import android.media.session.ISessionController;
import android.media.session.ISession;
@@ -49,15 +51,13 @@ import java.util.List;
* <p>
* A MediaSession is created by calling
* {@link MediaSessionManager#createSession(String)}. Once a session is created
- * apps that have the MEDIA_CONTENT_CONTROL permission can interact with the
- * session through
- * {@link MediaSessionManager#getActiveSessions(android.content.ComponentName)}.
- * The owner of the session may also use {@link #getSessionToken()} to allow
- * apps without this permission to create a {@link MediaController} to interact
- * with this session.
+ * the owner of the session may use {@link #getSessionToken()} to allow apps to
+ * create a {@link MediaController} to interact with this session.
* <p>
- * To receive commands, media keys, and other events a Callback must be set with
- * {@link #addCallback(Callback)}.
+ * To receive commands, media keys, and other events a {@link Callback} must be
+ * set with {@link #addCallback(Callback)}. To receive transport control
+ * commands a {@link TransportControlsCallback} must be set with
+ * {@link #addTransportControlsCallback}.
* <p>
* When an app is finished performing playback it must call {@link #release()}
* to clean up the session and notify any controllers.
@@ -74,9 +74,10 @@ public final class MediaSession {
public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0;
/**
- * Set this flag on the session to indicate that it handles commands through
- * the {@link TransportPerformer}. The performer can be retrieved by calling
- * {@link #getTransportPerformer()}.
+ * Set this flag on the session to indicate that it handles transport
+ * control commands through a {@link TransportControlsCallback}. The
+ * callback can be retrieved by calling
+ * {@link #addTransportControlsCallback}.
*/
public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
@@ -123,15 +124,21 @@ public final class MediaSession {
*/
public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5;
- private static final int MSG_MEDIA_BUTTON = 1;
- private static final int MSG_COMMAND = 2;
- private static final int MSG_ROUTE_CHANGE = 3;
- private static final int MSG_ROUTE_CONNECTED = 4;
- private static final int MSG_ROUTE_DISCONNECTED = 5;
+ /**
+ * The session uses local playback. Used for configuring volume handling
+ * with the system.
+ *
+ * @hide
+ */
+ public static final int VOLUME_TYPE_LOCAL = 1;
- private static final String KEY_COMMAND = "command";
- private static final String KEY_EXTRAS = "extras";
- private static final String KEY_CALLBACK = "callback";
+ /**
+ * The session uses remote playback. Used for configuring volume handling
+ * with the system.
+ *
+ * @hide
+ */
+ public static final int VOLUME_TYPE_REMOTE = 2;
private final Object mLock = new Object();
@@ -139,13 +146,16 @@ public final class MediaSession {
private final ISession mBinder;
private final CallbackStub mCbStub;
- private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
+ private final ArrayList<CallbackMessageHandler> mCallbacks
+ = new ArrayList<CallbackMessageHandler>();
+ private final ArrayList<TransportMessageHandler> mTransportCallbacks
+ = new ArrayList<TransportMessageHandler>();
// TODO route interfaces
private final ArrayMap<String, RouteInterface.EventListener> mInterfaceListeners
= new ArrayMap<String, RouteInterface.EventListener>();
- private TransportPerformer mPerformer;
private Route mRoute;
+ private RemoteVolumeProvider mVolumeProvider;
private boolean mActive = false;;
@@ -162,11 +172,12 @@ public final class MediaSession {
throw new RuntimeException("Dead object in MediaSessionController constructor: ", e);
}
mSessionToken = new MediaSessionToken(controllerBinder);
- mPerformer = new TransportPerformer(mBinder);
}
/**
- * Set the callback to receive updates on.
+ * Add a callback to receive updates on for the MediaSession. This includes
+ * media button and volume events. The caller's thread will be used to post
+ * events.
*
* @param callback The callback object
*/
@@ -193,7 +204,8 @@ public final class MediaSession {
if (handler == null) {
handler = new Handler();
}
- MessageHandler msgHandler = new MessageHandler(handler.getLooper(), callback);
+ CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(),
+ callback);
mCallbacks.add(msgHandler);
}
}
@@ -210,18 +222,6 @@ public final class MediaSession {
}
/**
- * Retrieves the {@link TransportPerformer} for this session. To receive
- * commands through the performer you must also set the
- * {@link #FLAG_HANDLES_TRANSPORT_CONTROLS} flag using
- * {@link #setFlags(int)}.
- *
- * @return The performer associated with this session.
- */
- public TransportPerformer getTransportPerformer() {
- return mPerformer;
- }
-
- /**
* Set an intent for launching UI for this Session. This can be used as a
* quick link to an ongoing media screen.
*
@@ -246,7 +246,7 @@ public final class MediaSession {
/**
* Set the stream this session is playing on. This will affect the system's
- * volume handling for this session. If {@link #useRemotePlayback} was
+ * volume handling for this session. If {@link #setPlaybackToRemote} was
* previously called it will stop receiving volume commands and the system
* will begin sending volume changes to the appropriate stream.
* <p>
@@ -254,25 +254,36 @@ public final class MediaSession {
*
* @param stream The {@link AudioManager} stream this session is playing on.
*/
- public void useLocalPlayback(int stream) {
- // TODO
+ public void setPlaybackToLocal(int stream) {
+ try {
+ mBinder.configureVolumeHandling(VOLUME_TYPE_LOCAL, stream, 0);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Failure in setPlaybackToLocal.", e);
+ }
}
/**
* Configure this session to use remote volume handling. This must be called
* to receive volume button events, otherwise the system will adjust the
- * current stream volume for this session. If {@link #useLocalPlayback} was
- * previously called that stream will stop receiving volume changes for this
- * session.
+ * current stream volume for this session. If {@link #setPlaybackToLocal}
+ * was previously called that stream will stop receiving volume changes for
+ * this session.
*
* @param volumeProvider The provider that will handle volume changes. May
* not be null.
*/
- public void useRemotePlayback(RemoteVolumeProvider volumeProvider) {
+ public void setPlaybackToRemote(RemoteVolumeProvider volumeProvider) {
if (volumeProvider == null) {
throw new IllegalArgumentException("volumeProvider may not be null!");
}
- // TODO
+ mVolumeProvider = volumeProvider;
+
+ try {
+ mBinder.configureVolumeHandling(VOLUME_TYPE_REMOTE, volumeProvider.getVolumeControl(),
+ volumeProvider.getMaxVolume());
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
+ }
}
/**
@@ -312,7 +323,7 @@ public final class MediaSession {
* @param event The name of the event to send
* @param extras Any extras included with the event
*/
- public void sendEvent(String event, Bundle extras) {
+ public void sendSessionEvent(String event, Bundle extras) {
if (TextUtils.isEmpty(event)) {
throw new IllegalArgumentException("event cannot be null or empty");
}
@@ -432,12 +443,160 @@ public final class MediaSession {
return true;
}
- private MessageHandler getHandlerForCallbackLocked(Callback cb) {
+ /**
+ * Add a callback to receive transport controls on, such as play, rewind, or
+ * fast forward.
+ *
+ * @param callback The callback object
+ */
+ public void addTransportControlsCallback(@NonNull TransportControlsCallback callback) {
+ addTransportControlsCallback(callback, null);
+ }
+
+ /**
+ * Add a callback to receive transport controls on, such as play, rewind, or
+ * fast forward. The updates will be posted to the specified handler. If no
+ * handler is provided they will be posted to the caller's thread.
+ *
+ * @param callback The callback to receive updates on
+ * @param handler The handler to post the updates on
+ */
+ public void addTransportControlsCallback(@NonNull TransportControlsCallback callback,
+ @Nullable Handler handler) {
+ if (callback == null) {
+ throw new IllegalArgumentException("Callback cannot be null");
+ }
+ synchronized (mLock) {
+ if (getTransportControlsHandlerForCallbackLocked(callback) != null) {
+ Log.w(TAG, "Callback is already added, ignoring");
+ return;
+ }
+ if (handler == null) {
+ handler = new Handler();
+ }
+ TransportMessageHandler msgHandler = new TransportMessageHandler(handler.getLooper(),
+ callback);
+ mTransportCallbacks.add(msgHandler);
+ }
+ }
+
+ /**
+ * Stop receiving transport controls on the specified callback. If an update
+ * has already been posted you may still receive it after this call returns.
+ *
+ * @param callback The callback to stop receiving updates on
+ */
+ public void removeTransportControlsCallback(@NonNull TransportControlsCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("Callback cannot be null");
+ }
+ synchronized (mLock) {
+ removeTransportControlsCallbackLocked(callback);
+ }
+ }
+
+ /**
+ * Update the current playback state.
+ *
+ * @param state The current state of playback
+ */
+ public void setPlaybackState(PlaybackState state) {
+ try {
+ mBinder.setPlaybackState(state);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Dead object in setPlaybackState.", e);
+ }
+ }
+
+ /**
+ * Update the current metadata. New metadata can be created using
+ * {@link android.media.MediaMetadata.Builder}.
+ *
+ * @param metadata The new metadata
+ */
+ public void setMetadata(MediaMetadata metadata) {
+ try {
+ mBinder.setMetadata(metadata);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Dead object in setPlaybackState.", e);
+ }
+ }
+
+ private void dispatchPlay() {
+ postToTransportCallbacks(TransportMessageHandler.MSG_PLAY);
+ }
+
+ private void dispatchPause() {
+ postToTransportCallbacks(TransportMessageHandler.MSG_PAUSE);
+ }
+
+ private void dispatchStop() {
+ postToTransportCallbacks(TransportMessageHandler.MSG_STOP);
+ }
+
+ private void dispatchNext() {
+ postToTransportCallbacks(TransportMessageHandler.MSG_NEXT);
+ }
+
+ private void dispatchPrevious() {
+ postToTransportCallbacks(TransportMessageHandler.MSG_PREVIOUS);
+ }
+
+ private void dispatchFastForward() {
+ postToTransportCallbacks(TransportMessageHandler.MSG_FAST_FORWARD);
+ }
+
+ private void dispatchRewind() {
+ postToTransportCallbacks(TransportMessageHandler.MSG_REWIND);
+ }
+
+ private void dispatchSeekTo(long pos) {
+ postToTransportCallbacks(TransportMessageHandler.MSG_SEEK_TO, pos);
+ }
+
+ private void dispatchRate(Rating rating) {
+ postToTransportCallbacks(TransportMessageHandler.MSG_RATE, rating);
+ }
+
+ private TransportMessageHandler getTransportControlsHandlerForCallbackLocked(
+ TransportControlsCallback callback) {
+ for (int i = mTransportCallbacks.size() - 1; i >= 0; i--) {
+ TransportMessageHandler handler = mTransportCallbacks.get(i);
+ if (callback == handler.mCallback) {
+ return handler;
+ }
+ }
+ return null;
+ }
+
+ private boolean removeTransportControlsCallbackLocked(TransportControlsCallback callback) {
+ for (int i = mTransportCallbacks.size() - 1; i >= 0; i--) {
+ if (callback == mTransportCallbacks.get(i).mCallback) {
+ mTransportCallbacks.remove(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void postToTransportCallbacks(int what, Object obj) {
+ synchronized (mLock) {
+ for (int i = mTransportCallbacks.size() - 1; i >= 0; i--) {
+ mTransportCallbacks.get(i).post(what, obj);
+ }
+ }
+ }
+
+ private void postToTransportCallbacks(int what) {
+ postToTransportCallbacks(what, null);
+ }
+
+ private CallbackMessageHandler getHandlerForCallbackLocked(Callback cb) {
if (cb == null) {
throw new IllegalArgumentException("Callback cannot be null");
}
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- MessageHandler handler = mCallbacks.get(i);
+ CallbackMessageHandler handler = mCallbacks.get(i);
if (cb == handler.mCallback) {
return handler;
}
@@ -450,7 +609,7 @@ public final class MediaSession {
throw new IllegalArgumentException("Callback cannot be null");
}
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- MessageHandler handler = mCallbacks.get(i);
+ CallbackMessageHandler handler = mCallbacks.get(i);
if (cb == handler.mCallback) {
mCallbacks.remove(i);
return true;
@@ -463,7 +622,7 @@ public final class MediaSession {
Command cmd = new Command(command, extras, resultCb);
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).post(MSG_COMMAND, cmd);
+ mCallbacks.get(i).post(CallbackMessageHandler.MSG_COMMAND, cmd);
}
}
}
@@ -471,7 +630,7 @@ public final class MediaSession {
private void postMediaButton(Intent mediaButtonIntent) {
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).post(MSG_MEDIA_BUTTON, mediaButtonIntent);
+ mCallbacks.get(i).post(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent);
}
}
}
@@ -479,7 +638,7 @@ public final class MediaSession {
private void postRequestRouteChange(RouteInfo route) {
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).post(MSG_ROUTE_CHANGE, route);
+ mCallbacks.get(i).post(CallbackMessageHandler.MSG_ROUTE_CHANGE, route);
}
}
}
@@ -488,7 +647,7 @@ public final class MediaSession {
synchronized (mLock) {
mRoute = new Route(route, options, this);
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).post(MSG_ROUTE_CONNECTED, mRoute);
+ mCallbacks.get(i).post(CallbackMessageHandler.MSG_ROUTE_CONNECTED, mRoute);
}
}
}
@@ -497,16 +656,16 @@ public final class MediaSession {
synchronized (mLock) {
if (mRoute != null && TextUtils.equals(mRoute.getRouteInfo().getId(), route.getId())) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).post(MSG_ROUTE_DISCONNECTED, mRoute, reason);
+ mCallbacks.get(i).post(CallbackMessageHandler.MSG_ROUTE_DISCONNECTED, mRoute,
+ reason);
}
}
}
}
/**
- * Receives commands or updates from controllers and routes. An app can
- * specify what commands and buttons it supports by setting them on the
- * MediaSession.
+ * Receives generic commands or updates from controllers and the system.
+ * Callbacks may be registered using {@link #addCallback}.
*/
public abstract static class Callback {
@@ -525,7 +684,7 @@ public final class MediaSession {
* @param mediaButtonIntent an intent containing the KeyEvent as an
* extra
*/
- public void onMediaButton(Intent mediaButtonIntent) {
+ public void onMediaButtonEvent(Intent mediaButtonIntent) {
}
/**
@@ -536,7 +695,7 @@ public final class MediaSession {
* @param command
* @param extras optional
*/
- public void onCommand(String command, Bundle extras, ResultReceiver cb) {
+ public void onControlCommand(String command, Bundle extras, ResultReceiver cb) {
}
/**
@@ -582,6 +741,82 @@ public final class MediaSession {
}
/**
+ * Receives transport control commands. Callbacks may be registered using
+ * {@link #addTransportControlsCallback}.
+ */
+ public static abstract class TransportControlsCallback {
+
+ /**
+ * Override to handle requests to begin playback.
+ */
+ public void onPlay() {
+ }
+
+ /**
+ * Override to handle requests to pause playback.
+ */
+ public void onPause() {
+ }
+
+ /**
+ * Override to handle requests to skip to the next media item.
+ */
+ public void onSkipToNext() {
+ }
+
+ /**
+ * Override to handle requests to skip to the previous media item.
+ */
+ public void onSkipToPrevious() {
+ }
+
+ /**
+ * Override to handle requests to fast forward.
+ */
+ public void onFastForward() {
+ }
+
+ /**
+ * Override to handle requests to rewind.
+ */
+ public void onRewind() {
+ }
+
+ /**
+ * Override to handle requests to stop playback.
+ */
+ public void onStop() {
+ }
+
+ /**
+ * Override to handle requests to seek to a specific position in ms.
+ *
+ * @param pos New position to move to, in milliseconds.
+ */
+ public void onSeekTo(long pos) {
+ }
+
+ /**
+ * Override to handle the item being rated.
+ *
+ * @param rating
+ */
+ public void onSetRating(Rating rating) {
+ }
+
+ /**
+ * Report that audio focus has changed on the app. This only happens if
+ * you have indicated you have started playing with
+ * {@link #setPlaybackState}.
+ *
+ * @param focusChange The type of focus change, TBD.
+ * @hide
+ */
+ public void onRouteFocusChange(int focusChange) {
+ }
+ }
+
+ /**
* @hide
*/
public static class CallbackStub extends ISessionCallback.Stub {
@@ -643,10 +878,7 @@ public final class MediaSession {
public void onPlay() throws RemoteException {
MediaSession session = mMediaSession.get();
if (session != null) {
- TransportPerformer tp = session.getTransportPerformer();
- if (tp != null) {
- tp.onPlay();
- }
+ session.dispatchPlay();
}
}
@@ -654,10 +886,7 @@ public final class MediaSession {
public void onPause() throws RemoteException {
MediaSession session = mMediaSession.get();
if (session != null) {
- TransportPerformer tp = session.getTransportPerformer();
- if (tp != null) {
- tp.onPause();
- }
+ session.dispatchPause();
}
}
@@ -665,10 +894,7 @@ public final class MediaSession {
public void onStop() throws RemoteException {
MediaSession session = mMediaSession.get();
if (session != null) {
- TransportPerformer tp = session.getTransportPerformer();
- if (tp != null) {
- tp.onStop();
- }
+ session.dispatchStop();
}
}
@@ -676,10 +902,7 @@ public final class MediaSession {
public void onNext() throws RemoteException {
MediaSession session = mMediaSession.get();
if (session != null) {
- TransportPerformer tp = session.getTransportPerformer();
- if (tp != null) {
- tp.onNext();
- }
+ session.dispatchNext();
}
}
@@ -687,10 +910,7 @@ public final class MediaSession {
public void onPrevious() throws RemoteException {
MediaSession session = mMediaSession.get();
if (session != null) {
- TransportPerformer tp = session.getTransportPerformer();
- if (tp != null) {
- tp.onPrevious();
- }
+ session.dispatchPrevious();
}
}
@@ -698,10 +918,7 @@ public final class MediaSession {
public void onFastForward() throws RemoteException {
MediaSession session = mMediaSession.get();
if (session != null) {
- TransportPerformer tp = session.getTransportPerformer();
- if (tp != null) {
- tp.onFastForward();
- }
+ session.dispatchFastForward();
}
}
@@ -709,10 +926,7 @@ public final class MediaSession {
public void onRewind() throws RemoteException {
MediaSession session = mMediaSession.get();
if (session != null) {
- TransportPerformer tp = session.getTransportPerformer();
- if (tp != null) {
- tp.onRewind();
- }
+ session.dispatchRewind();
}
}
@@ -720,10 +934,7 @@ public final class MediaSession {
public void onSeekTo(long pos) throws RemoteException {
MediaSession session = mMediaSession.get();
if (session != null) {
- TransportPerformer tp = session.getTransportPerformer();
- if (tp != null) {
- tp.onSeekTo(pos);
- }
+ session.dispatchSeekTo(pos);
}
}
@@ -731,10 +942,7 @@ public final class MediaSession {
public void onRate(Rating rating) throws RemoteException {
MediaSession session = mMediaSession.get();
if (session != null) {
- TransportPerformer tp = session.getTransportPerformer();
- if (tp != null) {
- tp.onRate(rating);
- }
+ session.dispatchRate(rating);
}
}
@@ -758,12 +966,38 @@ public final class MediaSession {
}
+ /*
+ * (non-Javadoc)
+ * @see android.media.session.ISessionCallback#onAdjustVolumeBy(int)
+ */
+ @Override
+ public void onAdjustVolumeBy(int delta) throws RemoteException {
+ // TODO(epastern): Auto-generated method stub
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.media.session.ISessionCallback#onSetVolumeTo(int)
+ */
+ @Override
+ public void onSetVolumeTo(int value) throws RemoteException {
+ // TODO(epastern): Auto-generated method stub
+
+ }
+
}
- private class MessageHandler extends Handler {
+ private class CallbackMessageHandler extends Handler {
+ private static final int MSG_MEDIA_BUTTON = 1;
+ private static final int MSG_COMMAND = 2;
+ private static final int MSG_ROUTE_CHANGE = 3;
+ private static final int MSG_ROUTE_CONNECTED = 4;
+ private static final int MSG_ROUTE_DISCONNECTED = 5;
+
private MediaSession.Callback mCallback;
- public MessageHandler(Looper looper, MediaSession.Callback callback) {
+ public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
super(looper, null, true);
mCallback = callback;
}
@@ -776,11 +1010,11 @@ public final class MediaSession {
}
switch (msg.what) {
case MSG_MEDIA_BUTTON:
- mCallback.onMediaButton((Intent) msg.obj);
+ mCallback.onMediaButtonEvent((Intent) msg.obj);
break;
case MSG_COMMAND:
Command cmd = (Command) msg.obj;
- mCallback.onCommand(cmd.command, cmd.extras, cmd.stub);
+ mCallback.onControlCommand(cmd.command, cmd.extras, cmd.stub);
break;
case MSG_ROUTE_CHANGE:
mCallback.onRequestRouteChange((RouteInfo) msg.obj);
@@ -815,4 +1049,64 @@ public final class MediaSession {
this.stub = stub;
}
}
+
+ private class TransportMessageHandler extends Handler {
+ private static final int MSG_PLAY = 1;
+ private static final int MSG_PAUSE = 2;
+ private static final int MSG_STOP = 3;
+ private static final int MSG_NEXT = 4;
+ private static final int MSG_PREVIOUS = 5;
+ private static final int MSG_FAST_FORWARD = 6;
+ private static final int MSG_REWIND = 7;
+ private static final int MSG_SEEK_TO = 8;
+ private static final int MSG_RATE = 9;
+
+ private TransportControlsCallback mCallback;
+
+ public TransportMessageHandler(Looper looper, TransportControlsCallback cb) {
+ super(looper);
+ mCallback = cb;
+ }
+
+ public void post(int what, Object obj) {
+ obtainMessage(what, obj).sendToTarget();
+ }
+
+ public void post(int what) {
+ post(what, null);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PLAY:
+ mCallback.onPlay();
+ break;
+ case MSG_PAUSE:
+ mCallback.onPause();
+ break;
+ case MSG_STOP:
+ mCallback.onStop();
+ break;
+ case MSG_NEXT:
+ mCallback.onSkipToNext();
+ break;
+ case MSG_PREVIOUS:
+ mCallback.onSkipToPrevious();
+ break;
+ case MSG_FAST_FORWARD:
+ mCallback.onFastForward();
+ break;
+ case MSG_REWIND:
+ mCallback.onRewind();
+ break;
+ case MSG_SEEK_TO:
+ mCallback.onSeekTo((Long) msg.obj);
+ break;
+ case MSG_RATE:
+ mCallback.onSetRating((Rating) msg.obj);
+ break;
+ }
+ }
+ }
}
diff --git a/media/java/android/media/session/MediaSessionInfo.java b/media/java/android/media/session/MediaSessionInfo.java
index 3d8d33f..f701211 100644
--- a/media/java/android/media/session/MediaSessionInfo.java
+++ b/media/java/android/media/session/MediaSessionInfo.java
@@ -20,6 +20,8 @@ import android.os.Parcelable;
/**
* Information about a media session, including the owner's package name.
+ *
+ * @hide
*/
public final class MediaSessionInfo implements Parcelable {
private final String mId;
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 249b9c4..099f601 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -76,13 +76,20 @@ public class MediaSessionLegacyHelper {
}
}
- public void addRccListener(PendingIntent pi, TransportPerformer.Listener listener) {
+ public void sendAdjustVolumeBy(int suggestedStream, int delta, int flags) {
+ mSessionManager.dispatchAdjustVolumeBy(suggestedStream, delta, flags);
+ if (DEBUG) {
+ Log.d(TAG, "dispatched volume adjustment");
+ }
+ }
+
+ public void addRccListener(PendingIntent pi,
+ MediaSession.TransportControlsCallback listener) {
if (pi == null) {
Log.w(TAG, "Pending intent was null, can't add rcc listener.");
return;
}
SessionHolder holder = getHolder(pi, true);
- TransportPerformer performer = holder.mSession.getTransportPerformer();
if (holder.mRccListener != null) {
if (holder.mRccListener == listener) {
if (DEBUG) {
@@ -92,9 +99,9 @@ public class MediaSessionLegacyHelper {
return;
}
// Otherwise it changed so we need to switch to the new one
- performer.removeListener(holder.mRccListener);
+ holder.mSession.removeTransportControlsCallback(holder.mRccListener);
}
- performer.addListener(listener, mHandler);
+ holder.mSession.addTransportControlsCallback(listener, mHandler);
holder.mRccListener = listener;
holder.mFlags |= MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS;
holder.mSession.setFlags(holder.mFlags);
@@ -110,7 +117,7 @@ public class MediaSessionLegacyHelper {
}
SessionHolder holder = getHolder(pi, false);
if (holder != null && holder.mRccListener != null) {
- holder.mSession.getTransportPerformer().removeListener(holder.mRccListener);
+ holder.mSession.removeTransportControlsCallback(holder.mRccListener);
holder.mRccListener = null;
holder.mFlags &= ~MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS;
holder.mSession.setFlags(holder.mFlags);
@@ -141,7 +148,7 @@ public class MediaSessionLegacyHelper {
// set this flag
holder.mFlags |= MediaSession.FLAG_HANDLES_MEDIA_BUTTONS;
holder.mSession.setFlags(holder.mFlags);
- holder.mSession.getTransportPerformer().addListener(holder.mMediaButtonListener, mHandler);
+ holder.mSession.addTransportControlsCallback(holder.mMediaButtonListener, mHandler);
holder.mMediaButtonReceiver = new MediaButtonReceiver(pi, context);
holder.mSession.addCallback(holder.mMediaButtonReceiver, mHandler);
@@ -156,7 +163,7 @@ public class MediaSessionLegacyHelper {
}
SessionHolder holder = getHolder(pi, false);
if (holder != null && holder.mMediaButtonListener != null) {
- holder.mSession.getTransportPerformer().removeListener(holder.mMediaButtonListener);
+ holder.mSession.removeTransportControlsCallback(holder.mMediaButtonListener);
holder.mFlags &= ~MediaSession.FLAG_HANDLES_MEDIA_BUTTONS;
holder.mSession.setFlags(holder.mFlags);
holder.mMediaButtonListener = null;
@@ -201,12 +208,12 @@ public class MediaSessionLegacyHelper {
}
@Override
- public void onMediaButton(Intent mediaButtonIntent) {
+ public void onMediaButtonEvent(Intent mediaButtonIntent) {
MediaSessionLegacyHelper.sendKeyEvent(mPendingIntent, mContext, mediaButtonIntent);
}
}
- private static final class MediaButtonListener extends TransportPerformer.Listener {
+ private static final class MediaButtonListener extends MediaSession.TransportControlsCallback {
private final PendingIntent mPendingIntent;
private final Context mContext;
@@ -226,12 +233,12 @@ public class MediaSessionLegacyHelper {
}
@Override
- public void onNext() {
+ public void onSkipToNext() {
sendKeyEvent(KeyEvent.KEYCODE_MEDIA_NEXT);
}
@Override
- public void onPrevious() {
+ public void onSkipToPrevious() {
sendKeyEvent(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
}
@@ -272,7 +279,7 @@ public class MediaSessionLegacyHelper {
public final PendingIntent mPi;
public MediaButtonListener mMediaButtonListener;
public MediaButtonReceiver mMediaButtonReceiver;
- public TransportPerformer.Listener mRccListener;
+ public MediaSession.TransportControlsCallback mRccListener;
public int mFlags;
public SessionHolder(MediaSession session, PendingIntent pi) {
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 0589a7d..9e8b0d3 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -106,6 +106,7 @@ public final class MediaSessionManager {
* @param notificationListener The enabled notification listener component.
* May be null.
* @return A list of controllers for ongoing sessions
+ * @hide
*/
public List<MediaController> getActiveSessions(ComponentName notificationListener) {
return getActiveSessionsForUser(notificationListener, UserHandle.myUserId());
@@ -165,4 +166,22 @@ public final class MediaSessionManager {
Log.e(TAG, "Failed to send key event.", e);
}
}
+
+ /**
+ * Dispatch an adjust volume request to the system. It will be routed to the
+ * most relevant stream/session.
+ *
+ * @param suggestedStream The stream to fall back to if there isn't a
+ * relevant stream
+ * @param delta The amount to adjust the volume by.
+ * @param flags Any flags to include with the volume change.
+ * @hide
+ */
+ public void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags) {
+ try {
+ mService.dispatchAdjustVolumeBy(suggestedStream, delta, flags);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send adjust volume.", e);
+ }
+ }
}
diff --git a/media/java/android/media/session/MediaSessionToken.java b/media/java/android/media/session/MediaSessionToken.java
index f5569a4..86f5662 100644
--- a/media/java/android/media/session/MediaSessionToken.java
+++ b/media/java/android/media/session/MediaSessionToken.java
@@ -20,7 +20,12 @@ import android.media.session.ISessionController;
import android.os.Parcel;
import android.os.Parcelable;
-public class MediaSessionToken implements Parcelable {
+/**
+ * Represents an ongoing session. This may be passed to apps by the session
+ * owner to allow them to create a {@link MediaController} to communicate with
+ * the session.
+ */
+public final class MediaSessionToken implements Parcelable {
private ISessionController mBinder;
/**
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 7ef38eaa..e09ac3f 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -22,7 +22,7 @@ import android.os.SystemClock;
/**
* Playback state for a {@link MediaSession}. This includes a state like
- * {@link PlaybackState#PLAYSTATE_PLAYING}, the current playback position,
+ * {@link PlaybackState#STATE_PLAYING}, the current playback position,
* and the current control capabilities.
*/
public final class PlaybackState implements Parcelable {
@@ -59,28 +59,28 @@ public final class PlaybackState implements Parcelable {
*
* @see #setActions
*/
- public static final long ACTION_PREVIOUS_ITEM = 1 << 4;
+ public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4;
/**
* Indicates this performer supports the next command.
*
* @see #setActions
*/
- public static final long ACTION_NEXT_ITEM = 1 << 5;
+ public static final long ACTION_SKIP_TO_NEXT = 1 << 5;
/**
* Indicates this performer supports the fast forward command.
*
* @see #setActions
*/
- public static final long ACTION_FASTFORWARD = 1 << 6;
+ public static final long ACTION_FAST_FORWARD = 1 << 6;
/**
* Indicates this performer supports the set rating command.
*
* @see #setActions
*/
- public static final long ACTION_RATING = 1 << 7;
+ public static final long ACTION_SET_RATING = 1 << 7;
/**
* Indicates this performer supports the seek to command.
@@ -102,42 +102,42 @@ public final class PlaybackState implements Parcelable {
*
* @see #setState
*/
- public final static int PLAYSTATE_NONE = 0;
+ public final static int STATE_NONE = 0;
/**
* State indicating this item is currently stopped.
*
* @see #setState
*/
- public final static int PLAYSTATE_STOPPED = 1;
+ public final static int STATE_STOPPED = 1;
/**
* State indicating this item is currently paused.
*
* @see #setState
*/
- public final static int PLAYSTATE_PAUSED = 2;
+ public final static int STATE_PAUSED = 2;
/**
* State indicating this item is currently playing.
*
* @see #setState
*/
- public final static int PLAYSTATE_PLAYING = 3;
+ public final static int STATE_PLAYING = 3;
/**
* State indicating this item is currently fast forwarding.
*
* @see #setState
*/
- public final static int PLAYSTATE_FAST_FORWARDING = 4;
+ public final static int STATE_FAST_FORWARDING = 4;
/**
* State indicating this item is currently rewinding.
*
* @see #setState
*/
- public final static int PLAYSTATE_REWINDING = 5;
+ public final static int STATE_REWINDING = 5;
/**
* State indicating this item is currently buffering and will begin playing
@@ -145,7 +145,7 @@ public final class PlaybackState implements Parcelable {
*
* @see #setState
*/
- public final static int PLAYSTATE_BUFFERING = 6;
+ public final static int STATE_BUFFERING = 6;
/**
* State indicating this item is currently in an error state. The error
@@ -153,30 +153,30 @@ public final class PlaybackState implements Parcelable {
*
* @see #setState
*/
- public final static int PLAYSTATE_ERROR = 7;
+ public final static int STATE_ERROR = 7;
/**
* State indicating the class doing playback is currently connecting to a
* route. Depending on the implementation you may return to the previous
- * state when the connection finishes or enter {@link #PLAYSTATE_NONE}. If
- * the connection failed {@link #PLAYSTATE_ERROR} should be used.
+ * state when the connection finishes or enter {@link #STATE_NONE}. If
+ * the connection failed {@link #STATE_ERROR} should be used.
* @hide
*/
- public final static int PLAYSTATE_CONNECTING = 8;
+ public final static int STATE_CONNECTING = 8;
/**
* State indicating the player is currently skipping to the previous item.
*
* @see #setState
*/
- public final static int PLAYSTATE_SKIPPING_BACKWARDS = 9;
+ public final static int STATE_SKIPPING_TO_PREVIOUS = 9;
/**
* State indicating the player is currently skipping to the next item.
*
* @see #setState
*/
- public final static int PLAYSTATE_SKIPPING_FORWARDS = 10;
+ public final static int STATE_SKIPPING_TO_NEXT = 10;
/**
* Use this value for the position to indicate the position is not known.
@@ -188,7 +188,7 @@ public final class PlaybackState implements Parcelable {
private long mBufferPosition;
private float mRate;
private long mActions;
- private String mErrorMessage;
+ private CharSequence mErrorMessage;
private long mUpdateTime;
/**
@@ -221,7 +221,7 @@ public final class PlaybackState implements Parcelable {
mUpdateTime = in.readLong();
mBufferPosition = in.readLong();
mActions = in.readLong();
- mErrorMessage = in.readString();
+ mErrorMessage = in.readCharSequence();
}
@@ -252,20 +252,20 @@ public final class PlaybackState implements Parcelable {
dest.writeLong(mUpdateTime);
dest.writeLong(mBufferPosition);
dest.writeLong(mActions);
- dest.writeString(mErrorMessage);
+ dest.writeCharSequence(mErrorMessage);
}
/**
* Get the current state of playback. One of the following:
* <ul>
- * <li> {@link PlaybackState#PLAYSTATE_NONE}</li>
- * <li> {@link PlaybackState#PLAYSTATE_STOPPED}</li>
- * <li> {@link PlaybackState#PLAYSTATE_PLAYING}</li>
- * <li> {@link PlaybackState#PLAYSTATE_PAUSED}</li>
- * <li> {@link PlaybackState#PLAYSTATE_FAST_FORWARDING}</li>
- * <li> {@link PlaybackState#PLAYSTATE_REWINDING}</li>
- * <li> {@link PlaybackState#PLAYSTATE_BUFFERING}</li>
- * <li> {@link PlaybackState#PLAYSTATE_ERROR}</li>
+ * <li> {@link PlaybackState#STATE_NONE}</li>
+ * <li> {@link PlaybackState#STATE_STOPPED}</li>
+ * <li> {@link PlaybackState#STATE_PLAYING}</li>
+ * <li> {@link PlaybackState#STATE_PAUSED}</li>
+ * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
+ * <li> {@link PlaybackState#STATE_REWINDING}</li>
+ * <li> {@link PlaybackState#STATE_BUFFERING}</li>
+ * <li> {@link PlaybackState#STATE_ERROR}</li>
*/
public int getState() {
return mState;
@@ -283,25 +283,25 @@ public final class PlaybackState implements Parcelable {
* <p>
* The state must be one of the following:
* <ul>
- * <li> {@link PlaybackState#PLAYSTATE_NONE}</li>
- * <li> {@link PlaybackState#PLAYSTATE_STOPPED}</li>
- * <li> {@link PlaybackState#PLAYSTATE_PLAYING}</li>
- * <li> {@link PlaybackState#PLAYSTATE_PAUSED}</li>
- * <li> {@link PlaybackState#PLAYSTATE_FAST_FORWARDING}</li>
- * <li> {@link PlaybackState#PLAYSTATE_REWINDING}</li>
- * <li> {@link PlaybackState#PLAYSTATE_BUFFERING}</li>
- * <li> {@link PlaybackState#PLAYSTATE_ERROR}</li>
+ * <li> {@link PlaybackState#STATE_NONE}</li>
+ * <li> {@link PlaybackState#STATE_STOPPED}</li>
+ * <li> {@link PlaybackState#STATE_PLAYING}</li>
+ * <li> {@link PlaybackState#STATE_PAUSED}</li>
+ * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
+ * <li> {@link PlaybackState#STATE_REWINDING}</li>
+ * <li> {@link PlaybackState#STATE_BUFFERING}</li>
+ * <li> {@link PlaybackState#STATE_ERROR}</li>
* </ul>
*
* @param state The current state of playback.
* @param position The position in the current track in ms.
- * @param rate The current rate of playback as a multiple of normal
+ * @param playbackRate The current rate of playback as a multiple of normal
* playback.
*/
- public void setState(int state, long position, float rate) {
+ public void setState(int state, long position, float playbackRate) {
this.mState = state;
this.mPosition = position;
- this.mRate = rate;
+ this.mRate = playbackRate;
mUpdateTime = SystemClock.elapsedRealtime();
}
@@ -337,7 +337,7 @@ public final class PlaybackState implements Parcelable {
*
* @return The current rate of playback.
*/
- public float getRate() {
+ public float getPlaybackRate() {
return mRate;
}
@@ -345,15 +345,15 @@ public final class PlaybackState implements Parcelable {
* Get the current actions available on this session. This should use a
* bitmask of the available actions.
* <ul>
- * <li> {@link PlaybackState#ACTION_PREVIOUS_ITEM}</li>
+ * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li>
* <li> {@link PlaybackState#ACTION_REWIND}</li>
* <li> {@link PlaybackState#ACTION_PLAY}</li>
* <li> {@link PlaybackState#ACTION_PAUSE}</li>
* <li> {@link PlaybackState#ACTION_STOP}</li>
- * <li> {@link PlaybackState#ACTION_FASTFORWARD}</li>
- * <li> {@link PlaybackState#ACTION_NEXT_ITEM}</li>
+ * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li>
+ * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
* <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
- * <li> {@link PlaybackState#ACTION_RATING}</li>
+ * <li> {@link PlaybackState#ACTION_SET_RATING}</li>
* </ul>
*/
public long getActions() {
@@ -364,15 +364,15 @@ public final class PlaybackState implements Parcelable {
* Set the current capabilities available on this session. This should use a
* bitmask of the available capabilities.
* <ul>
- * <li> {@link PlaybackState#ACTION_PREVIOUS_ITEM}</li>
+ * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li>
* <li> {@link PlaybackState#ACTION_REWIND}</li>
* <li> {@link PlaybackState#ACTION_PLAY}</li>
* <li> {@link PlaybackState#ACTION_PAUSE}</li>
* <li> {@link PlaybackState#ACTION_STOP}</li>
- * <li> {@link PlaybackState#ACTION_FASTFORWARD}</li>
- * <li> {@link PlaybackState#ACTION_NEXT_ITEM}</li>
+ * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li>
+ * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
* <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
- * <li> {@link PlaybackState#ACTION_RATING}</li>
+ * <li> {@link PlaybackState#ACTION_SET_RATING}</li>
* </ul>
*/
public void setActions(long capabilities) {
@@ -381,9 +381,9 @@ public final class PlaybackState implements Parcelable {
/**
* Get a user readable error message. This should be set when the state is
- * {@link PlaybackState#PLAYSTATE_ERROR}.
+ * {@link PlaybackState#STATE_ERROR}.
*/
- public String getErrorMessage() {
+ public CharSequence getErrorMessage() {
return mErrorMessage;
}
@@ -400,9 +400,9 @@ public final class PlaybackState implements Parcelable {
/**
* Set a user readable error message. This should be set when the state is
- * {@link PlaybackState#PLAYSTATE_ERROR}.
+ * {@link PlaybackState#STATE_ERROR}.
*/
- public void setErrorMessage(String errorMessage) {
+ public void setErrorMessage(CharSequence errorMessage) {
mErrorMessage = errorMessage;
}
@@ -417,23 +417,23 @@ public final class PlaybackState implements Parcelable {
public static int getStateFromRccState(int rccState) {
switch (rccState) {
case RemoteControlClient.PLAYSTATE_BUFFERING:
- return PLAYSTATE_BUFFERING;
+ return STATE_BUFFERING;
case RemoteControlClient.PLAYSTATE_ERROR:
- return PLAYSTATE_ERROR;
+ return STATE_ERROR;
case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- return PLAYSTATE_FAST_FORWARDING;
+ return STATE_FAST_FORWARDING;
case RemoteControlClient.PLAYSTATE_NONE:
- return PLAYSTATE_NONE;
+ return STATE_NONE;
case RemoteControlClient.PLAYSTATE_PAUSED:
- return PLAYSTATE_PAUSED;
+ return STATE_PAUSED;
case RemoteControlClient.PLAYSTATE_PLAYING:
- return PLAYSTATE_PLAYING;
+ return STATE_PLAYING;
case RemoteControlClient.PLAYSTATE_REWINDING:
- return PLAYSTATE_REWINDING;
+ return STATE_REWINDING;
case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- return PLAYSTATE_SKIPPING_BACKWARDS;
+ return STATE_SKIPPING_TO_PREVIOUS;
case RemoteControlClient.PLAYSTATE_STOPPED:
- return PLAYSTATE_STOPPED;
+ return STATE_STOPPED;
default:
return -1;
}
@@ -457,7 +457,7 @@ public final class PlaybackState implements Parcelable {
private static long getActionForRccFlag(int flag) {
switch (flag) {
case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS:
- return ACTION_PREVIOUS_ITEM;
+ return ACTION_SKIP_TO_PREVIOUS;
case RemoteControlClient.FLAG_KEY_MEDIA_REWIND:
return ACTION_REWIND;
case RemoteControlClient.FLAG_KEY_MEDIA_PLAY:
@@ -469,13 +469,13 @@ public final class PlaybackState implements Parcelable {
case RemoteControlClient.FLAG_KEY_MEDIA_STOP:
return ACTION_STOP;
case RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD:
- return ACTION_FASTFORWARD;
+ return ACTION_FAST_FORWARD;
case RemoteControlClient.FLAG_KEY_MEDIA_NEXT:
- return ACTION_NEXT_ITEM;
+ return ACTION_SKIP_TO_NEXT;
case RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE:
return ACTION_SEEK_TO;
case RemoteControlClient.FLAG_KEY_MEDIA_RATING:
- return ACTION_RATING;
+ return ACTION_SET_RATING;
}
return 0;
}
diff --git a/media/java/android/media/session/RemoteVolumeProvider.java b/media/java/android/media/session/RemoteVolumeProvider.java
index 9526cc8..47f672f 100644
--- a/media/java/android/media/session/RemoteVolumeProvider.java
+++ b/media/java/android/media/session/RemoteVolumeProvider.java
@@ -1,35 +1,61 @@
+/*
+ * 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.
+ */
package android.media.session;
/**
* Handles requests to adjust or set the volume on a session. This is also used
* to push volume updates back to the session after a request has been handled.
* You can set a volume provider on a session by calling
- * {@link MediaSession#useRemotePlayback}.
+ * {@link MediaSession#setPlaybackToRemote}.
*/
public abstract class RemoteVolumeProvider {
/**
- * Handles relative volume changes via {@link #onAdjustVolume(int)}.
+ * The volume is fixed and can not be modified. Requests to change volume
+ * should be ignored.
+ */
+ public static final int VOLUME_CONTROL_FIXED = 0;
+
+ /**
+ * The volume control uses relative adjustment via
+ * {@link #onAdjustVolumeBy(int)}. Attempts to set the volume to a specific
+ * value should be ignored.
*/
- public static final int FLAG_VOLUME_RELATIVE = 1 << 0;
+ public static final int VOLUME_CONTROL_RELATIVE = 1;
/**
- * Handles setting the volume via {@link #onSetVolume(int)}.
+ * The volume control uses an absolute value. It may be adjusted using
+ * {@link #onAdjustVolumeBy(int)} or set directly using
+ * {@link #onSetVolumeTo(int)}.
*/
- public static final int FLAG_VOLUME_ABSOLUTE = 1 << 1;
+ public static final int VOLUME_CONTROL_ABSOLUTE = 2;
- private final int mFlags;
+ private final int mControlType;
private final int mMaxVolume;
/**
* Create a new volume provider for handling volume events. You must specify
- * the type of events and the maximum volume that can be used.
+ * the type of volume control and the maximum volume that can be used.
*
- * @param flags The flags to use with this provider.
+ * @param volumeControl The method for controlling volume that is used by
+ * this provider.
* @param maxVolume The maximum allowed volume.
*/
- public RemoteVolumeProvider(int flags, int maxVolume) {
- mFlags = flags;
+ public RemoteVolumeProvider(int volumeControl, int maxVolume) {
+ mControlType = volumeControl;
mMaxVolume = maxVolume;
}
@@ -38,15 +64,15 @@ public abstract class RemoteVolumeProvider {
*
* @return The current volume.
*/
- public abstract int getCurrentVolume();
+ public abstract int onGetCurrentVolume();
/**
- * Get the flags that were set for this volume provider.
+ * Get the volume control type that this volume provider uses.
*
- * @return The flags for this volume provider
+ * @return The volume control type for this volume provider
*/
- public final int getFlags() {
- return mFlags;
+ public final int getVolumeControl() {
+ return mControlType;
}
/**
@@ -59,7 +85,7 @@ public abstract class RemoteVolumeProvider {
}
/**
- * Notify the system that the remove playback's volume has been changed.
+ * Notify the system that the remote playback's volume has been changed.
*/
public final void notifyVolumeChanged() {
// TODO
@@ -70,7 +96,7 @@ public abstract class RemoteVolumeProvider {
*
* @param volume The volume to set the output to.
*/
- public void onSetVolume(int volume) {
+ public void onSetVolumeTo(int volume) {
}
/**
@@ -79,6 +105,6 @@ public abstract class RemoteVolumeProvider {
*
* @param delta The amount to change the volume
*/
- public void onAdjustVolume(int delta) {
+ public void onAdjustVolumeBy(int delta) {
}
} \ No newline at end of file
diff --git a/media/java/android/media/session/TransportController.java b/media/java/android/media/session/TransportController.java
deleted file mode 100644
index 090489b..0000000
--- a/media/java/android/media/session/TransportController.java
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * 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.
- */
-package android.media.session;
-
-import android.media.MediaMetadata;
-import android.media.Rating;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * Interface for controlling media playback on a session. This allows an app to
- * request changes in playback, retrieve the current playback state and
- * metadata, and listen for changes to the playback state and metadata.
- */
-public final class TransportController {
- private static final String TAG = "TransportController";
-
- private final Object mLock = new Object();
- private final ArrayList<MessageHandler> mListeners = new ArrayList<MessageHandler>();
- private final ISessionController mBinder;
-
- /**
- * @hide
- */
- public TransportController(ISessionController binder) {
- mBinder = binder;
- }
-
- /**
- * Start listening to changes in playback state.
- */
- public void addStateListener(TransportStateListener listener) {
- addStateListener(listener, null);
- }
-
- public void addStateListener(TransportStateListener listener, Handler handler) {
- if (listener == null) {
- throw new IllegalArgumentException("Listener cannot be null");
- }
- synchronized (mLock) {
- if (getHandlerForListenerLocked(listener) != null) {
- Log.w(TAG, "Listener is already added, ignoring");
- return;
- }
- if (handler == null) {
- handler = new Handler();
- }
-
- MessageHandler msgHandler = new MessageHandler(handler.getLooper(), listener);
- mListeners.add(msgHandler);
- }
- }
-
- /**
- * Stop listening to changes in playback state.
- */
- public void removeStateListener(TransportStateListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("Listener cannot be null");
- }
- synchronized (mLock) {
- removeStateListenerLocked(listener);
- }
- }
-
- /**
- * Request that the player start its playback at its current position.
- */
- public void play() {
- try {
- mBinder.play();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play.", e);
- }
- }
-
- /**
- * Request that the player pause its playback and stay at its current
- * position.
- */
- public void pause() {
- try {
- mBinder.pause();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling pause.", e);
- }
- }
-
- /**
- * Request that the player stop its playback; it may clear its state in
- * whatever way is appropriate.
- */
- public void stop() {
- try {
- mBinder.stop();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling stop.", e);
- }
- }
-
- /**
- * Move to a new location in the media stream.
- *
- * @param pos Position to move to, in milliseconds.
- */
- public void seekTo(long pos) {
- try {
- mBinder.seekTo(pos);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling seekTo.", e);
- }
- }
-
- /**
- * Start fast forwarding. If playback is already fast forwarding this may
- * increase the rate.
- */
- public void fastForward() {
- try {
- mBinder.fastForward();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling fastForward.", e);
- }
- }
-
- /**
- * Skip to the next item.
- */
- public void next() {
- try {
- mBinder.next();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling next.", e);
- }
- }
-
- /**
- * Start rewinding. If playback is already rewinding this may increase the
- * rate.
- */
- public void rewind() {
- try {
- mBinder.rewind();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling rewind.", e);
- }
- }
-
- /**
- * Skip to the previous item.
- */
- public void previous() {
- try {
- mBinder.previous();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling previous.", e);
- }
- }
-
- /**
- * Rate the current content. This will cause the rating to be set for the
- * current user. The Rating type must match the type returned by
- * {@link #getRatingType()}.
- *
- * @param rating The rating to set for the current content
- */
- public void rate(Rating rating) {
- try {
- mBinder.rate(rating);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling rate.", e);
- }
- }
-
- /**
- * Get the rating type supported by the session. One of:
- * <ul>
- * <li>{@link Rating#RATING_NONE}</li>
- * <li>{@link Rating#RATING_HEART}</li>
- * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
- * <li>{@link Rating#RATING_3_STARS}</li>
- * <li>{@link Rating#RATING_4_STARS}</li>
- * <li>{@link Rating#RATING_5_STARS}</li>
- * <li>{@link Rating#RATING_PERCENTAGE}</li>
- * </ul>
- *
- * @return The supported rating type
- */
- public int getRatingType() {
- try {
- return mBinder.getRatingType();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getRatingType.", e);
- return Rating.RATING_NONE;
- }
- }
-
- /**
- * Get the current playback state for this session.
- *
- * @return The current PlaybackState or null
- */
- public PlaybackState getPlaybackState() {
- try {
- return mBinder.getPlaybackState();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getPlaybackState.", e);
- return null;
- }
- }
-
- /**
- * Get the current metadata for this session.
- *
- * @return The current MediaMetadata or null.
- */
- public MediaMetadata getMetadata() {
- try {
- return mBinder.getMetadata();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getMetadata.", e);
- return null;
- }
- }
-
- /**
- * @hide
- */
- public final void postPlaybackStateChanged(PlaybackState state) {
- synchronized (mLock) {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE, state);
- }
- }
- }
-
- /**
- * @hide
- */
- public final void postMetadataChanged(MediaMetadata metadata) {
- synchronized (mLock) {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).post(MessageHandler.MSG_UPDATE_METADATA,
- metadata);
- }
- }
- }
-
- private MessageHandler getHandlerForListenerLocked(TransportStateListener listener) {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- MessageHandler handler = mListeners.get(i);
- if (listener == handler.mListener) {
- return handler;
- }
- }
- return null;
- }
-
- private boolean removeStateListenerLocked(TransportStateListener listener) {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- if (listener == mListeners.get(i).mListener) {
- mListeners.remove(i);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Register using {@link #addStateListener} to receive updates when there
- * are playback changes on the session.
- */
- public static abstract class TransportStateListener {
- private MessageHandler mHandler;
- /**
- * Override to handle changes in playback state.
- *
- * @param state The new playback state of the session
- */
- public void onPlaybackStateChanged(PlaybackState state) {
- }
-
- /**
- * Override to handle changes to the current metadata.
- *
- * @see MediaMetadata
- * @param metadata The current metadata for the session or null
- */
- public void onMetadataChanged(MediaMetadata metadata) {
- }
-
- private void setHandler(Handler handler) {
- mHandler = new MessageHandler(handler.getLooper(), this);
- }
- }
-
- private static class MessageHandler extends Handler {
- private static final int MSG_UPDATE_PLAYBACK_STATE = 1;
- private static final int MSG_UPDATE_METADATA = 2;
-
- private TransportStateListener mListener;
-
- public MessageHandler(Looper looper, TransportStateListener cb) {
- super(looper, null, true);
- mListener = cb;
- }
-
- public void post(int msg, Object obj) {
- obtainMessage(msg, obj).sendToTarget();
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_PLAYBACK_STATE:
- mListener.onPlaybackStateChanged((PlaybackState) msg.obj);
- break;
- case MSG_UPDATE_METADATA:
- mListener.onMetadataChanged((MediaMetadata) msg.obj);
- break;
- }
- }
- }
-
-}
diff --git a/media/java/android/media/session/TransportPerformer.java b/media/java/android/media/session/TransportPerformer.java
deleted file mode 100644
index 1588d8f..0000000
--- a/media/java/android/media/session/TransportPerformer.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * 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.
- */
-package android.media.session;
-
-import android.media.AudioManager;
-import android.media.MediaMetadata;
-import android.media.Rating;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-
-/**
- * Allows broadcasting of playback changes.
- */
-public final class TransportPerformer {
- private static final String TAG = "TransportPerformer";
- private final Object mLock = new Object();
- private final ArrayList<MessageHandler> mListeners = new ArrayList<MessageHandler>();
-
- private ISession mBinder;
-
- /**
- * @hide
- */
- public TransportPerformer(ISession binder) {
- mBinder = binder;
- }
-
- /**
- * Add a listener to receive updates on.
- *
- * @param listener The callback object
- */
- public void addListener(Listener listener) {
- addListener(listener, null);
- }
-
- /**
- * Add a listener to receive updates on. The updates will be posted to the
- * specified handler. If no handler is provided they will be posted to the
- * caller's thread.
- *
- * @param listener The listener to receive updates on
- * @param handler The handler to post the updates on
- */
- public void addListener(Listener listener, Handler handler) {
- if (listener == null) {
- throw new IllegalArgumentException("Listener cannot be null");
- }
- synchronized (mLock) {
- if (getHandlerForListenerLocked(listener) != null) {
- Log.w(TAG, "Listener is already added, ignoring");
- }
- if (handler == null) {
- handler = new Handler();
- }
- MessageHandler msgHandler = new MessageHandler(handler.getLooper(), listener);
- mListeners.add(msgHandler);
- }
- }
-
- /**
- * Stop receiving updates on the specified handler. If an update has already
- * been posted you may still receive it after this call returns.
- *
- * @param listener The listener to stop receiving updates on
- */
- public void removeListener(Listener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("Listener cannot be null");
- }
- synchronized (mLock) {
- removeListenerLocked(listener);
- }
- }
-
- /**
- * Update the current playback state.
- *
- * @param state The current state of playback
- */
- public final void setPlaybackState(PlaybackState state) {
- try {
- mBinder.setPlaybackState(state);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Dead object in setPlaybackState.", e);
- }
- }
-
- /**
- * Update the current metadata. New metadata can be created using
- * {@link MediaMetadata.Builder}.
- *
- * @param metadata The new metadata
- */
- public final void setMetadata(MediaMetadata metadata) {
- try {
- mBinder.setMetadata(metadata);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Dead object in setPlaybackState.", e);
- }
- }
-
- /**
- * @hide
- */
- public final void onPlay() {
- post(MessageHandler.MESSAGE_PLAY);
- }
-
- /**
- * @hide
- */
- public final void onPause() {
- post(MessageHandler.MESSAGE_PAUSE);
- }
-
- /**
- * @hide
- */
- public final void onStop() {
- post(MessageHandler.MESSAGE_STOP);
- }
-
- /**
- * @hide
- */
- public final void onNext() {
- post(MessageHandler.MESSAGE_NEXT);
- }
-
- /**
- * @hide
- */
- public final void onPrevious() {
- post(MessageHandler.MESSAGE_PREVIOUS);
- }
-
- /**
- * @hide
- */
- public final void onFastForward() {
- post(MessageHandler.MESSAGE_FAST_FORWARD);
- }
-
- /**
- * @hide
- */
- public final void onRewind() {
- post(MessageHandler.MESSAGE_REWIND);
- }
-
- /**
- * @hide
- */
- public final void onSeekTo(long pos) {
- post(MessageHandler.MESSAGE_SEEK_TO, pos);
- }
-
- /**
- * @hide
- */
- public final void onRate(Rating rating) {
- post(MessageHandler.MESSAGE_RATE, rating);
- }
-
- private MessageHandler getHandlerForListenerLocked(Listener listener) {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- MessageHandler handler = mListeners.get(i);
- if (listener == handler.mListener) {
- return handler;
- }
- }
- return null;
- }
-
- private boolean removeListenerLocked(Listener listener) {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- if (listener == mListeners.get(i).mListener) {
- mListeners.remove(i);
- return true;
- }
- }
- return false;
- }
-
- private void post(int what, Object obj) {
- synchronized (mLock) {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).post(what, obj);
- }
- }
- }
-
- private void post(int what) {
- post(what, null);
- }
-
- /**
- * Extend Listener to handle transport controls. Listeners can be registered
- * using {@link #addListener}.
- */
- public static abstract class Listener {
-
- /**
- * Override to handle requests to begin playback.
- */
- public void onPlay() {
- }
-
- /**
- * Override to handle requests to pause playback.
- */
- public void onPause() {
- }
-
- /**
- * Override to handle requests to skip to the next media item.
- */
- public void onNext() {
- }
-
- /**
- * Override to handle requests to skip to the previous media item.
- */
- public void onPrevious() {
- }
-
- /**
- * Override to handle requests to fast forward.
- */
- public void onFastForward() {
- }
-
- /**
- * Override to handle requests to rewind.
- */
- public void onRewind() {
- }
-
- /**
- * Override to handle requests to stop playback.
- */
- public void onStop() {
- }
-
- /**
- * Override to handle requests to seek to a specific position in ms.
- *
- * @param pos New position to move to, in milliseconds.
- */
- public void onSeekTo(long pos) {
- }
-
- /**
- * Override to handle the item being rated.
- *
- * @param rating
- */
- public void onRate(Rating rating) {
- }
-
- /**
- * Report that audio focus has changed on the app. This only happens if
- * you have indicated you have started playing with
- * {@link #setPlaybackState}.
- *
- * @param focusChange The type of focus change, TBD.
- */
- public void onRouteFocusChange(int focusChange) {
- }
- }
-
- private class MessageHandler extends Handler {
- private static final int MESSAGE_PLAY = 1;
- private static final int MESSAGE_PAUSE = 2;
- private static final int MESSAGE_STOP = 3;
- private static final int MESSAGE_NEXT = 4;
- private static final int MESSAGE_PREVIOUS = 5;
- private static final int MESSAGE_FAST_FORWARD = 6;
- private static final int MESSAGE_REWIND = 7;
- private static final int MESSAGE_SEEK_TO = 8;
- private static final int MESSAGE_RATE = 9;
-
- private Listener mListener;
-
- public MessageHandler(Looper looper, Listener cb) {
- super(looper);
- mListener = cb;
- }
-
- public void post(int what, Object obj) {
- obtainMessage(what, obj).sendToTarget();
- }
-
- public void post(int what) {
- post(what, null);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_PLAY:
- mListener.onPlay();
- break;
- case MESSAGE_PAUSE:
- mListener.onPause();
- break;
- case MESSAGE_STOP:
- mListener.onStop();
- break;
- case MESSAGE_NEXT:
- mListener.onNext();
- break;
- case MESSAGE_PREVIOUS:
- mListener.onPrevious();
- break;
- case MESSAGE_FAST_FORWARD:
- mListener.onFastForward();
- break;
- case MESSAGE_REWIND:
- mListener.onRewind();
- break;
- case MESSAGE_SEEK_TO:
- mListener.onSeekTo((Long) msg.obj);
- break;
- case MESSAGE_RATE:
- mListener.onRate((Rating) msg.obj);
- break;
- }
- }
- }
-}
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index d781336..19b54a6 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -433,16 +433,14 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
case MTP_TYPE_STR:
{
jstring stringValue = (jstring)env->GetObjectArrayElement(stringValuesArray, 0);
+ const char* str = (stringValue ? env->GetStringUTFChars(stringValue, NULL) : NULL);
if (stringValue) {
- const char* str = env->GetStringUTFChars(stringValue, NULL);
- if (str == NULL) {
- return MTP_RESPONSE_GENERAL_ERROR;
- }
packet.putString(str);
env->ReleaseStringUTFChars(stringValue, str);
} else {
packet.putEmptyString();
}
+ env->DeleteLocalRef(stringValue);
break;
}
default:
@@ -515,7 +513,7 @@ MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
break;
}
default:
- ALOGE("unsupported type in getObjectPropertyValue\n");
+ ALOGE("unsupported type in setObjectPropertyValue\n");
return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
}
diff --git a/media/lib/signer/Android.mk b/media/lib/signer/Android.mk
index bca643a..b0d3177 100644
--- a/media/lib/signer/Android.mk
+++ b/media/lib/signer/Android.mk
@@ -23,7 +23,8 @@ LOCAL_MODULE:= com.android.mediadrm.signer
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
- $(call all-java-files-under, java)
+ $(call all-java-files-under, java) \
+ $(call all-aidl-files-under, java)
include $(BUILD_JAVA_LIBRARY)
diff --git a/media/lib/signer/com.android.mediadrm.signer.xml b/media/lib/signer/com.android.mediadrm.signer.xml
index b5b1f09..fd3a115 100644
--- a/media/lib/signer/com.android.mediadrm.signer.xml
+++ b/media/lib/signer/com.android.mediadrm.signer.xml
@@ -15,6 +15,6 @@
-->
<permissions>
- <library name="com.android.media.drm.signer"
- file="/system/framework/com.android.media.drm.signer.jar" />
+ <library name="com.android.mediadrm.signer"
+ file="/system/framework/com.android.mediadrm.signer.jar" />
</permissions>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index afc2931..ef06d2c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -18,6 +18,7 @@ package com.android.mediaframeworktest.unit;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
+import android.util.Pair;
import android.util.Range;
import android.util.Rational;
import android.util.SizeF;
@@ -790,6 +791,22 @@ public class CameraMetadataTest extends junit.framework.TestCase {
}
@SmallTest
+ public void testReadWritePair() {
+ // float x 2
+ checkKeyMarshal("android.lens.focusRange",
+ new TypeReference<Pair<Float, Float>>() {{ }},
+ Pair.create(1.0f / 2.0f, 1.0f / 3.0f),
+ toByteArray(1.0f / 2.0f, 1.0f / 3.0f));
+
+ // byte, int (fake from TYPE_BYTE)
+ // This takes advantage of the TYPE_BYTE -> int marshaler designed for enums.
+ checkKeyMarshal("android.flash.mode",
+ new TypeReference<Pair<Byte, Integer>>() {{ }},
+ Pair.create((byte)123, 22),
+ toByteArray((byte)123, (byte)22));
+ }
+
+ @SmallTest
public void testReadWriteRange() {
// int32 x 2
checkKeyMarshal("android.control.aeTargetFpsRange",
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 36c1d5c..ec87c6e 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -88,7 +88,7 @@ public class DefaultContainerService extends IntentService {
private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
/**
* Creates a new container and copies resource there.
- * @param paackageURI the uri of resource to be copied. Can be either
+ * @param packageURI the uri of resource to be copied. Can be either
* a content uri or a file uri
* @param cid the id of the secure container that should
* be used for creating a secure container into which the resource
@@ -101,13 +101,13 @@ public class DefaultContainerService extends IntentService {
*/
public String copyResourceToContainer(final Uri packageURI, final String cid,
final String key, final String resFileName, final String publicResFileName,
- boolean isExternal, boolean isForwardLocked) {
+ boolean isExternal, boolean isForwardLocked, String abiOverride) {
if (packageURI == null || cid == null) {
return null;
}
return copyResourceInner(packageURI, cid, key, resFileName, publicResFileName,
- isExternal, isForwardLocked);
+ isExternal, isForwardLocked, abiOverride);
}
/**
@@ -153,13 +153,12 @@ public class DefaultContainerService extends IntentService {
/**
* Determine the recommended install location for package
* specified by file uri location.
- * @param fileUri the uri of resource to be copied. Should be a
- * file uri
+ *
* @return Returns PackageInfoLite object containing
* the package info and recommended app location.
*/
public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags,
- long threshold) {
+ long threshold, String abiOverride) {
PackageInfoLite ret = new PackageInfoLite();
if (packagePath == null) {
@@ -191,7 +190,7 @@ public class DefaultContainerService extends IntentService {
ret.verifiers = pkg.verifiers;
ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation,
- packagePath, flags, threshold);
+ packagePath, flags, threshold, abiOverride);
return ret;
}
@@ -208,11 +207,11 @@ public class DefaultContainerService extends IntentService {
}
@Override
- public boolean checkExternalFreeStorage(Uri packageUri, boolean isForwardLocked)
- throws RemoteException {
+ public boolean checkExternalFreeStorage(Uri packageUri, boolean isForwardLocked,
+ String abiOverride) throws RemoteException {
final File apkFile = new File(packageUri.getPath());
try {
- return isUnderExternalThreshold(apkFile, isForwardLocked);
+ return isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride);
} catch (IOException e) {
return true;
}
@@ -265,11 +264,11 @@ public class DefaultContainerService extends IntentService {
}
@Override
- public long calculateInstalledSize(String packagePath, boolean isForwardLocked)
- throws RemoteException {
+ public long calculateInstalledSize(String packagePath, boolean isForwardLocked,
+ String abiOverride) throws RemoteException {
final File packageFile = new File(packagePath);
try {
- return calculateContainerSize(packageFile, isForwardLocked) * 1024 * 1024;
+ return calculateContainerSize(packageFile, isForwardLocked, abiOverride) * 1024 * 1024;
} catch (IOException e) {
/*
* Okay, something failed, so let's just estimate it to be 2x
@@ -328,7 +327,8 @@ public class DefaultContainerService extends IntentService {
}
private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName,
- String publicResFileName, boolean isExternal, boolean isForwardLocked) {
+ String publicResFileName, boolean isExternal, boolean isForwardLocked,
+ String abiOverride) {
if (isExternal) {
// Make sure the sdcard is mounted.
@@ -343,7 +343,22 @@ public class DefaultContainerService extends IntentService {
String codePath = packageURI.getPath();
File codeFile = new File(codePath);
NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codePath);
- final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
+ String[] abiList = Build.SUPPORTED_ABIS;
+ if (abiOverride != null) {
+ abiList = new String[] { abiOverride };
+ } else {
+ try {
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
+ NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ }
+ } catch (IOException ioe) {
+ Slog.w(TAG, "Problem determining ABI for: " + codeFile.getPath());
+ return null;
+ }
+ }
+
+ final int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList);
// Calculate size of container needed to hold base APK.
final int sizeMb;
@@ -414,7 +429,7 @@ public class DefaultContainerService extends IntentService {
int ret = PackageManager.INSTALL_SUCCEEDED;
if (abi >= 0) {
ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
- sharedLibraryDir, Build.SUPPORTED_ABIS[abi]);
+ sharedLibraryDir, abiList[abi]);
} else if (abi != PackageManager.NO_NATIVE_LIBRARIES) {
ret = abi;
}
@@ -672,7 +687,7 @@ public class DefaultContainerService extends IntentService {
private static final int PREFER_EXTERNAL = 2;
private int recommendAppInstallLocation(int installLocation, String archiveFilePath, int flags,
- long threshold) {
+ long threshold, String abiOverride) {
int prefer;
boolean checkBoth = false;
@@ -741,7 +756,7 @@ public class DefaultContainerService extends IntentService {
boolean fitsOnSd = false;
if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) {
try {
- fitsOnSd = isUnderExternalThreshold(apkFile, isForwardLocked);
+ fitsOnSd = isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride);
} catch (IOException e) {
return PackageHelper.RECOMMEND_FAILED_INVALID_URI;
}
@@ -812,13 +827,13 @@ public class DefaultContainerService extends IntentService {
* @return true if file fits
* @throws IOException when file does not exist
*/
- private boolean isUnderExternalThreshold(File apkFile, boolean isForwardLocked)
+ private boolean isUnderExternalThreshold(File apkFile, boolean isForwardLocked, String abiOverride)
throws IOException {
if (Environment.isExternalStorageEmulated()) {
return false;
}
- final int sizeMb = calculateContainerSize(apkFile, isForwardLocked);
+ final int sizeMb = calculateContainerSize(apkFile, isForwardLocked, abiOverride);
final int availSdMb;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
@@ -832,9 +847,11 @@ public class DefaultContainerService extends IntentService {
return availSdMb > sizeMb;
}
- private int calculateContainerSize(File apkFile, boolean forwardLocked) throws IOException {
+ private int calculateContainerSize(File apkFile, boolean forwardLocked,
+ String abiOverride) throws IOException {
NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(apkFile);
- final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
+ final int abi = NativeLibraryHelper.findSupportedAbi(handle,
+ (abiOverride != null) ? new String[] { abiOverride } : Build.SUPPORTED_ABIS);
try {
return calculateContainerSize(handle, apkFile, abi, forwardLocked);
diff --git a/packages/Keyguard/Android.mk b/packages/Keyguard/Android.mk
index 1be44f9..96ed2e7 100644
--- a/packages/Keyguard/Android.mk
+++ b/packages/Keyguard/Android.mk
@@ -16,8 +16,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-subdir-Iaidl-files) \
- $(call all-proto-files-under,src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-subdir-Iaidl-files)
LOCAL_MODULE := Keyguard
@@ -27,9 +26,6 @@ LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
-
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index 01d9ab3..e9cdfcd 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -162,5 +162,5 @@
<dimen name="big_font_size">120dp</dimen>
<!-- The y translation to apply at the start in appear animations. -->
- <dimen name="appear_y_translation_start">24dp</dimen>
+ <dimen name="appear_y_translation_start">32dp</dimen>
</resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationCreator.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationCreator.java
new file mode 100644
index 0000000..0d30ea6
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/AppearAnimationCreator.java
@@ -0,0 +1,29 @@
+/*
+ * 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
+ */
+
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.view.animation.Interpolator;
+
+/**
+ * An interface which can create animations when starting an appear animation with
+ * {@link com.android.keyguard.AppearAnimationUtils}
+ */
+public interface AppearAnimationCreator<T> {
+ void createAnimation(T animatedObject, long delay, long duration,
+ float startTranslationY, Interpolator interpolator, Runnable finishListener);
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
index ea896d5..6bb1f2c 100644
--- a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
+++ b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
@@ -16,84 +16,124 @@
package com.android.keyguard;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
import android.content.Context;
import android.view.View;
-import android.view.ViewPropertyAnimator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
/**
* A class to make nice appear transitions for views in a tabular layout.
*/
-public class AppearAnimationUtils {
+public class AppearAnimationUtils implements AppearAnimationCreator<View> {
public static final long APPEAR_DURATION = 220;
private final Interpolator mLinearOutSlowIn;
private final float mStartTranslation;
+ private final AppearAnimationProperties mProperties = new AppearAnimationProperties();
+ private final float mDelayScale;
public AppearAnimationUtils(Context ctx) {
+ this(ctx, 1.0f, 1.0f);
+ }
+
+ public AppearAnimationUtils(Context ctx, float delayScaleFactor,
+ float translationScaleFactor) {
mLinearOutSlowIn = AnimationUtils.loadInterpolator(
ctx, android.R.interpolator.linear_out_slow_in);
- mStartTranslation =
- ctx.getResources().getDimensionPixelOffset(R.dimen.appear_y_translation_start);
+ mStartTranslation = ctx.getResources().getDimensionPixelOffset(
+ R.dimen.appear_y_translation_start) * translationScaleFactor;
+ mDelayScale = delayScaleFactor;
}
- public void startAppearAnimation(View[][] views, final Runnable finishListener) {
- long maxDelay = 0;
- ViewPropertyAnimator maxDelayAnimator = null;
- for (int row = 0; row < views.length; row++) {
- View[] columns = views[row];
+ public void startAppearAnimation(View[][] objects, final Runnable finishListener) {
+ startAppearAnimation(objects, finishListener, this);
+ }
+
+ public <T> void startAppearAnimation(T[][] objects, final Runnable finishListener,
+ AppearAnimationCreator<T> creator) {
+ AppearAnimationProperties properties = getDelays(objects);
+ startAnimations(properties, objects, finishListener, creator);
+ }
+
+ private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects,
+ final Runnable finishListener, AppearAnimationCreator creator) {;
+ if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
+ finishListener.run();
+ return;
+ }
+ for (int row = 0; row < properties.delays.length; row++) {
+ long[] columns = properties.delays[row];
for (int col = 0; col < columns.length; col++) {
- long delay = calculateDelay(row, col);
- ViewPropertyAnimator animator = startAppearAnimation(columns[col], delay);
- if (animator != null && delay > maxDelay) {
- maxDelay = delay;
- maxDelayAnimator = animator;
+ long delay = columns[col];
+ Runnable endRunnable = null;
+ if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) {
+ endRunnable = finishListener;
}
+ creator.createAnimation(objects[row][col], delay, APPEAR_DURATION,
+ mStartTranslation, mLinearOutSlowIn, endRunnable);
}
}
- if (maxDelayAnimator != null) {
- maxDelayAnimator.setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- finishListener.run();
- }
- });
- } else {
- finishListener.run();
- }
+
}
- private ViewPropertyAnimator startAppearAnimation(View view, long delay) {
- if (view == null) return null;
- view.setAlpha(0f);
- view.setTranslationY(mStartTranslation);
- view.animate()
- .alpha(1f)
- .translationY(0)
- .setInterpolator(mLinearOutSlowIn)
- .setDuration(APPEAR_DURATION)
- .setStartDelay(delay)
- .setListener(null);
- if (view.hasOverlappingRendering()) {
- view.animate().withLayer();
+ private <T> AppearAnimationProperties getDelays(T[][] items) {
+ long maxDelay = 0;
+ mProperties.maxDelayColIndex = -1;
+ mProperties.maxDelayRowIndex = -1;
+ mProperties.delays = new long[items.length][];
+ for (int row = 0; row < items.length; row++) {
+ T[] columns = items[row];
+ mProperties.delays[row] = new long[columns.length];
+ for (int col = 0; col < columns.length; col++) {
+ long delay = calculateDelay(row, col);
+ mProperties.delays[row][col] = delay;
+ if (items[row][col] != null && delay > maxDelay) {
+ maxDelay = delay;
+ mProperties.maxDelayColIndex = col;
+ mProperties.maxDelayRowIndex = row;
+ }
+ }
}
- return view.animate();
+ return mProperties;
}
private long calculateDelay(int row, int col) {
- return (long) (row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20);
+ return (long) ((row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20) * mDelayScale);
}
- public TimeInterpolator getInterpolator() {
+ public Interpolator getInterpolator() {
return mLinearOutSlowIn;
}
public float getStartTranslation() {
return mStartTranslation;
}
+
+ @Override
+ public void createAnimation(View view, long delay, long duration, float startTranslationY,
+ Interpolator interpolator, Runnable endRunnable) {
+ if (view != null) {
+ view.setAlpha(0f);
+ view.setTranslationY(startTranslationY);
+ view.animate()
+ .alpha(1f)
+ .translationY(0)
+ .setInterpolator(interpolator)
+ .setDuration(duration)
+ .setStartDelay(delay);
+ if (view.hasOverlappingRendering()) {
+ view.animate().withLayer();
+ }
+ if (endRunnable != null) {
+ view.animate().withEndAction(endRunnable);
+ }
+ }
+ }
+
+ public class AppearAnimationProperties {
+ public long[][] delays;
+ public int maxDelayRowIndex;
+ public int maxDelayColIndex;
+ }
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 5853ff9..e6de72f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -21,6 +21,9 @@ import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -28,10 +31,13 @@ import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Interpolator;
import android.widget.Button;
import android.widget.LinearLayout;
@@ -41,7 +47,8 @@ import com.android.internal.widget.LockPatternView;
import java.io.IOException;
import java.util.List;
-public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView {
+public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView,
+ AppearAnimationCreator<LockPatternView.CellState> {
private static final String TAG = "SecurityPatternView";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -59,6 +66,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final AppearAnimationUtils mAppearAnimationUtils;
private CountDownTimer mCountdownTimer = null;
private LockPatternUtils mLockPatternUtils;
@@ -87,6 +95,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
private SecurityMessageDisplay mSecurityMessageDisplay;
private View mEcaView;
private Drawable mBouncerFrame;
+ private ViewGroup mKeyguardBouncerFrame;
+ private KeyguardMessageArea mHelpMessage;
enum FooterMode {
Normal,
@@ -101,6 +111,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
public KeyguardPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ mAppearAnimationUtils = new AppearAnimationUtils(context, 1.5f /* delayScale */,
+ 2.0f /* transitionScale */);
}
public void setKeyguardCallback(KeyguardSecurityCallback callback) {
@@ -148,6 +160,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
if (bouncerFrameView != null) {
mBouncerFrame = bouncerFrameView.getBackground();
}
+
+ mKeyguardBouncerFrame = (ViewGroup) findViewById(R.id.keyguard_bouncer_frame);
+ mHelpMessage = (KeyguardMessageArea) findViewById(R.id.keyguard_message_area);
}
private void updateFooter(FooterMode mode) {
@@ -403,8 +418,69 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
@Override
public void startAppearAnimation() {
- // TODO: Fancy animation.
- setAlpha(0);
- animate().alpha(1).withLayer().setDuration(200);
+ enableClipping(false);
+ mAppearAnimationUtils.startAppearAnimation(
+ mLockPatternView.getCellStates(),
+ new Runnable() {
+ @Override
+ public void run() {
+ enableClipping(true);
+ }
+ },
+ this);
+ if (!TextUtils.isEmpty(mHelpMessage.getText())) {
+ mAppearAnimationUtils.createAnimation(mHelpMessage, 0,
+ AppearAnimationUtils.APPEAR_DURATION,
+ mAppearAnimationUtils.getStartTranslation(),
+ mAppearAnimationUtils.getInterpolator(),
+ null /* finishRunnable */);
+ }
+ }
+
+ private void enableClipping(boolean enable) {
+ setClipChildren(enable);
+ mKeyguardBouncerFrame.setClipToPadding(enable);
+ mKeyguardBouncerFrame.setClipChildren(enable);
+ }
+
+ @Override
+ public void createAnimation(final LockPatternView.CellState animatedCell, long delay,
+ long duration, float startTranslationY, Interpolator interpolator,
+ final Runnable finishListener) {
+ animatedCell.scale = 0.0f;
+ animatedCell.translateY = startTranslationY;
+ ValueAnimator animator = ValueAnimator.ofFloat(startTranslationY, 0.0f);
+ animator.setInterpolator(interpolator);
+ animator.setDuration(duration);
+ animator.setStartDelay(delay);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float animatedFraction = animation.getAnimatedFraction();
+ animatedCell.scale = animatedFraction;
+ animatedCell.translateY = (float) animation.getAnimatedValue();
+ mLockPatternView.invalidate();
+ }
+ });
+ if (finishListener != null) {
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishListener.run();
+ }
+ });
+
+ // Also animate the Emergency call
+ mAppearAnimationUtils.createAnimation(mEcaView, delay, duration, startTranslationY,
+ interpolator, null);
+
+ // And the forgot pattern button
+ if (mForgotPatternButton.getVisibility() == View.VISIBLE) {
+ mAppearAnimationUtils.createAnimation(mForgotPatternButton, delay, duration,
+ startTranslationY, interpolator, null);
+ }
+ }
+ animator.start();
+ mLockPatternView.invalidate();
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java b/packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java
deleted file mode 100644
index 20af2f1..0000000
--- a/packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * 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
- */
-
-package com.android.keyguard.analytics;
-
-import com.google.protobuf.nano.CodedOutputByteBufferNano;
-import com.google.protobuf.nano.MessageNano;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.os.AsyncTask;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * Tracks sessions, touch and sensor events in Keyguard.
- *
- * A session starts when the user is presented with the Keyguard and ends when the Keyguard is no
- * longer visible to the user.
- */
-public class KeyguardAnalytics implements SensorEventListener {
-
- private static final boolean DEBUG = false;
- private static final String TAG = "KeyguardAnalytics";
- private static final long TIMEOUT_MILLIS = 11000; // 11 seconds.
-
- private static final int[] SENSORS = new int[] {
- Sensor.TYPE_ACCELEROMETER,
- Sensor.TYPE_GYROSCOPE,
- Sensor.TYPE_PROXIMITY,
- Sensor.TYPE_LIGHT,
- Sensor.TYPE_ROTATION_VECTOR,
- };
-
- private Session mCurrentSession = null;
- // Err on the side of caution, so logging is not started after a crash even tough the screen
- // is off.
- private boolean mScreenOn = false;
- private boolean mHidden = false;
-
- private final SensorManager mSensorManager;
- private final SessionTypeAdapter mSessionTypeAdapter;
- private final File mAnalyticsFile;
-
- public KeyguardAnalytics(Context context, SessionTypeAdapter sessionTypeAdapter,
- File analyticsFile) {
- mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
- mSessionTypeAdapter = sessionTypeAdapter;
- mAnalyticsFile = analyticsFile;
- }
-
- public Callback getCallback() {
- return mCallback;
- }
-
- public interface Callback {
- public void onShow();
- public void onHide();
- public void onScreenOn();
- public void onScreenOff();
- public boolean onTouchEvent(MotionEvent ev, int width, int height);
- public void onSetOccluded(boolean hidden);
- }
-
- public interface SessionTypeAdapter {
- public int getSessionType();
- }
-
- private void sessionEntrypoint() {
- if (mCurrentSession == null && mScreenOn && !mHidden) {
- onSessionStart();
- }
- }
-
- private void sessionExitpoint(int result) {
- if (mCurrentSession != null) {
- onSessionEnd(result);
- }
- }
-
- private void onSessionStart() {
- int type = mSessionTypeAdapter.getSessionType();
- mCurrentSession = new Session(System.currentTimeMillis(), System.nanoTime(), type);
- if (type == Session.TYPE_KEYGUARD_SECURE) {
- mCurrentSession.setRedactTouchEvents();
- }
- for (int sensorType : SENSORS) {
- Sensor s = mSensorManager.getDefaultSensor(sensorType);
- if (s != null) {
- mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
- }
- }
- if (DEBUG) {
- Log.d(TAG, "onSessionStart()");
- }
- }
-
- private void onSessionEnd(int result) {
- if (DEBUG) {
- Log.d(TAG, String.format("onSessionEnd(success=%d)", result));
- }
- mSensorManager.unregisterListener(this);
-
- Session session = mCurrentSession;
- mCurrentSession = null;
-
- session.end(System.currentTimeMillis(), result);
- queueSession(session);
- }
-
- private void queueSession(final Session currentSession) {
- if (DEBUG) {
- Log.i(TAG, "Saving session.");
- }
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- try {
- byte[] b = writeDelimitedProto(currentSession.toProto());
- OutputStream os = new FileOutputStream(mAnalyticsFile, true /* append */);
- if (DEBUG) {
- Log.d(TAG, String.format("Serialized size: %d kB.", b.length / 1024));
- }
- try {
- os.write(b);
- os.flush();
- } finally {
- try {
- os.close();
- } catch (IOException e) {
- Log.e(TAG, "Exception while closing file", e);
- }
- }
- } catch (IOException e) {
- Log.e(TAG, "Exception while writing file", e);
- }
- return null;
- }
-
- private byte[] writeDelimitedProto(MessageNano proto)
- throws IOException {
- byte[] result = new byte[CodedOutputByteBufferNano.computeMessageSizeNoTag(proto)];
- CodedOutputByteBufferNano ob = CodedOutputByteBufferNano.newInstance(result);
- ob.writeMessageNoTag(proto);
- ob.checkNoSpaceLeft();
- return result;
- }
- }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
- }
-
- @Override
- public synchronized void onSensorChanged(SensorEvent event) {
- if (false) {
- Log.v(TAG, String.format(
- "onSensorChanged(name=%s, values[0]=%f)",
- event.sensor.getName(), event.values[0]));
- }
- if (mCurrentSession != null) {
- mCurrentSession.addSensorEvent(event, System.nanoTime());
- enforceTimeout();
- }
- }
-
- private void enforceTimeout() {
- if (System.currentTimeMillis() - mCurrentSession.getStartTimestampMillis()
- > TIMEOUT_MILLIS) {
- onSessionEnd(Session.RESULT_UNKNOWN);
- if (DEBUG) {
- Log.i(TAG, "Analytics timed out.");
- }
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- private final Callback mCallback = new Callback() {
- @Override
- public void onShow() {
- if (DEBUG) {
- Log.d(TAG, "onShow()");
- }
- synchronized (KeyguardAnalytics.this) {
- sessionEntrypoint();
- }
- }
-
- @Override
- public void onHide() {
- if (DEBUG) {
- Log.d(TAG, "onHide()");
- }
- synchronized (KeyguardAnalytics.this) {
- sessionExitpoint(Session.RESULT_SUCCESS);
- }
- }
-
- @Override
- public void onScreenOn() {
- if (DEBUG) {
- Log.d(TAG, "onScreenOn()");
- }
- synchronized (KeyguardAnalytics.this) {
- mScreenOn = true;
- sessionEntrypoint();
- }
- }
-
- @Override
- public void onScreenOff() {
- if (DEBUG) {
- Log.d(TAG, "onScreenOff()");
- }
- synchronized (KeyguardAnalytics.this) {
- mScreenOn = false;
- sessionExitpoint(Session.RESULT_FAILURE);
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev, int width, int height) {
- if (DEBUG) {
- Log.v(TAG, "onTouchEvent(ev.action="
- + MotionEvent.actionToString(ev.getAction()) + ")");
- }
- synchronized (KeyguardAnalytics.this) {
- if (mCurrentSession != null) {
- mCurrentSession.addMotionEvent(ev);
- mCurrentSession.setTouchArea(width, height);
- enforceTimeout();
- }
- }
- return true;
- }
-
- @Override
- public void onSetOccluded(boolean hidden) {
- synchronized (KeyguardAnalytics.this) {
- if (hidden != mHidden) {
- if (DEBUG) {
- Log.d(TAG, "onSetOccluded(" + hidden + ")");
- }
- mHidden = hidden;
- if (hidden) {
- // Could have gone to camera on purpose / by falsing or an app could have
- // launched on top of the lockscreen.
- sessionExitpoint(Session.RESULT_UNKNOWN);
- } else {
- sessionEntrypoint();
- }
- }
- }
- }
- };
-
-}
diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java b/packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java
deleted file mode 100644
index e68f751..0000000
--- a/packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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
- */
-
-package com.android.keyguard.analytics;
-
-import android.graphics.RectF;
-import android.util.FloatMath;
-import android.util.SparseArray;
-import android.view.MotionEvent;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static com.android.keyguard.analytics.KeyguardAnalyticsProtos.Session.TouchEvent.BoundingBox;
-
-/**
- * Takes motion events and tracks the length and bounding box of each pointer gesture as well as
- * the bounding box of the whole gesture.
- */
-public class PointerTracker {
- private SparseArray<Pointer> mPointerInfoMap = new SparseArray<Pointer>();
- private RectF mTotalBoundingBox = new RectF();
-
- public void addMotionEvent(MotionEvent ev) {
- if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
- float x = ev.getX();
- float y = ev.getY();
- mTotalBoundingBox.set(x, y, x, y);
- }
- for (int i = 0; i < ev.getPointerCount(); i++) {
- int id = ev.getPointerId(i);
- Pointer pointer = getPointer(id);
- float x = ev.getX(i);
- float y = ev.getY(i);
- boolean down = ev.getActionMasked() == MotionEvent.ACTION_DOWN
- || (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN
- && ev.getActionIndex() == i);
- pointer.addPoint(x, y, down);
- mTotalBoundingBox.union(x, y);
- }
- }
-
- public float getPointerLength(int id) {
- return getPointer(id).length;
- }
-
- public BoundingBox getBoundingBox() {
- return boundingBoxFromRect(mTotalBoundingBox);
- }
-
- public BoundingBox getPointerBoundingBox(int id) {
- return boundingBoxFromRect(getPointer(id).boundingBox);
- }
-
- private BoundingBox boundingBoxFromRect(RectF f) {
- BoundingBox bb = new BoundingBox();
- bb.setHeight(f.height());
- bb.setWidth(f.width());
- return bb;
- }
-
- private Pointer getPointer(int id) {
- Pointer p = mPointerInfoMap.get(id);
- if (p == null) {
- p = new Pointer();
- mPointerInfoMap.put(id, p);
- }
- return p;
- }
-
- private static class Pointer {
- public float length;
- public final RectF boundingBox = new RectF();
-
- private float mLastX;
- private float mLastY;
-
- public void addPoint(float x, float y, boolean down) {
- float deltaX;
- float deltaY;
- if (down) {
- boundingBox.set(x, y, x, y);
- length = 0f;
- deltaX = 0;
- deltaY = 0;
- } else {
- deltaX = x - mLastX;
- deltaY = y - mLastY;
- }
- mLastX = x;
- mLastY = y;
- length += FloatMath.sqrt(deltaX * deltaX + deltaY * deltaY);
- boundingBox.union(x, y);
- }
- }
-}
diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/Session.java b/packages/Keyguard/src/com/android/keyguard/analytics/Session.java
deleted file mode 100644
index 05f9165..0000000
--- a/packages/Keyguard/src/com/android/keyguard/analytics/Session.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * 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
- */
-
-package com.android.keyguard.analytics;
-
-import android.os.Build;
-import android.util.Slog;
-import android.view.MotionEvent;
-
-import java.util.ArrayList;
-
-import static com.android.keyguard.analytics.KeyguardAnalyticsProtos.Session.SensorEvent;
-import static com.android.keyguard.analytics.KeyguardAnalyticsProtos.Session.TouchEvent;
-
-/**
- * Records data about one keyguard session.
- *
- * The recorded data contains start and end of the session, whether it unlocked the device
- * successfully, sensor data and touch data.
- *
- * If the keyguard is secure, the recorded touch data will correlate or contain the user pattern or
- * PIN. If this is not desired, the touch coordinates can be redacted before serialization.
- */
-public class Session {
-
- private static final String TAG = "KeyguardAnalytics";
- private static final boolean DEBUG = false;
-
- /**
- * The user has failed to unlock the device in this session.
- */
- public static final int RESULT_FAILURE = KeyguardAnalyticsProtos.Session.FAILURE;
- /**
- * The user has succeeded in unlocking the device in this session.
- */
- public static final int RESULT_SUCCESS = KeyguardAnalyticsProtos.Session.SUCCESS;
-
- /**
- * It is unknown how the session with the keyguard ended.
- */
- public static final int RESULT_UNKNOWN = KeyguardAnalyticsProtos.Session.UNKNOWN;
-
- /**
- * This session took place on an insecure keyguard.
- */
- public static final int TYPE_KEYGUARD_INSECURE
- = KeyguardAnalyticsProtos.Session.KEYGUARD_INSECURE;
-
- /**
- * This session took place on an secure keyguard.
- */
- public static final int TYPE_KEYGUARD_SECURE
- = KeyguardAnalyticsProtos.Session.KEYGUARD_SECURE;
-
- /**
- * This session took place during a fake wake up of the device.
- */
- public static final int TYPE_RANDOM_WAKEUP = KeyguardAnalyticsProtos.Session.RANDOM_WAKEUP;
-
-
- private final PointerTracker mPointerTracker = new PointerTracker();
-
- private final long mStartTimestampMillis;
- private final long mStartSystemTimeNanos;
- private final int mType;
-
- private boolean mRedactTouchEvents;
- private ArrayList<TouchEvent> mMotionEvents = new ArrayList<TouchEvent>(200);
- private ArrayList<SensorEvent> mSensorEvents = new ArrayList<SensorEvent>(600);
- private int mTouchAreaHeight;
- private int mTouchAreaWidth;
-
- private long mEndTimestampMillis;
- private int mResult;
- private boolean mEnded;
-
- public Session(long startTimestampMillis, long startSystemTimeNanos, int type) {
- mStartTimestampMillis = startTimestampMillis;
- mStartSystemTimeNanos = startSystemTimeNanos;
- mType = type;
- }
-
- public void end(long endTimestampMillis, int result) {
- mEnded = true;
- mEndTimestampMillis = endTimestampMillis;
- mResult = result;
- }
-
- public void addMotionEvent(MotionEvent motionEvent) {
- if (mEnded) {
- return;
- }
- mPointerTracker.addMotionEvent(motionEvent);
- mMotionEvents.add(protoFromMotionEvent(motionEvent));
- }
-
- public void addSensorEvent(android.hardware.SensorEvent eventOrig, long systemTimeNanos) {
- if (mEnded) {
- return;
- }
- SensorEvent event = protoFromSensorEvent(eventOrig, systemTimeNanos);
- mSensorEvents.add(event);
- if (DEBUG) {
- Slog.v(TAG, String.format("addSensorEvent(name=%s, values[0]=%f",
- event.getType(), event.values[0]));
- }
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("Session{");
- sb.append("mType=").append(mType);
- sb.append(", mStartTimestampMillis=").append(mStartTimestampMillis);
- sb.append(", mStartSystemTimeNanos=").append(mStartSystemTimeNanos);
- sb.append(", mEndTimestampMillis=").append(mEndTimestampMillis);
- sb.append(", mResult=").append(mResult);
- sb.append(", mRedactTouchEvents=").append(mRedactTouchEvents);
- sb.append(", mTouchAreaHeight=").append(mTouchAreaHeight);
- sb.append(", mTouchAreaWidth=").append(mTouchAreaWidth);
- sb.append(", mMotionEvents=[size=").append(mMotionEvents.size()).append("]");
- sb.append(", mSensorEvents=[size=").append(mSensorEvents.size()).append("]");
- sb.append('}');
- return sb.toString();
- }
-
- public KeyguardAnalyticsProtos.Session toProto() {
- KeyguardAnalyticsProtos.Session proto = new KeyguardAnalyticsProtos.Session();
- proto.setStartTimestampMillis(mStartTimestampMillis);
- proto.setDurationMillis(mEndTimestampMillis - mStartTimestampMillis);
- proto.setBuild(Build.FINGERPRINT);
- proto.setResult(mResult);
- proto.sensorEvents = mSensorEvents.toArray(proto.sensorEvents);
- proto.touchEvents = mMotionEvents.toArray(proto.touchEvents);
- proto.setTouchAreaWidth(mTouchAreaWidth);
- proto.setTouchAreaHeight(mTouchAreaHeight);
- proto.setType(mType);
- if (mRedactTouchEvents) {
- redactTouchEvents(proto.touchEvents);
- }
- return proto;
- }
-
- private void redactTouchEvents(TouchEvent[] touchEvents) {
- for (int i = 0; i < touchEvents.length; i++) {
- TouchEvent t = touchEvents[i];
- for (int j = 0; j < t.pointers.length; j++) {
- TouchEvent.Pointer p = t.pointers[j];
- p.clearX();
- p.clearY();
- }
- t.setRedacted(true);
- }
- }
-
- private SensorEvent protoFromSensorEvent(android.hardware.SensorEvent ev, long sysTimeNanos) {
- SensorEvent proto = new SensorEvent();
- proto.setType(ev.sensor.getType());
- proto.setTimeOffsetNanos(sysTimeNanos - mStartSystemTimeNanos);
- proto.setTimestamp(ev.timestamp);
- proto.values = ev.values.clone();
- return proto;
- }
-
- private TouchEvent protoFromMotionEvent(MotionEvent ev) {
- int count = ev.getPointerCount();
- TouchEvent proto = new TouchEvent();
- proto.setTimeOffsetNanos(ev.getEventTimeNano() - mStartSystemTimeNanos);
- proto.setAction(ev.getActionMasked());
- proto.setActionIndex(ev.getActionIndex());
- proto.pointers = new TouchEvent.Pointer[count];
- for (int i = 0; i < count; i++) {
- TouchEvent.Pointer p = new TouchEvent.Pointer();
- p.setX(ev.getX(i));
- p.setY(ev.getY(i));
- p.setSize(ev.getSize(i));
- p.setPressure(ev.getPressure(i));
- p.setId(ev.getPointerId(i));
- proto.pointers[i] = p;
- if ((ev.getActionMasked() == MotionEvent.ACTION_POINTER_UP && ev.getActionIndex() == i)
- || ev.getActionMasked() == MotionEvent.ACTION_UP) {
- p.boundingBox = mPointerTracker.getPointerBoundingBox(p.getId());
- p.setLength(mPointerTracker.getPointerLength(p.getId()));
- }
- }
- if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
- proto.boundingBox = mPointerTracker.getBoundingBox();
- }
- return proto;
- }
-
- /**
- * Discards the x / y coordinates of the touch events on serialization. Retained are the
- * size of the individual and overall bounding boxes and the length of each pointer's gesture.
- */
- public void setRedactTouchEvents() {
- mRedactTouchEvents = true;
- }
-
- public void setTouchArea(int width, int height) {
- mTouchAreaWidth = width;
- mTouchAreaHeight = height;
- }
-
- public long getStartTimestampMillis() {
- return mStartTimestampMillis;
- }
-}
diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto b/packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto
deleted file mode 100644
index 68b1590..0000000
--- a/packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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
- */
-
-syntax = "proto2";
-
-package keyguard;
-
-option java_package = "com.android.keyguard.analytics";
-option java_outer_classname = "KeyguardAnalyticsProtos";
-
-message Session {
- message TouchEvent {
- message BoundingBox {
- optional float width = 1;
- optional float height = 2;
- }
-
- enum Action {
- // Keep in sync with MotionEvent.
- DOWN = 0;
- UP = 1;
- MOVE = 2;
- CANCEL = 3;
- OUTSIDE = 4;
- POINTER_DOWN = 5;
- POINTER_UP = 6;
- }
-
- message Pointer {
- optional float x = 1;
- optional float y = 2;
- optional float size = 3;
- optional float pressure = 4;
- optional int32 id = 5;
- optional float length = 6;
- // Bounding box of the pointer. Only set on UP or POINTER_UP event of this pointer.
- optional BoundingBox boundingBox = 7;
- }
-
- optional uint64 timeOffsetNanos = 1;
- optional Action action = 2;
- optional int32 actionIndex = 3;
- repeated Pointer pointers = 4;
- /* If true, the the x / y coordinates of the touch events were redacted. Retained are the
- size of the individual and overall bounding boxes and the length of each pointer's
- gesture. */
- optional bool redacted = 5;
- // Bounding box of the whole gesture. Only set on UP event.
- optional BoundingBox boundingBox = 6;
- }
-
- message SensorEvent {
- enum Type {
- ACCELEROMETER = 1;
- GYROSCOPE = 4;
- LIGHT = 5;
- PROXIMITY = 8;
- ROTATION_VECTOR = 11;
- }
-
- optional Type type = 1;
- optional uint64 timeOffsetNanos = 2;
- repeated float values = 3;
- optional uint64 timestamp = 4;
- }
-
- enum Result {
- FAILURE = 0;
- SUCCESS = 1;
- UNKNOWN = 2;
- }
-
- enum Type {
- KEYGUARD_INSECURE = 0;
- KEYGUARD_SECURE = 1;
- RANDOM_WAKEUP = 2;
- }
-
- optional uint64 startTimestampMillis = 1;
- optional uint64 durationMillis = 2;
- optional string build = 3;
- optional Result result = 4;
- repeated TouchEvent touchEvents = 5;
- repeated SensorEvent sensorEvents = 6;
-
- optional int32 touchAreaWidth = 9;
- optional int32 touchAreaHeight = 10;
- optional Type type = 11;
-}
diff --git a/packages/SystemUI/res/drawable/heads_up_scrim.xml b/packages/SystemUI/res/drawable/heads_up_scrim.xml
new file mode 100644
index 0000000..59000fc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/heads_up_scrim.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:type="linear"
+ android:angle="-90"
+ android:startColor="#55000000"
+ android:endColor="#00000000" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_airplane_off.xml b/packages/SystemUI/res/drawable/ic_qs_airplane_off.xml
index 9f0ec67..c68238f 100644
--- a/packages/SystemUI/res/drawable/ic_qs_airplane_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_airplane_off.xml
@@ -19,17 +19,10 @@ Copyright (C) 2014 The Android Open Source Project
android:height="64dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
- android:fill="#00000000"
- android:stroke="#CCCCCC"
- android:strokeWidth="1.0"
- android:pathData="M10.2,9.0"/>
- <path
- android:fill="#00000000"
- android:stroke="#CCCCCC"
- android:strokeWidth="1.0"
- android:pathData="M21.0,16.0l0.0,-2.0l-8.0,-5.0L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5L10.0,9.0l-8.0,5.0l0.0,2.0l8.0,-2.5L10.0,19.0l-2.0,1.5L8.0,22.0l3.5,-1.0l3.5,1.0l0.0,-1.5L13.0,19.0l0.0,-5.5L21.0,16.0z"/>
+ android:fill="#4DFFFFFF"
+ android:pathData="M26.0,18.0L26.0,7.0c0.0,-1.7 -1.3,-3.0 -3.0,-3.0c-1.7,0.0 -3.0,1.3 -3.0,3.0l0.0,7.4L35.7,30.0l6.3,2.0l0.0,-4.0L26.0,18.0zM6.0,10.5l10.0,10.0L4.0,28.0l0.0,4.0l16.0,-5.0l0.0,11.0l-4.0,3.0l0.0,3.0l7.0,-2.0l7.0,2.0l0.0,-3.0l-4.0,-3.0l0.0,-7.5L37.5,42.0l2.5,-2.5L8.5,8.0L6.0,10.5z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_airplane_on.xml b/packages/SystemUI/res/drawable/ic_qs_airplane_on.xml
index 95c20bb..c1e3c7e 100644
--- a/packages/SystemUI/res/drawable/ic_qs_airplane_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_airplane_on.xml
@@ -19,13 +19,13 @@ Copyright (C) 2014 The Android Open Source Project
android:height="64dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
android:fill="#FFFFFFFF"
- android:pathData="M10.2,9.0"/>
+ android:pathData="M20.4,18.0"/>
<path
android:fill="#FFFFFFFF"
- android:pathData="M21.0,16.0l0.0,-2.0l-8.0,-5.0L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5L10.0,9.0l-8.0,5.0l0.0,2.0l8.0,-2.5L10.0,19.0l-2.0,1.5L8.0,22.0l3.5,-1.0l3.5,1.0l0.0,-1.5L13.0,19.0l0.0,-5.5L21.0,16.0z"/>
+ android:pathData="M42.0,32.0l0.0,-4.0L26.0,18.0L26.0,7.0c0.0,-1.7 -1.3,-3.0 -3.0,-3.0c-1.7,0.0 -3.0,1.3 -3.0,3.0l0.0,11.0L4.0,28.0l0.0,4.0l16.0,-5.0l0.0,11.0l-4.0,3.0l0.0,3.0l7.0,-2.0l7.0,2.0l0.0,-3.0l-4.0,-3.0L26.0,27.0L42.0,32.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
index 61a7777..3957d02 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
@@ -19,10 +19,10 @@ Copyright (C) 2014 The Android Open Source Project
android:height="64dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
android:fill="#FFFFFFFF"
- android:pathData="M17.7,7.7L12.0,2.0l-1.0,0.0l0.0,7.6L6.4,5.0L5.0,6.4l5.6,5.6L5.0,17.6L6.4,19.0l4.6,-4.6L11.0,22.0l1.0,0.0l5.7,-5.7L13.4,12.0L17.7,7.7zM13.0,5.8l1.9,1.9L13.0,9.6L13.0,5.8zM14.9,16.3L13.0,18.2l0.0,-3.8L14.9,16.3z"/>
+ android:pathData="M14.0,24.0l-4.0,-4.0l-4.0,4.0l4.0,4.0L14.0,24.0zM35.4,15.4L24.0,4.0l-2.0,0.0l0.0,15.2L12.8,10.0L10.0,12.8L21.2,24.0L10.0,35.2l2.8,2.8l9.2,-9.2L22.0,44.0l2.0,0.0l11.4,-11.4L26.8,24.0L35.4,15.4zM26.0,11.7l3.8,3.8L26.0,19.2L26.0,11.7zM29.8,32.6L26.0,36.3l0.0,-7.5L29.8,32.6zM38.0,20.0l-4.0,4.0l4.0,4.0l4.0,-4.0L38.0,20.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_zen_off.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connecting.xml
index 88a2c3a..e4038f9 100644
--- a/packages/SystemUI/res/drawable/ic_qs_zen_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connecting.xml
@@ -19,10 +19,10 @@ Copyright (C) 2014 The Android Open Source Project
android:height="64dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
- android:fill="#4DFFFFFF"
- android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM4.0,12.0c0.0,-4.4 3.6,-8.0 8.0,-8.0c1.8,0.0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4.0,13.8 4.0,12.0zM12.0,20.0c-1.8,0.0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20.0,10.2 20.0,12.0C20.0,16.4 16.4,20.0 12.0,20.0z"/>
+ android:fill="#FFFFFFFF"
+ android:pathData="M28.5,24.0l4.6,4.6c0.6,-1.4 0.9,-3.0 0.9,-4.7c0.0,-1.6 -0.3,-3.2 -0.9,-4.6L28.5,24.0zM39.1,13.4L36.5,16.0c1.3,2.4 2.0,5.1 2.0,8.0s-0.7,5.6 -2.0,8.0l2.4,2.4c1.9,-3.1 3.1,-6.7 3.1,-10.6C42.0,20.0 40.9,16.5 39.1,13.4zM31.4,15.4L20.0,4.0l-2.0,0.0l0.0,15.2L8.8,10.0L6.0,12.8L17.2,24.0L6.0,35.2L8.8,38.0l9.2,-9.2L18.0,44.0l2.0,0.0l11.4,-11.4L22.8,24.0L31.4,15.4zM22.0,11.7l3.8,3.8L22.0,19.2L22.0,11.7zM25.8,32.6L22.0,36.3l0.0,-7.5L25.8,32.6z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml
index 7ac1cb9..00c5af8 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml
@@ -19,12 +19,10 @@ Copyright (C) 2014 The Android Open Source Project
android:height="64dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
- android:fill="#00000000"
- android:stroke="#CCCCCC"
- android:strokeWidth="1.0"
- android:pathData="M17.7,7.7L12.0,2.0l-1.0,0.0l0.0,7.6L6.4,5.0L5.0,6.4l5.6,5.6L5.0,17.6L6.4,19.0l4.6,-4.6L11.0,22.0l1.0,0.0l5.7,-5.7L13.4,12.0L17.7,7.7zM13.0,5.8l1.9,1.9L13.0,9.6L13.0,5.8zM14.9,16.3L13.0,18.2l0.0,-3.8L14.9,16.3z"/>
+ android:fill="#4DFFFFFF"
+ android:pathData="M26.0,11.8l3.8,3.8l-3.2,3.2l2.8,2.8l6.0,-6.0L24.0,4.2l-2.0,0.0l0.0,10.1l4.0,4.0L26.0,11.8zM10.8,8.2L8.0,11.0l13.2,13.2L10.0,35.3l2.8,2.8L22.0,29.0l0.0,15.2l2.0,0.0l8.6,-8.6l4.6,4.6l2.8,-2.8L10.8,8.2zM26.0,36.5L26.0,29.0l3.8,3.8L26.0,36.5z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
index 61a7777..2b14f33 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
@@ -19,10 +19,10 @@ Copyright (C) 2014 The Android Open Source Project
android:height="64dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
android:fill="#FFFFFFFF"
- android:pathData="M17.7,7.7L12.0,2.0l-1.0,0.0l0.0,7.6L6.4,5.0L5.0,6.4l5.6,5.6L5.0,17.6L6.4,19.0l4.6,-4.6L11.0,22.0l1.0,0.0l5.7,-5.7L13.4,12.0L17.7,7.7zM13.0,5.8l1.9,1.9L13.0,9.6L13.0,5.8zM14.9,16.3L13.0,18.2l0.0,-3.8L14.9,16.3z"/>
+ android:pathData="M35.4,15.4L24.0,4.0l-2.0,0.0l0.0,15.2L12.8,10.0L10.0,12.8L21.2,24.0L10.0,35.2l2.8,2.8l9.2,-9.2L22.0,44.0l2.0,0.0l11.4,-11.4L26.8,24.0L35.4,15.4zM26.0,11.7l3.8,3.8L26.0,19.2L26.0,11.7zM29.8,32.6L26.0,36.3l0.0,-7.5L29.8,32.6z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_cast_off.xml b/packages/SystemUI/res/drawable/ic_qs_cast_off.xml
index 130c639..2a9541e 100644
--- a/packages/SystemUI/res/drawable/ic_qs_cast_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_cast_off.xml
@@ -19,12 +19,10 @@ Copyright (C) 2014 The Android Open Source Project
android:height="64dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
- android:fill="#00000000"
- android:stroke="#CCCCCC"
- android:strokeWidth="1.0"
- android:pathData="M21.0,3.0L3.0,3.0C1.9,3.0 1.0,3.9 1.0,5.0l0.0,3.0l2.0,0.0L3.0,5.0l18.0,0.0l0.0,14.0l-7.0,0.0l0.0,2.0l7.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L23.0,5.0C23.0,3.9 22.1,3.0 21.0,3.0zM1.0,18.0l0.0,3.0l3.0,0.0C4.0,19.3 2.7,18.0 1.0,18.0zM1.0,14.0l0.0,2.0c2.8,0.0 5.0,2.2 5.0,5.0l2.0,0.0C8.0,17.1 4.9,14.0 1.0,14.0zM1.0,10.0l0.0,2.0c5.0,0.0 9.0,4.0 9.0,9.0l2.0,0.0C12.0,14.9 7.1,10.0 1.0,10.0z"/>
+ android:fill="#4DFFFFFF"
+ android:pathData="M42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,6.0l4.0,0.0l0.0,-6.0l36.0,0.0l0.0,28.0L28.0,38.0l0.0,4.0l14.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0zM2.0,36.0l0.0,6.0l6.0,0.0C8.0,38.7 5.3,36.0 2.0,36.0zM2.0,28.0l0.0,4.0c5.5,0.0 10.0,4.5 10.0,10.0l4.0,0.0C16.0,34.3 9.7,28.0 2.0,28.0zM2.0,20.0l0.0,4.0c9.9,0.0 18.0,8.1 18.0,18.0l4.0,0.0C24.0,29.8 14.1,20.0 2.0,20.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_cast_on.xml b/packages/SystemUI/res/drawable/ic_qs_cast_on.xml
index 6c82b1c..8dacdc9 100644
--- a/packages/SystemUI/res/drawable/ic_qs_cast_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_cast_on.xml
@@ -19,10 +19,10 @@ Copyright (C) 2014 The Android Open Source Project
android:height="64dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
android:fill="#FFFFFFFF"
- android:pathData="M21.0,3.0L3.0,3.0C1.9,3.0 1.0,3.9 1.0,5.0l0.0,3.0l2.0,0.0L3.0,5.0l18.0,0.0l0.0,14.0l-7.0,0.0l0.0,2.0l7.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L23.0,5.0C23.0,3.9 22.1,3.0 21.0,3.0zM1.0,18.0l0.0,3.0l3.0,0.0C4.0,19.3 2.7,18.0 1.0,18.0zM1.0,14.0l0.0,2.0c2.8,0.0 5.0,2.2 5.0,5.0l2.0,0.0C8.0,17.1 4.9,14.0 1.0,14.0zM1.0,10.0l0.0,2.0c5.0,0.0 9.0,4.0 9.0,9.0l2.0,0.0C12.0,14.9 7.1,10.0 1.0,10.0z"/>
+ android:pathData="M42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,6.0l4.0,0.0l0.0,-6.0l36.0,0.0l0.0,28.0L28.0,38.0l0.0,4.0l14.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0zM2.0,36.0l0.0,6.0l6.0,0.0C8.0,38.7 5.3,36.0 2.0,36.0zM2.0,28.0l0.0,4.0c5.5,0.0 10.0,4.5 10.0,10.0l4.0,0.0C16.0,34.3 9.7,28.0 2.0,28.0zM2.0,20.0l0.0,4.0c9.9,0.0 18.0,8.1 18.0,18.0l4.0,0.0C24.0,29.8 14.1,20.0 2.0,20.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml b/packages/SystemUI/res/drawable/ic_qs_zen_on.xml
index 0e933cf..4887b32 100644
--- a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_zen_on.xml
@@ -19,10 +19,10 @@ Copyright (C) 2014 The Android Open Source Project
android:height="64dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
android:fill="#FFFFFFFF"
- android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM4.0,12.0c0.0,-4.4 3.6,-8.0 8.0,-8.0c1.8,0.0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4.0,13.8 4.0,12.0zM12.0,20.0c-1.8,0.0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20.0,10.2 20.0,12.0C20.0,16.4 16.4,20.0 12.0,20.0z"/>
+ android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_vol_zen_off.xml b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
index 51be1a8..477c36b 100644
--- a/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
+++ b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
@@ -19,10 +19,10 @@ Copyright (C) 2014 The Android Open Source Project
android:height="32dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
android:fill="#4DFFFFFF"
- android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM4.0,12.0c0.0,-4.4 3.6,-8.0 8.0,-8.0c1.8,0.0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4.0,13.8 4.0,12.0zM12.0,20.0c-1.8,0.0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20.0,10.2 20.0,12.0C20.0,16.4 16.4,20.0 12.0,20.0z"/>
+ android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_vol_zen_on.xml b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
index c8c217d..0a43a7b 100644
--- a/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
+++ b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
@@ -19,10 +19,10 @@ Copyright (C) 2014 The Android Open Source Project
android:height="32dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
android:fill="#FFFFFFFF"
- android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM4.0,12.0c0.0,-4.4 3.6,-8.0 8.0,-8.0c1.8,0.0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4.0,13.8 4.0,12.0zM12.0,20.0c-1.8,0.0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20.0,10.2 20.0,12.0C20.0,16.4 16.4,20.0 12.0,20.0z"/>
+ android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml b/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
index 8b104d1..5992470 100644
--- a/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
@@ -15,14 +15,14 @@ Copyright (C) 2014 The Android Open Source Project
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="19dp"
- android:height="19dp"/>
+ android:width="18dp"
+ android:height="18dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
android:fill="#FFFFFFFF"
- android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM4.0,12.0c0.0,-4.4 3.6,-8.0 8.0,-8.0c1.8,0.0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4.0,13.8 4.0,12.0zM12.0,20.0c-1.8,0.0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20.0,10.2 20.0,12.0C20.0,16.4 16.4,20.0 12.0,20.0z"/>
+ android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
</vector>
diff --git a/packages/SystemUI/res/layout/heads_up.xml b/packages/SystemUI/res/layout/heads_up.xml
index 236fdc3..0e2b6d6 100644
--- a/packages/SystemUI/res/layout/heads_up.xml
+++ b/packages/SystemUI/res/layout/heads_up.xml
@@ -17,13 +17,14 @@
<com.android.systemui.statusbar.policy.HeadsUpNotificationView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
- android:layout_width="match_parent">
+ android:layout_width="match_parent"
+ android:background="@drawable/heads_up_scrim">
<FrameLayout
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_side_padding"
- android:layout_marginEnd="@dimen/notification_side_padding"
- android:elevation="16dp"
+ android:paddingStart="@dimen/notification_side_padding"
+ android:paddingEnd="@dimen/notification_side_padding"
+ android:elevation="8dp"
android:id="@+id/content_holder"
style="@style/NotificationsQuickSettings" />
diff --git a/packages/SystemUI/res/layout/recents_empty.xml b/packages/SystemUI/res/layout/recents_empty.xml
index 6268628..ac6450b 100644
--- a/packages/SystemUI/res/layout/recents_empty.xml
+++ b/packages/SystemUI/res/layout/recents_empty.xml
@@ -19,8 +19,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
- android:textSize="40sp"
+ android:textSize="20sp"
android:textColor="#ffffffff"
+ android:textStyle="italic"
android:text="@string/recents_empty_message"
- android:fontFamily="sans-serif-thin"
+ android:fontFamily="sans-serif-light"
android:visibility="gone" /> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
new file mode 100644
index 0000000..5afa967
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/qs_panel_background"
+ android:translationZ="@dimen/volume_panel_z"
+ android:layout_margin="@dimen/volume_panel_z">
+
+ <include layout="@layout/volume_panel" />
+
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_panel_item.xml b/packages/SystemUI/res/layout/volume_panel_item.xml
index 98cb8f4..4a2a0c0 100644
--- a/packages/SystemUI/res/layout/volume_panel_item.xml
+++ b/packages/SystemUI/res/layout/volume_panel_item.xml
@@ -15,28 +15,34 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="80dip"
- android:orientation="horizontal"
- android:layout_marginTop="8dip"
- android:layout_marginBottom="8dip"
- android:gravity="start|center_vertical">
+ android:layout_width="match_parent"
+ android:layout_height="80dip"
+ android:orientation="horizontal"
+ android:layout_marginTop="8dip"
+ android:layout_marginBottom="8dip"
+ android:gravity="start|center_vertical">
<ImageView
- android:id="@+id/stream_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="16dip"
- android:background="?android:attr/selectableItemBackground"
- android:contentDescription="@null" />
-
- <SeekBar
- style="?android:attr/seekBarStyle"
- android:id="@+id/seekbar"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:padding="16dip"
- android:layout_marginEnd="16dip" />
-
+ android:id="@+id/stream_icon"
+ style="@style/BorderlessButton.Tiny"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="16dip"
+ android:contentDescription="@null" />
+ <FrameLayout
+ android:id="@+id/seekbar_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <SeekBar
+ style="?android:attr/seekBarStyle"
+ android:id="@+id/seekbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dip"
+ android:paddingBottom="16dip"
+ android:paddingStart="11dip"
+ android:paddingEnd="11dip"
+ android:layout_marginEnd="16dip" />
+ </FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 39ce0a2..42c4392 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -38,6 +38,9 @@
<dimen name="status_bar_recents_app_icon_left_margin">8dp</dimen>
<dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
+ <!-- The side padding for the task stack as a percentage of the width. -->
+ <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.2229</item>
+
<!-- Width of the zen mode interstitial dialog. -->
<dimen name="zen_mode_dialog_width">384dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index b510fef..326f602 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -19,6 +19,9 @@
<!-- Recent Applications parameters -->
<dimen name="status_bar_recents_app_label_width">190dip</dimen>
+ <!-- The side padding for the task stack as a percentage of the width. -->
+ <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.25</item>
+
<fraction name="keyguard_clock_y_fraction_max">37%</fraction>
<fraction name="keyguard_clock_y_fraction_min">14%</fraction>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 5750faa..313e2e8 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -46,6 +46,9 @@
<!-- On tablets this is just the close_handle_height -->
<dimen name="peek_height">@dimen/close_handle_height</dimen>
+ <!-- The side padding for the task stack as a percentage of the width. -->
+ <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.075</item>
+
<!-- Width of the zen mode interstitial dialog. -->
<dimen name="zen_mode_dialog_width">384dp</dimen>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index e6fa535..757d4ad 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -72,6 +72,8 @@
<color name="recents_task_bar_light_dismiss_color">#ffeeeeee</color>
<!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
<color name="recents_task_bar_dark_dismiss_color">#ff333333</color>
+ <!-- The recents task bar highlight color. -->
+ <color name="recents_task_bar_highlight_color">#28ffffff</color>
<color name="keyguard_affordance">#ffffffff</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 61ed3cf..bfbdcf3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -196,9 +196,6 @@
<dimen name="qs_dual_tile_height">109dp</dimen>
<dimen name="qs_dual_tile_padding">12dp</dimen>
- <!-- How far the hidden header peeks from the top of the screen when QS is in detail mode. -->
- <dimen name="qs_header_peek_height">8dp</dimen>
-
<!-- How far the expanded QS panel peeks from the header in collapsed state. -->
<dimen name="qs_peek_height">8dp</dimen>
@@ -235,12 +232,21 @@
<!-- The amount to translate when animating the removal of a task. -->
<dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
+ <!-- The amount of highlight to make on each task view. -->
+ <dimen name="recents_task_view_highlight">1dp</dimen>
+
<!-- The amount of space a user has to scroll to dismiss any info panes. -->
<dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen>
<!-- The height of the search bar space. -->
<dimen name="recents_search_bar_space_height">64dp</dimen>
+ <!-- The side padding for the task stack as a percentage of the width. -->
+ <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.04444</item>
+
+ <!-- The top offset for the task stack. -->
+ <dimen name="recents_stack_top_padding">16dp</dimen>
+
<!-- Used to calculate the translation animation duration, the expected amount of movement
in dps over one second of time. -->
<dimen name="recents_animation_movement_in_dps_per_second">800dp</dimen>
@@ -299,6 +305,7 @@
keyguard_clock_height_fraction_* for the difference between min and max.-->
<dimen name="keyguard_clock_notifications_margin_min">22dp</dimen>
<dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
+ <dimen name="heads_up_window_height">250dp</dimen>
<!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
<dimen name="keyguard_min_swipe_amount">75dp</dimen>
@@ -311,4 +318,7 @@
<!-- Volume panel z depth -->
<dimen name="volume_panel_z">3dp</dimen>
+
+ <!-- Move distance for the hint animations on the lockscreen (unlock, phone, camera)-->
+ <dimen name="hint_move_distance">75dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ef3956e..373f11f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -290,6 +290,8 @@
<string name="accessibility_desc_off">Off.</string>
<!-- Content description of an item that is connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_connected">Connected.</string>
+ <!-- Content description of an item that is connecting for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_connecting">Connecting.</string>
<!-- Content description of the data connection type GPRS for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_data_connection_gprs">GPRS</string>
@@ -522,7 +524,7 @@
<string name="quick_settings_notifications_label">Notifications</string>
<!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
- <string name="recents_empty_message">RECENTS</string>
+ <string name="recents_empty_message">No recent apps</string>
<!-- Recents: The info panel app info button string. [CHAR LIMIT=NONE] -->
<string name="recents_app_info_button_label">Application Info</string>
<!-- Recents: Temporary string for the button in the recents search bar. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 1b12cb0..d153d09 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -51,12 +51,12 @@ public class SwipeHelper implements Gefingerpoken {
private int MAX_DISMISS_VELOCITY = 2000; // dp/sec
private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms
- public static float ALPHA_FADE_START = 0f; // fraction of thumbnail width
+ public static float SWIPE_PROGRESS_FADE_START = 0f; // fraction of thumbnail width
// where fade starts
- static final float ALPHA_FADE_END = 0.5f; // fraction of thumbnail width
- // beyond which alpha->0
- private float mMinAlpha = 0f;
- private float mMaxAlpha = 1f;
+ static final float SWIPE_PROGRESS_FADE_END = 0.5f; // fraction of thumbnail width
+ // beyond which swipe progress->0
+ private float mMinSwipeProgress = 0f;
+ private float mMaxSwipeProgress = 1f;
private float mPagingTouchSlop;
private Callback mCallback;
@@ -137,36 +137,39 @@ public class SwipeHelper implements Gefingerpoken {
v.getMeasuredHeight();
}
- public void setMinAlpha(float minAlpha) {
- mMinAlpha = minAlpha;
+ public void setMinSwipeProgress(float minSwipeProgress) {
+ mMinSwipeProgress = minSwipeProgress;
}
- public void setMaxAlpha(float maxAlpha) {
- mMaxAlpha = maxAlpha;
+ public void setMaxSwipeProgress(float maxSwipeProgress) {
+ mMaxSwipeProgress = maxSwipeProgress;
}
- private float getAlphaForOffset(View view) {
+ private float getSwipeProgressForOffset(View view) {
float viewSize = getSize(view);
- final float fadeSize = ALPHA_FADE_END * viewSize;
+ final float fadeSize = SWIPE_PROGRESS_FADE_END * viewSize;
float result = 1.0f;
float pos = getTranslation(view);
- if (pos >= viewSize * ALPHA_FADE_START) {
- result = 1.0f - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
- } else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
- result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
+ if (pos >= viewSize * SWIPE_PROGRESS_FADE_START) {
+ result = 1.0f - (pos - viewSize * SWIPE_PROGRESS_FADE_START) / fadeSize;
+ } else if (pos < viewSize * (1.0f - SWIPE_PROGRESS_FADE_START)) {
+ result = 1.0f + (viewSize * SWIPE_PROGRESS_FADE_START + pos) / fadeSize;
}
- return Math.min(Math.max(mMinAlpha, result), mMaxAlpha);
+ return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress);
}
- private void updateAlphaFromOffset(View animView, boolean dismissable) {
- if (FADE_OUT_DURING_SWIPE && dismissable) {
- float alpha = getAlphaForOffset(animView);
- if (alpha != 0f && alpha != 1f) {
- animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- } else {
- animView.setLayerType(View.LAYER_TYPE_NONE, null);
+ private void updateSwipeProgressFromOffset(View animView, boolean dismissable) {
+ float swipeProgress = getSwipeProgressForOffset(animView);
+ if (!mCallback.updateSwipeProgress(animView, dismissable, swipeProgress)) {
+ if (FADE_OUT_DURING_SWIPE && dismissable) {
+ float alpha = swipeProgress;
+ if (alpha != 0f && alpha != 1f) {
+ animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ } else {
+ animView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ animView.setAlpha(getSwipeProgressForOffset(animView));
}
- animView.setAlpha(getAlphaForOffset(animView));
}
invalidateGlobalRegion(animView);
}
@@ -307,7 +310,7 @@ public class SwipeHelper implements Gefingerpoken {
});
anim.addUpdateListener(new AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
- updateAlphaFromOffset(animView, canAnimViewBeDismissed);
+ updateSwipeProgressFromOffset(animView, canAnimViewBeDismissed);
}
});
anim.start();
@@ -321,12 +324,12 @@ public class SwipeHelper implements Gefingerpoken {
anim.setDuration(duration);
anim.addUpdateListener(new AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
- updateAlphaFromOffset(animView, canAnimViewBeDismissed);
+ updateSwipeProgressFromOffset(animView, canAnimViewBeDismissed);
}
});
anim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animator) {
- updateAlphaFromOffset(animView, canAnimViewBeDismissed);
+ updateSwipeProgressFromOffset(animView, canAnimViewBeDismissed);
mCallback.onChildSnappedBack(animView);
}
});
@@ -365,7 +368,7 @@ public class SwipeHelper implements Gefingerpoken {
}
setTranslation(mCurrAnimView, delta);
- updateAlphaFromOffset(mCurrAnimView, mCanCurrViewBeDimissed);
+ updateSwipeProgressFromOffset(mCurrAnimView, mCanCurrViewBeDimissed);
}
break;
case MotionEvent.ACTION_UP:
@@ -415,5 +418,12 @@ public class SwipeHelper implements Gefingerpoken {
void onDragCancelled(View v);
void onChildSnappedBack(View animView);
+
+ /**
+ * Updates the swipe progress on a child.
+ *
+ * @return if true, prevents the default alpha fading.
+ */
+ boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 4c7f3df..b280ab7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -204,9 +204,9 @@ public class KeyguardService extends Service {
}
@Override
- public void startKeyguardExitAnimation(long fadeoutDuration) {
+ public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
checkPermission();
- mKeyguardViewMediator.startKeyguardExitAnimation(fadeoutDuration);
+ mKeyguardViewMediator.startKeyguardExitAnimation(startTime, fadeoutDuration);
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7110d8d..4837a53 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -59,8 +59,6 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.MultiUserAvatarCache;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.analytics.KeyguardAnalytics;
-import com.android.keyguard.analytics.Session;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -70,7 +68,6 @@ import com.android.systemui.statusbar.phone.StatusBarWindowManager;
import java.io.File;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
-import static com.android.keyguard.analytics.KeyguardAnalytics.SessionTypeAdapter;
/**
@@ -117,7 +114,6 @@ import static com.android.keyguard.analytics.KeyguardAnalytics.SessionTypeAdapte
public class KeyguardViewMediator extends SystemUI {
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
final static boolean DEBUG = false;
- private static final boolean ENABLE_ANALYTICS = Build.IS_DEBUGGABLE;
private final static boolean DBG_WAKE = false;
private final static String TAG = "KeyguardViewMediator";
@@ -199,8 +195,6 @@ public class KeyguardViewMediator extends SystemUI {
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private KeyguardAnalytics mKeyguardAnalytics;
-
// these are protected by synchronized (this)
/**
@@ -469,22 +463,6 @@ public class KeyguardViewMediator extends SystemUI {
mViewMediatorCallback, mLockPatternUtils);
final ContentResolver cr = mContext.getContentResolver();
- if (ENABLE_ANALYTICS && !LockPatternUtils.isSafeModeEnabled() &&
- Settings.Secure.getInt(cr, KEYGUARD_ANALYTICS_SETTING, 0) == 1) {
- mKeyguardAnalytics = new KeyguardAnalytics(mContext, new SessionTypeAdapter() {
-
- @Override
- public int getSessionType() {
- return mLockPatternUtils.isSecure() && !mUpdateMonitor.getUserHasTrust(
- mLockPatternUtils.getCurrentUser())
- ? Session.TYPE_KEYGUARD_SECURE
- : Session.TYPE_KEYGUARD_INSECURE;
- }
- }, new File(mContext.getCacheDir(), "keyguard_analytics.bin"));
- } else {
- mKeyguardAnalytics = null;
- }
-
mScreenOn = mPM.isScreenOn();
mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0);
@@ -585,9 +563,6 @@ public class KeyguardViewMediator extends SystemUI {
} else {
doKeyguardLocked(null);
}
- if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) {
- mKeyguardAnalytics.getCallback().onScreenOff();
- }
}
KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurndOff(why);
}
@@ -830,9 +805,6 @@ public class KeyguardViewMediator extends SystemUI {
updateActivityLockScreenState();
adjustStatusBarLocked();
}
- if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) {
- mKeyguardAnalytics.getCallback().onSetOccluded(isOccluded);
- }
}
}
@@ -1083,7 +1055,8 @@ public class KeyguardViewMediator extends SystemUI {
handleDismiss();
break;
case START_KEYGUARD_EXIT_ANIM:
- handleStartKeyguardExitAnimation((Long) msg.obj);
+ StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
+ handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
break;
}
}
@@ -1227,7 +1200,7 @@ public class KeyguardViewMediator extends SystemUI {
}
}
- private void handleStartKeyguardExitAnimation(long fadeoutDuration) {
+ private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
synchronized (KeyguardViewMediator.this) {
// only play "unlock" noises if not on a call (since the incall UI
@@ -1236,7 +1209,7 @@ public class KeyguardViewMediator extends SystemUI {
playSounds(false);
}
- mStatusBarKeyguardViewManager.hide();
+ mStatusBarKeyguardViewManager.hide(startTime, fadeoutDuration);
mShowing = false;
mKeyguardDonePending = false;
updateActivityLockScreenState();
@@ -1346,12 +1319,24 @@ public class KeyguardViewMediator extends SystemUI {
return mStatusBarKeyguardViewManager;
}
- public void startKeyguardExitAnimation(long fadeoutDuration) {
- Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM, fadeoutDuration);
+ public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,
+ new StartKeyguardExitAnimParams(startTime, fadeoutDuration));
mHandler.sendMessage(msg);
}
public ViewMediatorCallback getViewMediatorCallback() {
return mViewMediatorCallback;
}
+
+ private static class StartKeyguardExitAnimParams {
+
+ long startTime;
+ long fadeoutDuration;
+
+ private StartKeyguardExitAnimParams(long startTime, long fadeoutDuration) {
+ this.startTime = startTime;
+ this.fadeoutDuration = fadeoutDuration;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 6ce0e48..2bf369a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -61,6 +61,7 @@ public class QSPanel extends ViewGroup {
mContext = context;
mDetail = new FrameLayout(mContext);
+ mDetail.setBackgroundColor(mContext.getResources().getColor(R.color.system_primary_color));
mDetail.setVisibility(GONE);
mDetail.setClickable(true);
addView(mDetail);
@@ -151,6 +152,7 @@ public class QSPanel extends ViewGroup {
}
private void handleShowDetail(TileRecord r, boolean show) {
+ if (r == null) return;
AnimatorListener listener = null;
if (show) {
if (mDetailRecord != null) return;
@@ -204,7 +206,7 @@ public class QSPanel extends ViewGroup {
mDetail.measure(exactly(width), unspecified());
if (mDetail.getVisibility() == VISIBLE && mDetail.getChildCount() > 0) {
final int dmh = mDetail.getMeasuredHeight();
- if (dmh > 0) h = dmh;
+ if (dmh > 0) h = Math.max(h, dmh);
}
setMeasuredDimension(width, h);
}
@@ -231,7 +233,8 @@ public class QSPanel extends ViewGroup {
left + record.tileView.getMeasuredWidth(),
top + record.tileView.getMeasuredHeight());
}
- mDetail.layout(0, 0, mDetail.getMeasuredWidth(), mDetail.getMeasuredHeight());
+ final int dh = Math.max(mDetail.getMeasuredHeight(), getMeasuredHeight());
+ mDetail.layout(0, 0, mDetail.getMeasuredWidth(), dh);
}
private int getRowTop(int row) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 7335ab4..d220e1a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles;
import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
import android.content.Intent;
import android.provider.Settings;
+import android.text.TextUtils;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
@@ -70,18 +71,27 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> {
final boolean supported = mController.isBluetoothSupported();
final boolean enabled = mController.isBluetoothEnabled();
final boolean connected = mController.isBluetoothConnected();
+ final boolean connecting = mController.isBluetoothConnecting();
state.visible = supported;
state.value = enabled;
final String stateContentDescription;
if (enabled) {
+ state.label = null;
if (connected) {
state.iconId = R.drawable.ic_qs_bluetooth_connected;
stateContentDescription = mContext.getString(R.string.accessibility_desc_connected);
+ state.label = mController.getLastDeviceName();
+ } else if (connecting) {
+ state.iconId = R.drawable.ic_qs_bluetooth_connecting;
+ stateContentDescription = mContext.getString(R.string.accessibility_desc_connecting);
+ state.label = mController.getLastDeviceName();
} else {
state.iconId = R.drawable.ic_qs_bluetooth_on;
stateContentDescription = mContext.getString(R.string.accessibility_desc_on);
}
- state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
+ if (TextUtils.isEmpty(state.label)) {
+ state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
+ }
} else {
state.iconId = R.drawable.ic_qs_bluetooth_off;
state.label = mContext.getString(R.string.quick_settings_bluetooth_off_label);
@@ -91,9 +101,9 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> {
R.string.accessibility_quick_settings_bluetooth, stateContentDescription);
}
- private final BluetoothStateChangeCallback mCallback = new BluetoothStateChangeCallback() {
+ private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
@Override
- public void onBluetoothStateChange(boolean on) {
+ public void onBluetoothStateChange(boolean enabled, boolean connecting) {
refreshState();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 0759b8e..72a3341 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -64,7 +64,7 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView
}
public void setMinSwipeAlpha(float minAlpha) {
- mSwipeHelper.setMinAlpha(minAlpha);
+ mSwipeHelper.setMinSwipeProgress(minAlpha);
}
private int scrollPositionOfMostRecent() {
@@ -221,6 +221,11 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView
public void onChildSnappedBack(View animView) {
}
+ @Override
+ public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
+ return false;
+ }
+
public View getChildAtPosition(MotionEvent ev) {
final float x = ev.getX() + getScrollX();
final float y = ev.getY() + getScrollY();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index c2dde6a..1213375 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -65,7 +65,7 @@ public class RecentsVerticalScrollView extends ScrollView
}
public void setMinSwipeAlpha(float minAlpha) {
- mSwipeHelper.setMinAlpha(minAlpha);
+ mSwipeHelper.setMinSwipeProgress(minAlpha);
}
private int scrollPositionOfMostRecent() {
@@ -229,6 +229,11 @@ public class RecentsVerticalScrollView extends ScrollView
public void onChildSnappedBack(View animView) {
}
+ @Override
+ public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
+ return false;
+ }
+
public View getChildAtPosition(MotionEvent ev) {
final float x = ev.getX() + getScrollX();
final float y = ev.getY() + getScrollY();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 4db81bf..76e88a5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -101,8 +101,6 @@ public class Constants {
public static final int TaskStackOverscrollRange = 150;
public static final int FilterStartDelay = 25;
- // The padding will be applied to the smallest dimension, and then applied to all sides
- public static final float StackPaddingPct = 0.085f;
// The overlap height relative to the task height
public static final float StackOverlapPct = 0.65f;
// The height of the peek space relative to the stack height
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 1ae7a0d..6391685 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -52,10 +52,12 @@ public class RecentsConfiguration {
public int filteringCurrentViewsMinAnimDuration;
public int filteringNewViewsMinAnimDuration;
- public int taskBarEnterAnimDuration;
- public int taskBarExitAnimDuration;
+
public int taskStackScrollDismissInfoPaneDistance;
public int taskStackMaxDim;
+ public float taskStackWidthPaddingPct;
+ public int taskStackTopPaddingPx;
+
public int taskViewInfoPaneAnimDuration;
public int taskViewRemoveAnimDuration;
public int taskViewRemoveAnimTranslationXPx;
@@ -63,12 +65,18 @@ public class RecentsConfiguration {
public int taskViewTranslationZIncrementPx;
public int taskViewShadowOutlineBottomInsetPx;
public int taskViewRoundedCornerRadiusPx;
+ public int taskViewHighlightPx;
+
public int searchBarSpaceHeightPx;
public int taskBarViewDefaultBackgroundColor;
public int taskBarViewDefaultTextColor;
public int taskBarViewLightTextColor;
public int taskBarViewDarkTextColor;
+ public int taskBarViewHighlightColor;
+
+ public int taskBarEnterAnimDuration;
+ public int taskBarExitAnimDuration;
public boolean launchedFromAltTab;
public boolean launchedWithThumbnailAnimation;
@@ -115,13 +123,16 @@ public class RecentsConfiguration {
res.getInteger(R.integer.recents_filter_animate_current_views_min_duration);
filteringNewViewsMinAnimDuration =
res.getInteger(R.integer.recents_filter_animate_new_views_min_duration);
- taskBarEnterAnimDuration =
- res.getInteger(R.integer.recents_animate_task_bar_enter_duration);
- taskBarExitAnimDuration =
- res.getInteger(R.integer.recents_animate_task_bar_exit_duration);
+
taskStackScrollDismissInfoPaneDistance = res.getDimensionPixelSize(
R.dimen.recents_task_stack_scroll_dismiss_info_pane_distance);
taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
+
+ TypedValue widthPaddingPctValue = new TypedValue();
+ res.getValue(R.dimen.recents_stack_width_padding_percentage, widthPaddingPctValue, true);
+ taskStackWidthPaddingPct = widthPaddingPctValue.getFloat();
+ taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding);
+
taskViewInfoPaneAnimDuration =
res.getInteger(R.integer.recents_animate_task_view_info_pane_duration);
taskViewRemoveAnimDuration =
@@ -130,11 +141,13 @@ public class RecentsConfiguration {
res.getDimensionPixelSize(R.dimen.recents_task_view_remove_anim_translation_x);
taskViewRoundedCornerRadiusPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+ taskViewHighlightPx = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
taskViewTranslationZIncrementPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
taskViewShadowOutlineBottomInsetPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_shadow_outline_bottom_inset);
+
searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
taskBarViewDefaultBackgroundColor =
@@ -145,6 +158,13 @@ public class RecentsConfiguration {
res.getColor(R.color.recents_task_bar_light_text_color);
taskBarViewDarkTextColor =
res.getColor(R.color.recents_task_bar_dark_text_color);
+ taskBarViewHighlightColor =
+ res.getColor(R.color.recents_task_bar_highlight_color);
+
+ taskBarEnterAnimDuration =
+ res.getInteger(R.integer.recents_animate_task_bar_enter_duration);
+ taskBarExitAnimDuration =
+ res.getInteger(R.integer.recents_animate_task_bar_exit_duration);
fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index 9e6c98e..c10ddd1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -18,6 +18,10 @@ package com.android.systemui.recents.views;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.FrameLayout;
@@ -41,6 +45,8 @@ class TaskBarView extends FrameLayout {
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
+ static Paint sHighlightPaint;
+
public TaskBarView(Context context) {
this(context, null);
}
@@ -55,9 +61,23 @@ class TaskBarView extends FrameLayout {
public TaskBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ setWillNotDraw(false);
+
+ // Load the dismiss resources
Resources res = context.getResources();
mLightDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_light);
mDarkDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_dark);
+
+ // Configure the highlight paint
+ if (sHighlightPaint == null) {
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
+ sHighlightPaint = new Paint();
+ sHighlightPaint.setStyle(Paint.Style.STROKE);
+ sHighlightPaint.setStrokeWidth(config.taskViewHighlightPx);
+ sHighlightPaint.setColor(config.taskBarViewHighlightColor);
+ sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
+ sHighlightPaint.setAntiAlias(true);
+ }
}
@Override
@@ -68,6 +88,17 @@ class TaskBarView extends FrameLayout {
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
}
+ @Override
+ protected void onDraw(Canvas canvas) {
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
+
+ // Draw the highlight at the top edge (but put the bottom edge just out of view)
+ float offset = config.taskViewHighlightPx / 2f;
+ float radius = config.taskViewRoundedCornerRadiusPx;
+ canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
+ getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
+ }
+
/** Synchronizes this bar view's properties with the task's transform */
void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
TaskViewTransform toTransform, int duration) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 1fbaf87..053f122 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -645,6 +645,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Note: We let the stack view be the full height because we want the cards to go under the
// navigation bar if possible. However, the stack rects which we use to calculate
// max scroll, etc. need to take the nav bar into account
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
// Compute the stack rects
mRect.set(0, 0, width, height);
@@ -652,23 +653,21 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mStackRect.left += insetLeft;
mStackRect.bottom -= insetBottom;
- int smallestDimension = Math.min(width, height);
- int padding = (int) (Constants.Values.TaskStackView.StackPaddingPct * smallestDimension / 2f);
+ int widthPadding = (int) (config.taskStackWidthPaddingPct * mStackRect.width());
+ int heightPadding = config.taskStackTopPaddingPx;
if (Constants.DebugFlags.App.EnableSearchLayout) {
- mStackRect.top += padding;
- mStackRect.left += padding;
- mStackRect.right -= padding;
- mStackRect.bottom -= padding;
+ mStackRect.top += heightPadding;
+ mStackRect.left += widthPadding;
+ mStackRect.right -= widthPadding;
+ mStackRect.bottom -= heightPadding;
} else {
- mStackRect.inset(padding, padding);
+ mStackRect.inset(widthPadding, heightPadding);
}
mStackRectSansPeek.set(mStackRect);
mStackRectSansPeek.top += Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height();
// Compute the task rect
- int minHeight = (int) (mStackRect.height() -
- (Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height()));
- int size = Math.min(minHeight, Math.min(mStackRect.width(), mStackRect.height()));
+ int size = mStackRect.width();
int left = mStackRect.left + (mStackRect.width() - size) / 2;
mTaskRect.set(left, mStackRectSansPeek.top,
left + size, mStackRectSansPeek.top + size);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 21b41c7..06cc476 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -83,6 +83,7 @@ import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Locale;
@@ -117,6 +118,9 @@ public abstract class BaseStatusBar extends SystemUI implements
public static final int EXPANDED_LEAVE_ALONE = -10000;
public static final int EXPANDED_FULL_OPEN = -10001;
+ /** If true, delays dismissing the Keyguard until the ActivityManager calls back. */
+ protected static final boolean DELAY_DISMISS_TO_ACTIVITY_LAUNCH = false;
+
protected CommandQueue mCommandQueue;
protected IStatusBarService mBarService;
protected H mHandler = createHandler();
@@ -228,7 +232,7 @@ public abstract class BaseStatusBar extends SystemUI implements
}
final boolean isActivity = pendingIntent.isActivity();
if (isActivity) {
- startNotificationActivity(new OnDismissAction() {
+ dismissKeyguardThenExecute(new OnDismissAction() {
@Override
public boolean onDismiss() {
try {
@@ -250,7 +254,8 @@ public abstract class BaseStatusBar extends SystemUI implements
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
visibilityChanged(false);
}
- return handled; // Wait for activity start.
+ // Wait for activity start.
+ return handled && DELAY_DISMISS_TO_ACTIVITY_LAUNCH;
}
});
return true;
@@ -332,8 +337,7 @@ public abstract class BaseStatusBar extends SystemUI implements
mHandler.post(new Runnable() {
@Override
public void run() {
- mNotificationData.updateRanking(currentRanking);
- updateNotifications();
+ updateRankingInternal(currentRanking);
}
});
}
@@ -479,7 +483,7 @@ public abstract class BaseStatusBar extends SystemUI implements
* Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
* @param action A dismiss action that is called if it's safe to start the activity.
*/
- protected void startNotificationActivity(OnDismissAction action) {
+ protected void dismissKeyguardThenExecute(OnDismissAction action) {
action.onDismiss();
}
@@ -1049,7 +1053,7 @@ public abstract class BaseStatusBar extends SystemUI implements
}
public void onClick(final View v) {
- startNotificationActivity(new OnDismissAction() {
+ dismissKeyguardThenExecute(new OnDismissAction() {
public boolean onDismiss() {
try {
// The intent we are sending is for the application, which
@@ -1069,7 +1073,7 @@ public abstract class BaseStatusBar extends SystemUI implements
v.getLocationOnScreen(pos);
Intent overlay = new Intent();
overlay.setSourceBounds(new Rect(pos[0], pos[1],
- pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+ pos[0] + v.getWidth(), pos[1] + v.getHeight()));
try {
mIntent.send(mContext, 0, overlay);
sent = true;
@@ -1094,7 +1098,7 @@ public abstract class BaseStatusBar extends SystemUI implements
visibilityChanged(false);
boolean waitForActivityLaunch = sent && mIntent.isActivity();
- return waitForActivityLaunch;
+ return waitForActivityLaunch && DELAY_DISMISS_TO_ACTIVITY_LAUNCH;
}
});
}
@@ -1275,6 +1279,8 @@ public abstract class BaseStatusBar extends SystemUI implements
public abstract void addNotificationInternal(StatusBarNotification notification,
Ranking ranking);
+ protected abstract void updateRankingInternal(Ranking ranking);
+
@Override
public void removeNotification(String key) {
if (!USE_NOTIFICATION_LISTENER) {
@@ -1282,7 +1288,7 @@ public abstract class BaseStatusBar extends SystemUI implements
}
}
- protected abstract void removeNotificationInternal(String key, Ranking ranking);
+ public abstract void removeNotificationInternal(String key, Ranking ranking);
public void updateNotification(StatusBarNotification notification) {
if (!USE_NOTIFICATION_LISTENER) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
index 24da5c2..de27119 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
@@ -20,8 +20,10 @@ import android.app.Notification;
import android.content.Context;
import android.os.Process;
import android.provider.Settings;
+import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.view.View;
import com.android.systemui.R;
@@ -30,12 +32,13 @@ import com.android.systemui.statusbar.phone.PhoneStatusBar;
public class InterceptedNotifications {
private static final String TAG = "InterceptedNotifications";
- private static final String EXTRA_INTERCEPT = "android.intercept";
+ private static final String SYNTHETIC_KEY = "InterceptedNotifications.SYNTHETIC_KEY";
private final Context mContext;
private final PhoneStatusBar mBar;
private final ArrayMap<String, StatusBarNotification> mIntercepted
= new ArrayMap<String, StatusBarNotification>();
+ private final ArraySet<String> mReleased = new ArraySet<String>();
private String mSynKey;
@@ -48,25 +51,45 @@ public class InterceptedNotifications {
final int n = mIntercepted.size();
for (int i = 0; i < n; i++) {
final StatusBarNotification sbn = mIntercepted.valueAt(i);
- sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false);
+ mReleased.add(sbn.getKey());
mBar.addNotificationInternal(sbn, null);
}
mIntercepted.clear();
updateSyntheticNotification();
}
- public boolean tryIntercept(StatusBarNotification notification) {
- if (!notification.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) return false;
+ public boolean tryIntercept(StatusBarNotification notification, Ranking ranking) {
+ if (ranking == null) return false;
if (shouldDisplayIntercepted()) return false;
+ if (mReleased.contains(notification.getKey())) return false;
+ if (!ranking.isInterceptedByDoNotDisturb(notification.getKey())) return false;
mIntercepted.put(notification.getKey(), notification);
updateSyntheticNotification();
return true;
}
+ public void retryIntercepts(Ranking ranking) {
+ if (ranking == null) return;
+
+ boolean changed = false;
+ final int N = mIntercepted.size();
+ for (int i = 0; i < N; i++) {
+ final StatusBarNotification sbn = mIntercepted.valueAt(i);
+ if (!tryIntercept(sbn, ranking)) {
+ changed = true;
+ mBar.addNotificationInternal(sbn, ranking);
+ }
+ }
+ if (changed) {
+ updateSyntheticNotification();
+ }
+ }
+
public void remove(String key) {
if (mIntercepted.remove(key) != null) {
updateSyntheticNotification();
}
+ mReleased.remove(key);
}
public boolean isSyntheticEntry(Entry ent) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java
new file mode 100644
index 0000000..367d326
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.view.animation.Interpolator;
+
+/**
+ * An implementation of a bouncer interpolator optimized for unlock hinting.
+ */
+public class BounceInterpolator implements Interpolator {
+
+ private final static float SCALE_FACTOR = 7.5625f;
+
+ @Override
+ public float getInterpolation(float t) {
+ if (t < 4f / 11f) {
+ return SCALE_FACTOR * t * t;
+ } else if (t < 8f / 11f) {
+ float t2 = t - 6f / 11f;
+ return SCALE_FACTOR * t2 * t2 + 3f / 4f;
+ } else if (t < 10f / 11f) {
+ float t2 = t - 9f / 11f;
+ return SCALE_FACTOR * t2 * t2 + 15f / 16f;
+ } else {
+ float t2 = t - 21f / 22f;
+ return SCALE_FACTOR * t2 * t2 + 63f / 64f;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 994b329..97aa993 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
@@ -32,6 +33,10 @@ import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
/**
@@ -43,6 +48,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
+ private static final Intent SECURE_CAMERA_INTENT =
+ new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
+ .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ private static final Intent INSECURE_CAMERA_INTENT =
+ new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
private ImageView mCameraImageView;
@@ -51,6 +61,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private ActivityStarter mActivityStarter;
private UnlockMethodCache mUnlockMethodCache;
+ private LockPatternUtils mLockPatternUtils;
public KeyguardBottomAreaView(Context context) {
super(context);
@@ -72,10 +83,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mLockPatternUtils = new LockPatternUtils(mContext);
mCameraImageView = (ImageView) findViewById(R.id.camera_button);
mPhoneImageView = (ImageView) findViewById(R.id.phone_button);
mLockIcon = (ImageView) findViewById(R.id.lock_icon);
- watchForDevicePolicyChanges();
+ watchForCameraPolicyChanges();
watchForAccessibilityChanges();
updateCameraVisibility();
updatePhoneVisibility();
@@ -88,8 +100,19 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mActivityStarter = activityStarter;
}
+ private Intent getCameraIntent() {
+ KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ boolean currentUserHasTrust = updateMonitor.getUserHasTrust(
+ mLockPatternUtils.getCurrentUser());
+ return mLockPatternUtils.isSecure() && !currentUserHasTrust
+ ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
+ }
+
private void updateCameraVisibility() {
- boolean visible = !isCameraDisabledByDpm();
+ ResolveInfo resolved = mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(),
+ PackageManager.MATCH_DEFAULT_ONLY,
+ mLockPatternUtils.getCurrentUser());
+ boolean visible = !isCameraDisabledByDpm() && resolved != null;
mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
}
@@ -122,19 +145,12 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
return false;
}
- private void watchForDevicePolicyChanges() {
+ private void watchForCameraPolicyChanges() {
final IntentFilter filter = new IntentFilter();
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
- getContext().registerReceiver(new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- post(new Runnable() {
- @Override
- public void run() {
- updateCameraVisibility();
- }
- });
- }
- }, filter);
+ getContext().registerReceiverAsUser(mDevicePolicyReceiver,
+ UserHandle.ALL, filter, null, null);
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
}
private void watchForAccessibilityChanges() {
@@ -171,9 +187,12 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
public void launchCamera() {
- mContext.startActivityAsUser(
- new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
- UserHandle.CURRENT);
+ Intent intent = getCameraIntent();
+ if (intent == SECURE_CAMERA_INTENT) {
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ } else {
+ mActivityStarter.startActivity(intent);
+ }
}
public void launchPhone() {
@@ -186,6 +205,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
super.onVisibilityChanged(changedView, visibility);
if (changedView == this && visibility == VISIBLE) {
updateTrust();
+ updateCameraVisibility();
}
}
@@ -214,5 +234,25 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
@Override
public void onMethodSecureChanged(boolean methodSecure) {
updateTrust();
+ updateCameraVisibility();
}
+
+ private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ updateCameraVisibility();
+ }
+ });
+ }
+ };
+
+ private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onUserSwitchComplete(int userId) {
+ updateCameraVisibility();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index b7a7b0a..3aaace4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -17,10 +17,14 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
+import android.os.SystemClock;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardViewBase;
@@ -43,6 +47,8 @@ public class KeyguardBouncer {
private StatusBarWindowManager mWindowManager;
private KeyguardViewBase mKeyguardView;
private ViewGroup mRoot;
+ private Interpolator mFadeOutInterpolator = new LinearInterpolator();
+ private boolean mFadingOut;
public KeyguardBouncer(Context context, ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager,
@@ -86,6 +92,29 @@ public class KeyguardBouncer {
}
}
+ public void animateHide(long delay, long duration) {
+ if (isShowing()) {
+ mFadingOut = true;
+ mKeyguardView.animate()
+ .alpha(0)
+ .withLayer()
+
+ // Make it disappear faster, as the focus should be on the activity behind.
+ .setDuration(duration / 3)
+ .setInterpolator(mFadeOutInterpolator)
+ .setStartDelay(delay)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mFadingOut = false;
+ hide(true /* destroyView */);
+ }
+ });
+ } else {
+ hide(true /* destroyView */);
+ }
+ }
+
/**
* Reset the state of the view.
*/
@@ -110,7 +139,7 @@ public class KeyguardBouncer {
}
public boolean isShowing() {
- return mRoot != null && mRoot.getVisibility() == View.VISIBLE;
+ return mRoot != null && mRoot.getVisibility() == View.VISIBLE && !mFadingOut;
}
public void prepare() {
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 dce5a30..dfd5a88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -34,7 +34,6 @@ import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import com.android.systemui.R;
-import com.android.systemui.qs.QSPanel;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.GestureRecorder;
@@ -46,13 +45,15 @@ import java.util.ArrayList;
public class NotificationPanelView extends PanelView implements
ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
- View.OnClickListener, KeyguardPageSwipeHelper.Callback {
+ View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
+ KeyguardPageSwipeHelper.Callback {
+
+ private static float EXPANSION_RUBBER_BAND_EXTRA_FACTOR = 0.6f;
private KeyguardPageSwipeHelper mPageSwiper;
- PhoneStatusBar mStatusBar;
private StatusBarHeaderView mHeader;
private View mQsContainer;
- private QSPanel mQsPanel;
+ private View mQsPanel;
private View mKeyguardStatusView;
private ObservableScrollView mScrollView;
private View mStackScrollerContainer;
@@ -83,14 +84,14 @@ public class NotificationPanelView extends PanelView implements
private int mQsMaxExpansionHeight;
private int mMinStackHeight;
private int mQsPeekHeight;
- private int mQsHeaderPeekHeight;
- private boolean mQsShowingDetail;
private float mNotificationTranslation;
private int mStackScrollerIntrinsicPadding;
+ private boolean mStackScrollerOverscrolling;
private boolean mQsExpansionEnabled = true;
private ValueAnimator mQsExpansionAnimator;
private FlingAnimationUtils mFlingAnimationUtils;
private int mStatusBarMinHeight;
+
private Interpolator mFastOutSlowInInterpolator;
private ObjectAnimator mClockAnimator;
private int mClockAnimationTarget = -1;
@@ -133,13 +134,13 @@ public class NotificationPanelView extends PanelView implements
mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
mStackScrollerContainer = findViewById(R.id.notification_container_parent);
mQsContainer = findViewById(R.id.quick_settings_container);
- mQsPanel = (QSPanel) findViewById(R.id.quick_settings_panel);
- mQsPanel.setCallback(mQsPanelCallback);
+ mQsPanel = findViewById(R.id.quick_settings_panel);
mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
mScrollView.setListener(this);
mNotificationStackScroller = (NotificationStackScrollLayout)
findViewById(R.id.notification_stack_scroller);
mNotificationStackScroller.setOnHeightChangedListener(this);
+ mNotificationStackScroller.setOverscrollTopChangedListener(this);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
android.R.interpolator.fast_out_slow_in);
mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
@@ -158,7 +159,6 @@ public class NotificationPanelView extends PanelView implements
mStatusBarMinHeight = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
mQsPeekHeight = getResources().getDimensionPixelSize(R.dimen.qs_peek_height);
- mQsHeaderPeekHeight = getResources().getDimensionPixelSize(R.dimen.qs_header_peek_height);
mClockPositionAlgorithm.loadDimens(getResources());
}
@@ -174,7 +174,9 @@ public class NotificationPanelView extends PanelView implements
setQsStackScrollerPadding(mQsMaxExpansionHeight);
}
} else {
- setQsExpansion(mQsMinExpansionHeight);
+ if (!mStackScrollerOverscrolling) {
+ setQsExpansion(mQsMinExpansionHeight);
+ }
positionClockAndNotifications();
mNotificationStackScroller.setStackHeight(getExpandedHeight());
}
@@ -187,7 +189,10 @@ public class NotificationPanelView extends PanelView implements
private void positionClockAndNotifications() {
boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending();
if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
- mStackScrollerIntrinsicPadding = mHeader.getBottom() + mQsPeekHeight
+ int bottom = mStackScrollerOverscrolling
+ ? mHeader.getCollapsedHeight()
+ : mHeader.getBottom();
+ mStackScrollerIntrinsicPadding = bottom + mQsPeekHeight
+ mNotificationTopPadding;
mTopPaddingAdjustment = 0;
} else {
@@ -491,6 +496,16 @@ public class NotificationPanelView extends PanelView implements
}
}
+
+ @Override
+ public void onOverscrollTopChanged(float amount) {
+ cancelAnimation();
+ float rounded = amount >= 1f ? amount : 0f;
+ mStackScrollerOverscrolling = rounded != 0f;
+ setQsExpansion(mQsMinExpansionHeight + rounded);
+ updateQsState();
+ }
+
private void onQsExpansionStarted() {
onQsExpansionStarted(0);
}
@@ -518,47 +533,20 @@ public class NotificationPanelView extends PanelView implements
}
private void updateQsState() {
- mHeader.setExpanded(mQsExpanded);
+ boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling;
+ mHeader.setExpanded(expandVisually, mStackScrollerOverscrolling);
mNotificationStackScroller.setEnabled(!mQsExpanded);
- mQsPanel.setVisibility(mQsExpanded ? View.VISIBLE : View.INVISIBLE);
+ mQsPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
mQsContainer.setVisibility(mKeyguardShowing && !mQsExpanded
? View.INVISIBLE
: View.VISIBLE);
mScrollView.setTouchEnabled(mQsExpanded);
- if (mQsShowingDetail) {
- if (mQsFullyExpanded) {
- setQsHeaderPeeking(true);
- }
- } else {
- setQsHeaderPeeking(false);
- }
- }
-
- private void setQsHeaderPeeking(boolean peeking) {
- final boolean stackIsPeeking = mStackScrollerContainer.getTranslationY() != 0;
- final boolean headerIsPeeking = mHeader.getTranslationY() != 0;
- final int ty = mQsHeaderPeekHeight - mHeader.getExpandedHeight();
- if (peeking) {
- if (!headerIsPeeking) {
- mHeader.animate().translationY(ty);
- }
- if (!stackIsPeeking) {
- mStackScrollerContainer.animate().translationY(ty);
- }
- } else {
- if (headerIsPeeking) {
- mHeader.animate().translationY(0);
- }
- if (stackIsPeeking) {
- mStackScrollerContainer.animate().translationY(0);
- }
- }
}
private void setQsExpansion(float height) {
height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
mQsFullyExpanded = height == mQsMaxExpansionHeight;
- if (height > mQsMinExpansionHeight && !mQsExpanded) {
+ if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling) {
setQsExpanded(true);
} else if (height <= mQsMinExpansionHeight && mQsExpanded) {
setQsExpanded(false);
@@ -566,7 +554,9 @@ public class NotificationPanelView extends PanelView implements
mQsExpansionHeight = height;
mHeader.setExpansion(height - mQsPeekHeight);
setQsTranslation(height);
- setQsStackScrollerPadding(height);
+ if (!mStackScrollerOverscrolling) {
+ setQsStackScrollerPadding(height);
+ }
mStatusBar.userActivity();
}
@@ -650,16 +640,10 @@ public class NotificationPanelView extends PanelView implements
if (!mQsExpansionEnabled) {
return false;
}
- final float ty = mHeader.getTranslationY();
boolean onHeader = x >= mHeader.getLeft() && x <= mHeader.getRight()
- && y >= mHeader.getTop() + ty && y <= mHeader.getBottom() + ty;
+ && y >= mHeader.getTop() && y <= mHeader.getBottom();
if (mQsExpanded) {
- if (mQsShowingDetail && onHeader) {
- // bring back the header, crudely
- setQsHeaderPeeking(false);
- mQsPanel.setExpanded(false);
- }
- return !mQsShowingDetail && onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0);
+ return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0);
} else {
return onHeader;
}
@@ -743,8 +727,11 @@ public class NotificationPanelView extends PanelView implements
@Override
protected void onOverExpansionChanged(float overExpansion) {
float currentOverScroll = mNotificationStackScroller.getCurrentOverScrolledPixels(true);
- mNotificationStackScroller.setOverScrolledPixels(currentOverScroll + overExpansion
- - mOverExpansion, true /* onTop */, false /* animate */);
+ float expansionChange = overExpansion - mOverExpansion;
+ expansionChange *= EXPANSION_RUBBER_BAND_EXTRA_FACTOR;
+ mNotificationStackScroller.setOverScrolledPixels(currentOverScroll + expansionChange,
+ true /* onTop */,
+ false /* animate */);
super.onOverExpansionChanged(overExpansion);
}
@@ -823,12 +810,4 @@ public class NotificationPanelView extends PanelView implements
public View getRightIcon() {
return mKeyguardBottomArea.getCameraImageView();
}
-
- private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
- @Override
- public void onShowingDetail(boolean showingDetail) {
- mQsShowingDetail = showingDetail;
- updateQsState();
- }
- };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index c5a9b85..4686933 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -27,10 +27,13 @@ import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.StatusBarState;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -44,13 +47,16 @@ public abstract class PanelView extends FrameLayout {
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
+ protected PhoneStatusBar mStatusBar;
private float mPeekHeight;
+ private float mHintDistance;
private float mInitialOffsetOnTouch;
private float mExpandedFraction = 0;
private float mExpandedHeight = 0;
private boolean mJustPeeked;
private boolean mClosing;
private boolean mTracking;
+ private boolean mTouchSlopExceeded;
private int mTrackingPointer;
protected int mTouchSlop;
@@ -66,6 +72,9 @@ public abstract class PanelView extends FrameLayout {
private float mInitialTouchY;
private float mInitialTouchX;
+ private Interpolator mLinearOutSlowInInterpolator;
+ private Interpolator mBounceInterpolator;
+
protected void onExpandingFinished() {
mBar.onExpandingFinished();
}
@@ -89,6 +98,9 @@ public abstract class PanelView extends FrameLayout {
public PanelView(Context context, AttributeSet attrs) {
super(context, attrs);
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f);
+ mLinearOutSlowInInterpolator =
+ AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
+ mBounceInterpolator = new BounceInterpolator();
}
protected void loadDimens() {
@@ -98,6 +110,7 @@ public abstract class PanelView extends FrameLayout {
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
+ mHintDistance = res.getDimension(R.dimen.hint_move_distance);
}
private void trackMovement(MotionEvent event) {
@@ -138,6 +151,7 @@ public abstract class PanelView extends FrameLayout {
mInitialTouchY = y;
mInitialTouchX = x;
mInitialOffsetOnTouch = mExpandedHeight;
+ mTouchSlopExceeded = false;
if (mVelocityTracker == null) {
initVelocityTracker();
}
@@ -170,16 +184,18 @@ public abstract class PanelView extends FrameLayout {
case MotionEvent.ACTION_MOVE:
float h = y - mInitialTouchY;
- if (waitForTouchSlop && !mTracking && Math.abs(h) > mTouchSlop
- && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
- mInitialOffsetOnTouch = mExpandedHeight;
- mInitialTouchX = x;
- mInitialTouchY = y;
- if (mHeightAnimator != null) {
- mHeightAnimator.cancel(); // end any outstanding animations
+ if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
+ mTouchSlopExceeded = true;
+ if (waitForTouchSlop && !mTracking) {
+ mInitialOffsetOnTouch = mExpandedHeight;
+ mInitialTouchX = x;
+ mInitialTouchY = y;
+ if (mHeightAnimator != null) {
+ mHeightAnimator.cancel(); // end any outstanding animations
+ }
+ onTrackingStarted();
+ h = 0;
}
- onTrackingStarted();
- h = 0;
}
final float newHeight = h + mInitialOffsetOnTouch;
if (newHeight > mPeekHeight) {
@@ -200,8 +216,15 @@ public abstract class PanelView extends FrameLayout {
case MotionEvent.ACTION_CANCEL:
mTrackingPointer = -1;
trackMovement(event);
- boolean expand = flingWithCurrentVelocity();
- onTrackingStopped(expand);
+ if (mTracking && mTouchSlopExceeded) {
+ float vel = getCurrentVelocity();
+ boolean expand = flingExpands(vel);
+ onTrackingStopped(expand);
+ fling(vel, expand);
+ } else {
+ boolean expands = onEmptySpaceClick();
+ onTrackingStopped(expands);
+ }
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
@@ -262,6 +285,7 @@ public abstract class PanelView extends FrameLayout {
}
mInitialTouchY = y;
mInitialTouchX = x;
+ mTouchSlopExceeded = false;
initVelocityTracker();
trackMovement(event);
break;
@@ -285,6 +309,7 @@ public abstract class PanelView extends FrameLayout {
mInitialTouchY = y;
mInitialTouchX = x;
mTracking = true;
+ mTouchSlopExceeded = true;
onTrackingStarted();
return true;
}
@@ -323,18 +348,15 @@ public abstract class PanelView extends FrameLayout {
}
/**
- * @return whether the panel will be expanded after the animation
+ * @param vel the current velocity of the motion
+ * @return whether a fling should expands the panel; contracts otherwise
*/
- private boolean flingWithCurrentVelocity() {
- float vel = getCurrentVelocity();
- boolean expand;
+ private boolean flingExpands(float vel) {
if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
- expand = getExpandedFraction() > 0.5f;
+ return getExpandedFraction() > 0.5f;
} else {
- expand = vel > 0;
+ return vel > 0;
}
- fling(vel, expand);
- return expand;
}
protected void fling(float vel, boolean expand) {
@@ -342,9 +364,10 @@ public abstract class PanelView extends FrameLayout {
float target = expand ? getMaxPanelHeight() : 0.0f;
if (target == mExpandedHeight) {
onExpandingFinished();
+ mBar.panelExpansionChanged(this, mExpandedFraction);
return;
}
- ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, target);
+ ValueAnimator animator = createHeightAnimator(target);
if (expand) {
mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
} else {
@@ -356,12 +379,6 @@ public abstract class PanelView extends FrameLayout {
animator.setDuration((long) (animator.getDuration() / 1.75f));
}
}
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- setExpandedHeight((Float) animation.getAnimatedValue());
- }
- });
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -430,7 +447,7 @@ public abstract class PanelView extends FrameLayout {
public void setExpandedHeightInternal(float h) {
float fh = getMaxPanelHeight();
- mExpandedHeight = Math.min(fh, h);
+ mExpandedHeight = Math.max(0, Math.min(fh, h));
float overExpansion = h - fh;
overExpansion = Math.max(0, overExpansion);
if (overExpansion != mOverExpansion) {
@@ -442,16 +459,14 @@ public abstract class PanelView extends FrameLayout {
}
onHeightUpdated(mExpandedHeight);
- mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh);
+ mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : mExpandedHeight / fh);
}
protected void onOverExpansionChanged(float overExpansion) {
mOverExpansion = overExpansion;
}
- protected void onHeightUpdated(float expandedHeight) {
- requestLayout();
- }
+ protected abstract void onHeightUpdated(float expandedHeight);
/**
* This returns the maximum height of the panel. Children should override this if their
@@ -526,6 +541,101 @@ public abstract class PanelView extends FrameLayout {
}
}
+ protected void startUnlockHintAnimation() {
+
+ // We don't need to hint the user if an animation is already running or the user is changing
+ // the expansion.
+ if (mHeightAnimator != null || mTracking) {
+ return;
+ }
+ cancelPeek();
+ onExpandingStarted();
+ startUnlockHintAnimationPhase1();
+ mStatusBar.onUnlockHintStarted();
+ }
+
+ /**
+ * Phase 1: Move everything upwards.
+ */
+ private void startUnlockHintAnimationPhase1() {
+ float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
+ ValueAnimator animator = createHeightAnimator(target);
+ animator.setDuration(250);
+ animator.setInterpolator(mLinearOutSlowInInterpolator);
+ animator.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCancelled) {
+ mHeightAnimator = null;
+ onExpandingFinished();
+ mStatusBar.onUnlockHintFinished();
+ } else {
+ startUnlockHintAnimationPhase2();
+ }
+ }
+ });
+ animator.start();
+ mHeightAnimator = animator;
+ }
+
+ /**
+ * Phase 2: Bounce down.
+ */
+ private void startUnlockHintAnimationPhase2() {
+ ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
+ animator.setDuration(450);
+ animator.setInterpolator(mBounceInterpolator);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mHeightAnimator = null;
+ onExpandingFinished();
+ mStatusBar.onUnlockHintFinished();
+ }
+ });
+ animator.start();
+ mHeightAnimator = animator;
+ }
+
+ private ValueAnimator createHeightAnimator(float targetHeight) {
+ ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ setExpandedHeight((Float) animation.getAnimatedValue());
+ }
+ });
+ return animator;
+ }
+
+ /**
+ * Gets called when the user performs a click anywhere in the empty area of the panel.
+ *
+ * @return whether the panel will be expanded after the action performed by this method
+ */
+ private boolean onEmptySpaceClick() {
+ switch (mStatusBar.getBarState()) {
+ case StatusBarState.KEYGUARD:
+ startUnlockHintAnimation();
+ return true;
+ case StatusBarState.SHADE_LOCKED:
+ // TODO: Go to Keyguard again.
+ return true;
+ case StatusBarState.SHADE:
+ collapse();
+ return false;
+ default:
+ return true;
+ }
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
+ " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s"
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 0e5b7e1..b1216e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -980,8 +980,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
private void addHeadsUpView() {
+ int headsUpHeight = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.heads_up_window_height);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT, headsUpHeight,
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
@@ -1050,7 +1052,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
if (shadeEntry == null) {
return;
}
- if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification)) {
+ if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification, ranking)) {
// Forward the ranking so we can sort the new notification.
mNotificationData.updateRanking(ranking);
return;
@@ -1114,6 +1116,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
@Override
+ protected void updateRankingInternal(Ranking ranking) {
+ mNotificationData.updateRanking(ranking);
+ mIntercepted.retryIntercepts(ranking);
+ updateNotifications();
+ }
+
+ @Override
public void removeNotificationInternal(String key, Ranking ranking) {
StatusBarNotification old = removeNotificationViews(key, ranking);
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
@@ -2348,16 +2357,24 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
};
- public void startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) {
+ public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned) {
if (onlyProvisioned && !isDeviceProvisioned()) return;
- try {
- // Dismiss the lock screen when Settings starts.
- ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
- } catch (RemoteException e) {
- }
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
- animateCollapsePanels();
+
+ dismissKeyguardThenExecute(new OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ try {
+ // Dismiss the lock screen when Settings starts.
+ ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+ } catch (RemoteException e) {
+ }
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ animateCollapsePanels();
+
+ return DELAY_DISMISS_TO_ACTIVITY_LAUNCH;
+ }
+ });
}
private View.OnClickListener mClockClickListener = new View.OnClickListener() {
@@ -2418,7 +2435,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
};
@Override
- protected void startNotificationActivity(OnDismissAction action) {
+ protected void dismissKeyguardThenExecute(OnDismissAction action) {
if (mStatusBarKeyguardViewManager.isShowing()) {
mStatusBarKeyguardViewManager.dismissWithAction(action);
} else {
@@ -2940,15 +2957,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
public void onTrackingStarted() {
- if (mState == StatusBarState.KEYGUARD) {
- mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
- }
+ }
+
+ public void onUnlockHintStarted() {
+ mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
+ }
+
+ public void onUnlockHintFinished() {
+ mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
}
public void onTrackingStopped(boolean expand) {
- if (mState == StatusBarState.KEYGUARD) {
- mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
- }
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
if (!expand && !mUnlockMethodCache.isMethodInsecure()) {
showBouncer();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 1344703..7c87580 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -95,7 +95,6 @@ public class QSTileHost implements QSTile.Host {
mTiles.add(new LocationTile(this));
mTiles.add(new CastTile(this));
mTiles.add(new HotspotTile(this));
- mTiles.add(new BugreportTile(this));
mUserTracker = new CurrentUserTracker(mContext) {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 6156fc3..1264d75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -19,16 +19,12 @@ package com.android.systemui.statusbar.phone;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.View;
import android.view.ViewTreeObserver;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
/**
* Controls both the scrim behind the notifications and in front of the notifications (when a
@@ -53,6 +49,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
private boolean mAnimateChange;
private boolean mUpdatePending;
private boolean mExpanding;
+ private boolean mAnimateKeyguardFadingOut;
+ private long mDurationOverride = -1;
+ private long mAnimationDelay;
+ private Runnable mOnAnimationFinished;
+ private boolean mAnimationStarted;
private final Interpolator mInterpolator = new DecelerateInterpolator();
@@ -87,14 +88,26 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
scheduleUpdate();
}
+ public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) {
+ mAnimateKeyguardFadingOut = true;
+ mDurationOverride = duration;
+ mAnimationDelay = delay;
+ mAnimateChange = true;
+ mOnAnimationFinished = onAnimationFinished;
+ scheduleUpdate();
+ }
+
private void scheduleUpdate() {
if (mUpdatePending) return;
+
+ // Make sure that a frame gets scheduled.
+ mScrimBehind.invalidate();
mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this);
mUpdatePending = true;
}
private void updateScrims() {
- if (!mKeyguardShowing) {
+ if (!mKeyguardShowing || mAnimateKeyguardFadingOut) {
updateScrimNormal();
setScrimInFrontColor(0);
} else {
@@ -170,8 +183,20 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
}
});
anim.setInterpolator(mInterpolator);
- anim.setDuration(ANIMATION_DURATION);
+ anim.setStartDelay(mAnimationDelay);
+ anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mOnAnimationFinished != null) {
+ mOnAnimationFinished.run();
+ mOnAnimationFinished = null;
+ }
+ }
+ });
anim.start();
+ mAnimationStarted = true;
}
private int getBackgroundAlpha(View scrim) {
@@ -188,6 +213,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
mUpdatePending = false;
updateScrims();
+ mAnimateKeyguardFadingOut = false;
+ mDurationOverride = -1;
+ mAnimationDelay = 0;
+
+ // Make sure that we always call the listener even if we didn't start an animation.
+ if (!mAnimationStarted && mOnAnimationFinished != null) {
+ mOnAnimationFinished.run();
+ mOnAnimationFinished = null;
+ }
+ mAnimationStarted = false;
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 3245f1a..13d3291 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -44,6 +44,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
private static final float EXPANSION_RUBBERBAND_FACTOR = 0.35f;
private boolean mExpanded;
+ private boolean mOverscrolled;
private boolean mKeyguardShowing;
private View mBackground;
@@ -125,10 +126,12 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
return mExpandedHeight;
}
- public void setExpanded(boolean expanded) {
+ public void setExpanded(boolean expanded, boolean overscrolled) {
boolean changed = expanded != mExpanded;
+ boolean overscrollChanged = overscrolled != mOverscrolled;
mExpanded = expanded;
- if (changed) {
+ mOverscrolled = overscrolled;
+ if (changed || overscrollChanged) {
updateHeights();
updateVisibilities();
updateSystemIconsLayoutParams();
@@ -136,7 +139,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
updateZTranslation();
updateClickTargets();
if (mQSPanel != null) {
- mQSPanel.setExpanded(expanded);
+ mQSPanel.setExpanded(expanded && !overscrolled);
}
}
}
@@ -184,13 +187,13 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE);
mDate.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
- mSettingsButton.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+ mSettingsButton.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
mBrightnessContainer.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
if (mStatusIcons != null) {
- mStatusIcons.setVisibility(!mExpanded ? View.VISIBLE : View.GONE);
+ mStatusIcons.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
}
if (mSignalCluster != null) {
- mSignalCluster.setVisibility(!mExpanded ? View.VISIBLE : View.GONE);
+ mSignalCluster.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
}
}
@@ -293,5 +296,15 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
public void setQSPanel(QSPanel qsp) {
mQSPanel = qsp;
+ if (mQSPanel != null) {
+ mQSPanel.setCallback(mQsPanelCallback);
+ }
}
+
+ private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
+ @Override
+ public void onShowingDetail(boolean showingDetail) {
+ mBrightnessContainer.animate().alpha(showingDetail ? 0 : 1).withLayer().start();
+ }
+ };
}
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 d5551b8..e3145a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone;
import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Slog;
import android.view.KeyEvent;
import android.view.View;
@@ -183,11 +184,23 @@ public class StatusBarKeyguardViewManager {
/**
* Hides the keyguard view
*/
- public void hide() {
+ public void hide(long startTime, long fadeoutDuration) {
mShowing = false;
mPhoneStatusBar.hideKeyguard();
+ mStatusBarWindowManager.setKeyguardFadingAway(true);
mStatusBarWindowManager.setKeyguardShowing(false);
- mBouncer.hide(true /* destroyView */);
+ long uptimeMillis = SystemClock.uptimeMillis();
+ long delay = startTime - uptimeMillis;
+ if (delay < 0) {
+ delay = 0;
+ }
+ mBouncer.animateHide(delay, fadeoutDuration);
+ mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() {
+ @Override
+ public void run() {
+ mStatusBarWindowManager.setKeyguardFadingAway(false);
+ }
+ });
mViewMediatorCallback.keyguardGone();
updateStates();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index b7bf6cd..fe57cef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -124,7 +124,8 @@ public class StatusBarWindowManager {
}
private void applyHeight(State state) {
- boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded;
+ boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded
+ || state.keyguardFadingAway;
if (expanded) {
mLp.height = ViewGroup.LayoutParams.MATCH_PARENT;
} else {
@@ -201,6 +202,11 @@ public class StatusBarWindowManager {
apply(mCurrentState);
}
+ public void setKeyguardFadingAway(boolean keyguardFadingAway) {
+ mCurrentState.keyguardFadingAway = keyguardFadingAway;
+ apply(mCurrentState);
+ }
+
/**
* @param state The {@link StatusBarState} of the status bar.
*/
@@ -217,6 +223,7 @@ public class StatusBarWindowManager {
boolean statusBarFocusable;
long keyguardUserActivityTimeout;
boolean bouncerShowing;
+ boolean keyguardFadingAway;
/**
* The {@link BaseStatusBar} state from the status bar.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index f4145cd..8e9fb30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -16,14 +16,18 @@
package com.android.systemui.statusbar.policy;
-import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
-
public interface BluetoothController {
- void addStateChangedCallback(BluetoothStateChangeCallback callback);
- void removeStateChangedCallback(BluetoothStateChangeCallback callback);
+ void addStateChangedCallback(Callback callback);
+ void removeStateChangedCallback(Callback callback);
boolean isBluetoothSupported();
boolean isBluetoothEnabled();
boolean isBluetoothConnected();
+ boolean isBluetoothConnecting();
+ String getLastDeviceName();
void setBluetoothEnabled(boolean enabled);
+
+ public interface Callback {
+ void onBluetoothStateChange(boolean enabled, boolean connecting);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 5a19881..117bf61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.policy;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -31,14 +30,13 @@ import java.util.Set;
public class BluetoothControllerImpl extends BroadcastReceiver implements BluetoothController {
private static final String TAG = "StatusBar.BluetoothController";
+ private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+ private final Set<BluetoothDevice> mBondedDevices = new HashSet<BluetoothDevice>();
private final BluetoothAdapter mAdapter;
- private boolean mEnabled = false;
-
- private Set<BluetoothDevice> mBondedDevices = new HashSet<BluetoothDevice>();
-
- private ArrayList<BluetoothStateChangeCallback> mChangeCallbacks =
- new ArrayList<BluetoothStateChangeCallback>();
+ private boolean mEnabled;
+ private boolean mConnecting;
+ private BluetoothDevice mLastDevice;
public BluetoothControllerImpl(Context context) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -57,14 +55,14 @@ public class BluetoothControllerImpl extends BroadcastReceiver implements Blueto
updateBondedBluetoothDevices();
}
- public void addStateChangedCallback(BluetoothStateChangeCallback cb) {
- mChangeCallbacks.add(cb);
+ public void addStateChangedCallback(Callback cb) {
+ mCallbacks.add(cb);
fireCallback(cb);
}
@Override
- public void removeStateChangedCallback(BluetoothStateChangeCallback cb) {
- mChangeCallbacks.remove(cb);
+ public void removeStateChangedCallback(Callback cb) {
+ mCallbacks.remove(cb);
}
@Override
@@ -79,6 +77,12 @@ public class BluetoothControllerImpl extends BroadcastReceiver implements Blueto
}
@Override
+ public boolean isBluetoothConnecting() {
+ return mAdapter != null
+ && mAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTING;
+ }
+
+ @Override
public void setBluetoothEnabled(boolean enabled) {
if (mAdapter != null) {
if (enabled) {
@@ -99,6 +103,13 @@ public class BluetoothControllerImpl extends BroadcastReceiver implements Blueto
}
@Override
+ public String getLastDeviceName() {
+ return mLastDevice != null ? mLastDevice.getName()
+ : mBondedDevices.size() == 1 ? mBondedDevices.iterator().next().getName()
+ : null;
+ }
+
+ @Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
@@ -106,6 +117,11 @@ public class BluetoothControllerImpl extends BroadcastReceiver implements Blueto
handleAdapterStateChange(
intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR));
}
+ if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
+ mConnecting = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, -1)
+ == BluetoothAdapter.STATE_CONNECTING;
+ mLastDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ }
fireCallbacks();
updateBondedBluetoothDevices();
}
@@ -131,12 +147,12 @@ public class BluetoothControllerImpl extends BroadcastReceiver implements Blueto
}
private void fireCallbacks() {
- for (BluetoothStateChangeCallback cb : mChangeCallbacks) {
+ for (Callback cb : mCallbacks) {
fireCallback(cb);
}
}
- private void fireCallback(BluetoothStateChangeCallback cb) {
- cb.onBluetoothStateChange(mEnabled);
+ private void fireCallback(Callback cb) {
+ cb.onBluetoothStateChange(mEnabled, mConnecting);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index cadb44a..f978833 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -20,6 +20,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.widget.TextView;
@@ -29,8 +30,6 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
-import libcore.icu.ICU;
-
public class DateView extends TextView {
private static final String TAG = "DateView";
@@ -87,7 +86,7 @@ public class DateView extends TextView {
if (mDateFormat == null) {
final String dateFormat = getContext().getString(R.string.system_ui_date_pattern);
final Locale l = Locale.getDefault();
- final String fmt = ICU.getBestDateTimePattern(dateFormat, l.toString());
+ final String fmt = DateFormat.getBestDateTimePattern(l, dateFormat);
mDateFormat = new SimpleDateFormat(fmt, l);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index 9271e71..ac26da2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -47,7 +47,7 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
int[] mTmpTwoArray = new int[2];
private final int mTouchSensitivityDelay;
- private final float mMaxAlpha = 0.95f;
+ private final float mMaxAlpha = 1f;
private SwipeHelper mSwipeHelper;
private EdgeSwipeHelper mEdgeSwipeHelper;
@@ -114,7 +114,7 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
float pagingTouchSlop = viewConfiguration.getScaledPagingTouchSlop();
float touchSlop = viewConfiguration.getScaledTouchSlop();
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
- mSwipeHelper.setMaxAlpha(mMaxAlpha);
+ mSwipeHelper.setMaxSwipeProgress(mMaxAlpha);
mEdgeSwipeHelper = new EdgeSwipeHelper(touchSlop);
int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
@@ -184,7 +184,13 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Outline o = new Outline();
- o.setRect(0, 0, mContentHolder.getWidth(), mContentHolder.getHeight());
+
+ // Apply padding to shadow.
+ int outlineLeft = mContentHolder.getPaddingLeft();
+ int outlineTop = mContentHolder.getPaddingTop();
+ o.setRect(outlineLeft, outlineTop,
+ mContentHolder.getWidth() - outlineLeft - mContentHolder.getPaddingRight(),
+ mContentHolder.getHeight() - outlineTop - mContentHolder.getPaddingBottom());
mContentHolder.setOutline(o);
}
@@ -246,6 +252,12 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
}
@Override
+ public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
+ getBackground().setAlpha((int) (255 * swipeProgress));
+ return false;
+ }
+
+ @Override
public View getChildAtPosition(MotionEvent ev) {
return mContentHolder;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 58176b9..6892b85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -18,13 +18,10 @@ package com.android.systemui.statusbar.stack;
import android.content.Context;
import android.content.res.Configuration;
-
import android.graphics.Canvas;
import android.graphics.Paint;
-
import android.util.AttributeSet;
import android.util.Log;
-
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -40,8 +37,8 @@ import com.android.systemui.SwipeHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.SpeedBumpView;
-import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
import com.android.systemui.statusbar.policy.ScrollAdapter;
+import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
import java.util.ArrayList;
@@ -121,6 +118,7 @@ public class NotificationStackScrollLayout extends ViewGroup
private float mOverScrolledBottomPixels;
private OnChildLocationsChangedListener mListener;
+ private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
private boolean mNeedsAnimation;
private boolean mTopPaddingNeedsAnimation;
@@ -449,6 +447,11 @@ public class NotificationStackScrollLayout extends ViewGroup
}
}
+ @Override
+ public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
+ return false;
+ }
+
public void onBeginDrag(View v) {
setSwipingInProgress(true);
mAmbientState.onBeginDrag(v);
@@ -870,9 +873,24 @@ public class NotificationStackScrollLayout extends ViewGroup
setOverScrolledPixels(amount / RUBBER_BAND_FACTOR, onTop);
mAmbientState.setOverScrollAmount(amount, onTop);
requestChildrenUpdate();
+ if (onTop) {
+ float scrollAmount = mOwnScrollY < 0 ? -mOwnScrollY : 0;
+ notifyOverscrollTopListener(scrollAmount + amount);
+ }
}
}
+ private void notifyOverscrollTopListener(float amount) {
+ if (mOverscrollTopChangedListener != null) {
+ mOverscrollTopChangedListener.onOverscrollTopChanged(amount);
+ }
+ }
+
+ public void setOverscrollTopChangedListener(
+ OnOverscrollTopChangedListener overscrollTopChangedListener) {
+ mOverscrollTopChangedListener = overscrollTopChangedListener;
+ }
+
public float getCurrentOverScrollAmount(boolean top) {
return mAmbientState.getOverScrollAmount(top);
}
@@ -908,6 +926,12 @@ public class NotificationStackScrollLayout extends ViewGroup
onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
invalidateParentIfNeeded();
updateChildren();
+ float overScrollTop = getCurrentOverScrollAmount(true);
+ if (mOwnScrollY < 0) {
+ notifyOverscrollTopListener(-mOwnScrollY + overScrollTop);
+ } else {
+ notifyOverscrollTopListener(overScrollTop);
+ }
}
} else {
customScrollTo(scrollY);
@@ -1591,6 +1615,13 @@ public class NotificationStackScrollLayout extends ViewGroup
public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout);
}
+ /**
+ * A listener that gets notified when the overscroll at the top has changed.
+ */
+ public interface OnOverscrollTopChangedListener {
+ public void onOverscrollTopChanged(float amount);
+ }
+
static class AnimationEvent {
static AnimationFilter[] FILTERS = new AnimationFilter[] {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index f41ab3a..2edd7d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -715,6 +715,9 @@ public class StackStateAnimator {
public void animateOverScrollToAmount(float targetAmount, final boolean onTop) {
final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
+ if (targetAmount == startOverScrollAmount) {
+ return;
+ }
cancelOverScrollAnimators(onTop);
ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount,
targetAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index c2bd1cb..faea8de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.tv;
import android.os.IBinder;
+import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import android.view.View;
@@ -54,11 +55,15 @@ public class TvStatusBar extends BaseStatusBar {
}
@Override
+ protected void updateRankingInternal(Ranking ranking) {
+ }
+
+ @Override
public void updateNotification(StatusBarNotification notification) {
}
@Override
- protected void removeNotificationInternal(String key, Ranking ranking) {
+ public void removeNotificationInternal(String key, Ranking ranking) {
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index cbfc641..898b46e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -16,6 +16,8 @@
package com.android.systemui.volume;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
@@ -42,12 +44,15 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
-import android.widget.FrameLayout;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -81,6 +86,7 @@ public class VolumePanel extends Handler {
private static final int FREE_DELAY = 10000;
private static final int TIMEOUT_DELAY = 3000;
private static final int TIMEOUT_DELAY_EXPANDED = 10000;
+ private static final float ICON_PULSE_SCALE = 1.3f;
private static final int MSG_VOLUME_CHANGED = 0;
private static final int MSG_FREE_RESOURCES = 1;
@@ -105,6 +111,7 @@ public class VolumePanel extends Handler {
protected final Context mContext;
private final AudioManager mAudioManager;
private final ZenModeController mZenController;
+ private final Interpolator mFastOutSlowInInterpolator;
private boolean mRingIsSilent;
private boolean mVoiceCapable;
private boolean mZenModeCapable;
@@ -220,6 +227,7 @@ public class VolumePanel extends Handler {
ViewGroup group;
ImageView icon;
SeekBar seekbarView;
+ View seekbarContainer;
int iconRes;
int iconMuteRes;
}
@@ -273,6 +281,8 @@ public class VolumePanel extends Handler {
mParent = parent;
mZenController = zenController;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.fast_out_slow_in);
// For now, only show master volume if master volume is supported
final Resources res = context.getResources();
@@ -284,7 +294,6 @@ public class VolumePanel extends Handler {
}
}
if (LOGD) Log.d(mTag, String.format("new VolumePanel hasParent=%s", parent != null));
- final int layoutId = com.android.systemui.R.layout.volume_panel;
if (parent == null) {
// dialog mode
mDialog = new Dialog(context) {
@@ -318,15 +327,7 @@ public class VolumePanel extends Handler {
| LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| LayoutParams.FLAG_HARDWARE_ACCELERATED);
mDialog.setCanceledOnTouchOutside(true);
- // temporary workaround for no window shadows
- final FrameLayout layout = new FrameLayout(mContext);
- final int z = res.getDimensionPixelSize(com.android.systemui.R.dimen.volume_panel_z);
- layout.setPadding(z, z, z, z);
- final View v = LayoutInflater.from(mContext).inflate(layoutId, layout, false);
- v.setBackgroundResource(com.android.systemui.R.drawable.qs_panel_background);
- v.setElevation(z);
- layout.addView(v);
- mDialog.setContentView(layout);
+ mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
mDialog.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
@@ -336,6 +337,7 @@ public class VolumePanel extends Handler {
});
mDialog.create();
+ // temporary workaround, until we support window-level shadows
mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
mView = window.findViewById(R.id.content);
@@ -350,7 +352,8 @@ public class VolumePanel extends Handler {
} else {
// embedded mode
mDialog = null;
- mView = LayoutInflater.from(mContext).inflate(layoutId, parent, true);
+ mView = LayoutInflater.from(mContext).inflate(
+ com.android.systemui.R.layout.volume_panel, parent, true);
}
mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
@@ -460,6 +463,26 @@ public class VolumePanel extends Handler {
sc.iconRes = streamRes.iconRes;
sc.iconMuteRes = streamRes.iconMuteRes;
sc.icon.setImageResource(sc.iconRes);
+ sc.icon.setClickable(isNotificationOrRing(streamType));
+ if (sc.icon.isClickable()) {
+ sc.icon.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ resetTimeout();
+ toggle(sc);
+ }
+ });
+ sc.icon.setOnLongClickListener(new OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ resetTimeout();
+ longToggle(sc);
+ return true;
+ }
+ });
+ }
+ sc.seekbarContainer =
+ sc.group.findViewById(com.android.systemui.R.id.seekbar_container);
sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
@@ -470,6 +493,26 @@ public class VolumePanel extends Handler {
}
}
+ private void toggle(StreamControl sc) {
+ if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
+ mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
+ postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
+ } else {
+ mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+ postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
+ }
+ }
+
+ private void longToggle(StreamControl sc) {
+ if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
+ mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+ postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
+ } else {
+ mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
+ postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI); // disable the slider
+ }
+ }
+
private void reorderSliders(int activeStreamType) {
mSliderPanel.removeAllViews();
@@ -493,21 +536,62 @@ public class VolumePanel extends Handler {
// Force reloading the image resource
sc.icon.setImageDrawable(null);
sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
- if (((sc.streamType == AudioManager.STREAM_RING) ||
- (sc.streamType == AudioManager.STREAM_NOTIFICATION)) &&
+ if (isNotificationOrRing(sc.streamType) &&
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
sc.icon.setImageResource(com.android.systemui.R.drawable.ic_ringer_vibrate);
}
+ updateSliderEnabled(sc, muted, false);
+ }
+
+ private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
+ final boolean wasEnabled = sc.seekbarView.isEnabled();
if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
// never disable touch interactions for remote playback, the muting is not tied to
// the state of the phone.
sc.seekbarView.setEnabled(true);
- } else if ((sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
+ } else if (fixedVolume ||
+ (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
(sConfirmSafeVolumeDialog != null)) {
sc.seekbarView.setEnabled(false);
+ } else if (isNotificationOrRing(sc.streamType)
+ && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
+ sc.seekbarView.setEnabled(false);
} else {
sc.seekbarView.setEnabled(true);
}
+ // pulse the ringer icon when the disabled slider is touched in silent mode
+ if (sc.icon.isClickable() && wasEnabled != sc.seekbarView.isEnabled()) {
+ if (sc.seekbarView.isEnabled()) {
+ sc.seekbarContainer.setOnTouchListener(null);
+ } else {
+ sc.seekbarContainer.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ resetTimeout();
+ pulseIcon(sc.icon);
+ return false;
+ }
+ });
+ }
+ }
+ }
+
+ private void pulseIcon(final ImageView icon) {
+ if (icon.getScaleX() != 1) return; // already running
+ icon.animate().cancel();
+ icon.animate().scaleX(ICON_PULSE_SCALE).scaleY(ICON_PULSE_SCALE)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ icon.animate().scaleX(1).scaleY(1).setListener(null);
+ }
+ });
+ }
+
+ private static boolean isNotificationOrRing(int streamType) {
+ return streamType == AudioManager.STREAM_RING
+ || streamType == AudioManager.STREAM_NOTIFICATION;
}
public void setZenModePanelCallback(ZenModePanel.Callback callback) {
@@ -562,8 +646,7 @@ public class VolumePanel extends Handler {
private void updateZenMode(boolean zen) {
if (mZenModeCapable) {
- final boolean show = mActiveStreamType == AudioManager.STREAM_NOTIFICATION
- || mActiveStreamType == AudioManager.STREAM_RING;
+ final boolean show = isNotificationOrRing(mActiveStreamType);
mExpandButton.setVisibility(show ? View.VISIBLE : View.GONE);
mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
@@ -576,7 +659,7 @@ public class VolumePanel extends Handler {
public void postZenModeChanged(boolean zen) {
removeMessages(MSG_ZEN_MODE_CHANGED);
- obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0).sendToTarget();
+ obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0, 0).sendToTarget();
}
public void postVolumeChanged(int streamType, int flags) {
@@ -654,7 +737,7 @@ public class VolumePanel extends Handler {
public void postLayoutDirection(int layoutDirection) {
removeMessages(MSG_LAYOUT_DIRECTION);
- obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection).sendToTarget();
+ obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
}
/**
@@ -790,15 +873,8 @@ public class VolumePanel extends Handler {
}
sc.seekbarView.setProgress(index);
- if (((flags & AudioManager.FLAG_FIXED_VOLUME) != 0) ||
- (streamType != mAudioManager.getMasterStreamType() &&
- streamType != AudioService.STREAM_REMOTE_MUSIC &&
- isMuted(streamType)) ||
- sConfirmSafeVolumeDialog != null) {
- sc.seekbarView.setEnabled(false);
- } else {
- sc.seekbarView.setEnabled(true);
- }
+ updateSliderEnabled(sc, isMuted(streamType),
+ (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
}
if (!isShowing()) {
@@ -822,6 +898,11 @@ public class VolumePanel extends Handler {
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
}
+
+ // Pulse the slider icon if an adjustment was suppressed due to silent mode.
+ if (sc != null && (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
+ pulseIcon(sc.icon);
+ }
}
private boolean isShowing() {
@@ -1173,7 +1254,7 @@ public class VolumePanel extends Handler {
private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
public void onZenChanged(boolean zen) {
- updateZenMode(zen);
+ postZenModeChanged(zen);
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 77d267e..c338563 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -45,6 +45,7 @@ public class ZenModePanel extends LinearLayout {
private static final int[] MINUTES = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
+ private final Context mContext;
private final LayoutInflater mInflater;
private final HashSet<RadioButton> mRadioButtons = new HashSet<RadioButton>();
private final H mHandler = new H();
@@ -56,6 +57,7 @@ public class ZenModePanel extends LinearLayout {
public ZenModePanel(Context context, AttributeSet attrs) {
super(context, attrs);
+ mContext = context;
mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
}
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 673ce0b..0c16b78 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -35,6 +35,7 @@ import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.ConnectivityManager;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -51,6 +52,7 @@ import android.service.dreams.IDreamManager;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.TypedValue;
@@ -185,7 +187,8 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
// If we only have 1 item and it's a simple press action, just do this action.
if (mAdapter.getCount() == 1
- && mAdapter.getItem(0) instanceof SinglePressAction) {
+ && mAdapter.getItem(0) instanceof SinglePressAction
+ && !(mAdapter.getItem(0) instanceof LongPressAction)) {
((SinglePressAction) mAdapter.getItem(0)).onPress();
} else {
WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
@@ -262,7 +265,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
continue;
}
if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
- mItems.add(getPowerAction());
+ mItems.add(new PowerAction());
} else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
mItems.add(mAirplaneModeOn);
} else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)
@@ -300,7 +303,11 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
long id) {
- return mAdapter.getItem(position).onLongPress();
+ final Action action = mAdapter.getItem(position);
+ if (action instanceof LongPressAction) {
+ return ((LongPressAction) action).onLongPress();
+ }
+ return false;
}
});
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
@@ -310,34 +317,38 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
return dialog;
}
- private Action getPowerAction() {
- return new SinglePressAction(
- com.android.internal.R.drawable.ic_lock_power_off,
- R.string.global_action_power_off) {
+ private final class PowerAction extends SinglePressAction implements LongPressAction {
+ private PowerAction() {
+ super(com.android.internal.R.drawable.ic_lock_power_off,
+ R.string.global_action_power_off);
+ }
- public void onPress() {
- // shutdown by making sure radio and power are handled accordingly.
- mWindowManagerFuncs.shutdown(true);
- }
+ @Override
+ public boolean onLongPress() {
+ mWindowManagerFuncs.rebootSafeMode(true);
+ return true;
+ }
- public boolean onLongPress() {
- mWindowManagerFuncs.rebootSafeMode(true);
- return true;
- }
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
- public boolean showDuringKeyguard() {
- return true;
- }
+ @Override
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
- public boolean showBeforeProvisioning() {
- return true;
- }
- };
+ @Override
+ public void onPress() {
+ // shutdown by making sure radio and power are handled accordingly.
+ mWindowManagerFuncs.shutdown(false /* confirm */);
+ }
}
private Action getBugReportAction() {
- return new SinglePressAction(com.android.internal.R.drawable.stat_sys_adb,
- R.string.global_action_bug_report) {
+ return new SinglePressAction(com.android.internal.R.drawable.ic_lock_bugreport,
+ R.string.bugreport_title) {
public void onPress() {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
@@ -367,10 +378,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
dialog.show();
}
- public boolean onLongPress() {
- return false;
- }
-
public boolean showDuringKeyguard() {
return true;
}
@@ -378,6 +385,14 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
public boolean showBeforeProvisioning() {
return false;
}
+
+ @Override
+ public String getStatus() {
+ return mContext.getString(
+ com.android.internal.R.string.bugreport_status,
+ Build.VERSION.RELEASE,
+ Build.ID);
+ }
};
}
@@ -393,11 +408,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
@Override
- public boolean onLongPress() {
- return false;
- }
-
- @Override
public boolean showDuringKeyguard() {
return true;
}
@@ -583,8 +593,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
void onPress();
- public boolean onLongPress();
-
/**
* @return whether this action should appear in the dialog when the keygaurd
* is showing.
@@ -601,6 +609,13 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
/**
+ * An action that also supports long press.
+ */
+ private interface LongPressAction extends Action {
+ boolean onLongPress();
+ }
+
+ /**
* A single press action maintains no state, just responds to a press
* and takes an action.
*/
@@ -635,12 +650,12 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
return true;
}
- abstract public void onPress();
-
- public boolean onLongPress() {
- return false;
+ public String getStatus() {
+ return null;
}
+ abstract public void onPress();
+
public View create(
Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
View v = inflater.inflate(R.layout.global_actions_item, parent, false);
@@ -648,7 +663,13 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
ImageView icon = (ImageView) v.findViewById(R.id.icon);
TextView messageView = (TextView) v.findViewById(R.id.message);
- v.findViewById(R.id.status).setVisibility(View.GONE);
+ TextView statusView = (TextView) v.findViewById(R.id.status);
+ final String status = getStatus();
+ if (!TextUtils.isEmpty(status)) {
+ statusView.setText(status);
+ } else {
+ statusView.setVisibility(View.GONE);
+ }
if (mIcon != null) {
icon.setImageDrawable(mIcon);
icon.setScaleType(ScaleType.CENTER_CROP);
@@ -769,10 +790,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
changeStateFromPress(nowOn);
}
- public boolean onLongPress() {
- return false;
- }
-
public boolean isEnabled() {
return !mState.inTransition();
}
@@ -862,10 +879,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
public void onPress() {
}
- public boolean onLongPress() {
- return false;
- }
-
public boolean showDuringKeyguard() {
return true;
}
diff --git a/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java b/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java
index 3cf7e82..8c8209f 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java
@@ -30,6 +30,7 @@ import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.PrintWriter;
/**
* Stores a mapping of global keys.
@@ -123,4 +124,21 @@ final class GlobalKeyManager {
}
}
}
+
+ public void dump(String prefix, PrintWriter pw) {
+ final int numKeys = mKeyMapping.size();
+ if (numKeys == 0) {
+ pw.print(prefix); pw.println("mKeyMapping.size=0");
+ return;
+ }
+ pw.print(prefix); pw.println("mKeyMapping={");
+ for (int i = 0; i < numKeys; ++i) {
+ pw.print(" ");
+ pw.print(prefix);
+ pw.print(KeyEvent.keyCodeToString(mKeyMapping.keyAt(i)));
+ pw.print("=");
+ pw.println(mKeyMapping.valueAt(i).flattenToString());
+ }
+ pw.print(prefix); pw.println("}");
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 6b0095a..5dc9e58 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1434,30 +1434,58 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
final int features = getLocalFeatures();
if (value == PROGRESS_VISIBILITY_ON) {
if ((features & (1 << FEATURE_PROGRESS)) != 0) {
- int level = horizontalProgressBar.getProgress();
- int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
- View.VISIBLE : View.INVISIBLE;
- horizontalProgressBar.setVisibility(visibility);
+ if (horizontalProgressBar != null) {
+ int level = horizontalProgressBar.getProgress();
+ int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
+ View.VISIBLE : View.INVISIBLE;
+ horizontalProgressBar.setVisibility(visibility);
+ } else {
+ Log.e(TAG, "Horizontal progress bar not located in current window decor");
+ }
}
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
- circularProgressBar.setVisibility(View.VISIBLE);
+ if (circularProgressBar != null) {
+ circularProgressBar.setVisibility(View.VISIBLE);
+ } else {
+ Log.e(TAG, "Circular progress bar not located in current window decor");
+ }
}
} else if (value == PROGRESS_VISIBILITY_OFF) {
if ((features & (1 << FEATURE_PROGRESS)) != 0) {
- horizontalProgressBar.setVisibility(View.GONE);
+ if (horizontalProgressBar != null) {
+ horizontalProgressBar.setVisibility(View.GONE);
+ } else {
+ Log.e(TAG, "Horizontal progress bar not located in current window decor");
+ }
}
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
- circularProgressBar.setVisibility(View.GONE);
+ if (circularProgressBar != null) {
+ circularProgressBar.setVisibility(View.GONE);
+ } else {
+ Log.e(TAG, "Circular progress bar not located in current window decor");
+ }
}
} else if (value == PROGRESS_INDETERMINATE_ON) {
- horizontalProgressBar.setIndeterminate(true);
+ if (horizontalProgressBar != null) {
+ horizontalProgressBar.setIndeterminate(true);
+ } else {
+ Log.e(TAG, "Horizontal progress bar not located in current window decor");
+ }
} else if (value == PROGRESS_INDETERMINATE_OFF) {
- horizontalProgressBar.setIndeterminate(false);
+ if (horizontalProgressBar != null) {
+ horizontalProgressBar.setIndeterminate(false);
+ } else {
+ Log.e(TAG, "Horizontal progress bar not located in current window decor");
+ }
} else if (PROGRESS_START <= value && value <= PROGRESS_END) {
// We want to set the progress value before testing for visibility
// so that when the progress bar becomes visible again, it has the
// correct level.
- horizontalProgressBar.setProgress(value - PROGRESS_START);
+ if (horizontalProgressBar != null) {
+ horizontalProgressBar.setProgress(value - PROGRESS_START);
+ } else {
+ Log.e(TAG, "Horizontal progress bar not located in current window decor");
+ }
if (value < PROGRESS_END) {
showProgressBars(horizontalProgressBar, circularProgressBar);
@@ -1465,7 +1493,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
hideProgressBars(horizontalProgressBar, circularProgressBar);
}
} else if (PROGRESS_SECONDARY_START <= value && value <= PROGRESS_SECONDARY_END) {
- horizontalProgressBar.setSecondaryProgress(value - PROGRESS_SECONDARY_START);
+ if (horizontalProgressBar != null) {
+ horizontalProgressBar.setSecondaryProgress(value - PROGRESS_SECONDARY_START);
+ } else {
+ Log.e(TAG, "Horizontal progress bar not located in current window decor");
+ }
showProgressBars(horizontalProgressBar, circularProgressBar);
}
@@ -1475,11 +1507,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private void showProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) {
final int features = getLocalFeatures();
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
- spinnyProgressBar.getVisibility() == View.INVISIBLE) {
+ spinnyProgressBar != null && spinnyProgressBar.getVisibility() == View.INVISIBLE) {
spinnyProgressBar.setVisibility(View.VISIBLE);
}
// Only show the progress bars if the primary progress is not complete
- if ((features & (1 << FEATURE_PROGRESS)) != 0 &&
+ if ((features & (1 << FEATURE_PROGRESS)) != 0 && horizontalProgressBar != null &&
horizontalProgressBar.getProgress() < 10000) {
horizontalProgressBar.setVisibility(View.VISIBLE);
}
@@ -1490,11 +1522,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
Animation anim = AnimationUtils.loadAnimation(getContext(), com.android.internal.R.anim.fade_out);
anim.setDuration(1000);
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
+ spinnyProgressBar != null &&
spinnyProgressBar.getVisibility() == View.VISIBLE) {
spinnyProgressBar.startAnimation(anim);
spinnyProgressBar.setVisibility(View.INVISIBLE);
}
- if ((features & (1 << FEATURE_PROGRESS)) != 0 &&
+ if ((features & (1 << FEATURE_PROGRESS)) != 0 && horizontalProgressBar != null &&
horizontalProgressBar.getVisibility() == View.VISIBLE) {
horizontalProgressBar.startAnimation(anim);
horizontalProgressBar.setVisibility(View.INVISIBLE);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index fb041fa..0d39586 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -1959,9 +1959,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public Animation createForceHideEnterAnimation(boolean onWallpaper) {
- return AnimationUtils.loadAnimation(mContext, onWallpaper
- ? com.android.internal.R.anim.lock_screen_wallpaper_behind_enter
- : com.android.internal.R.anim.lock_screen_behind_enter);
+ return AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.lock_screen_behind_enter);
}
private static void awakenDreams() {
@@ -3393,7 +3392,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pf.top = mContentTop;
pf.right = mContentRight;
pf.bottom = mContentBottom;
- if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+ if (win.isVoiceInteraction()) {
+ df.left = of.left = cf.left = mVoiceContentLeft;
+ df.top = of.top = cf.top = mVoiceContentTop;
+ df.right = of.right = cf.right = mVoiceContentRight;
+ df.bottom = of.bottom = cf.bottom = mVoiceContentBottom;
+ } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
df.left = of.left = cf.left = mDockLeft;
df.top = of.top = cf.top = mDockTop;
df.right = of.right = cf.right = mDockRight;
@@ -4613,14 +4617,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- public void startKeyguardExitAnimation(final long fadeoutDuration) {
+ public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
if (mKeyguardDelegate != null) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mKeyguardDelegate.startKeyguardExitAnimation(fadeoutDuration);
- }
- });
+ mKeyguardDelegate.startKeyguardExitAnimation(startTime, fadeoutDuration);
}
}
@@ -5678,6 +5677,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock);
pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation);
+ mGlobalKeyManager.dump(prefix, pw);
mStatusBarController.dump(pw, prefix);
mNavigationBarController.dump(pw, prefix);
PolicyControl.dump(prefix, pw);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
index faf7020..63a5850 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
@@ -274,9 +274,9 @@ public class KeyguardServiceDelegate {
mKeyguardState.currentUser = newUserId;
}
- public void startKeyguardExitAnimation(long fadeoutDuration) {
+ public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
if (mKeyguardService != null) {
- mKeyguardService.startKeyguardExitAnimation(fadeoutDuration);
+ mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
index f236ce7..5096bd3 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
@@ -190,9 +190,9 @@ public class KeyguardServiceWrapper implements IKeyguardService {
}
}
- public void startKeyguardExitAnimation(long fadeoutDuration) {
+ public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
try {
- mService.startKeyguardExitAnimation(fadeoutDuration);
+ mService.startKeyguardExitAnimation(startTime, fadeoutDuration);
} catch (RemoteException e) {
Slog.w(TAG , "Remote Exception", e);
}
diff --git a/services/Android.mk b/services/Android.mk
index 5fcef64..b4de903 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -25,6 +25,7 @@ services := \
backup \
devicepolicy \
print \
+ restrictions \
usb \
voiceinteraction
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 0082b1e..14c15a7 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -26,6 +26,7 @@ import android.app.PendingIntent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupTransport;
import android.app.backup.FullBackup;
import android.app.backup.RestoreSet;
import android.app.backup.IBackupManager;
@@ -82,7 +83,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.StringBuilderPrinter;
-import com.android.internal.backup.BackupConstants;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.backup.IObbBackupService;
import com.android.server.AppWidgetBackupBridge;
@@ -2098,7 +2098,7 @@ public class BackupManagerService extends IBackupManager.Stub {
}
mAgentBinder = null;
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
// Sanity check: if the queue is empty we have no work to do.
if (mOriginalQueue.isEmpty()) {
@@ -2121,14 +2121,14 @@ public class BackupManagerService extends IBackupManager.Stub {
EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
// If we haven't stored package manager metadata yet, we must init the transport.
- if (mStatus == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
+ if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) {
Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
addBackupTrace("initializing transport " + transportName);
resetBackupState(mStateDir); // Just to make sure.
mStatus = mTransport.initializeDevice();
addBackupTrace("transport.initializeDevice() == " + mStatus);
- if (mStatus == BackupConstants.TRANSPORT_OK) {
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
} else {
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
@@ -2141,7 +2141,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// directly and use a synthetic BackupRequest. We always run this pass
// because it's cheap and this way we guarantee that we don't get out of
// step even if we're selecting among various transports at run time.
- if (mStatus == BackupConstants.TRANSPORT_OK) {
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
mPackageManager, allAgentPackages());
mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
@@ -2149,7 +2149,7 @@ public class BackupManagerService extends IBackupManager.Stub {
addBackupTrace("PMBA invoke: " + mStatus);
}
- if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+ if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
// The backend reports that our dataset has been wiped. Note this in
// the event log; the no-success code below will reset the backup
// state as well.
@@ -2158,13 +2158,13 @@ public class BackupManagerService extends IBackupManager.Stub {
} catch (Exception e) {
Slog.e(TAG, "Error in backup thread", e);
addBackupTrace("Exception in backup thread: " + e);
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
} finally {
// If we've succeeded so far, invokeAgentForBackup() will have run the PM
// metadata and its completion/timeout callback will continue the state
// machine chain. If it failed that won't happen; we handle that now.
addBackupTrace("exiting prelim: " + mStatus);
- if (mStatus != BackupConstants.TRANSPORT_OK) {
+ if (mStatus != BackupTransport.TRANSPORT_OK) {
// if things went wrong at this point, we need to
// restage everything and try again later.
resetBackupState(mStateDir); // Just to make sure.
@@ -2176,7 +2176,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// Transport has been initialized and the PM metadata submitted successfully
// if that was warranted. Now we process the single next thing in the queue.
void invokeNextAgent() {
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
addBackupTrace("invoke q=" + mQueue.size());
// Sanity check that we have work to do. If not, skip to the end where
@@ -2236,39 +2236,39 @@ public class BackupManagerService extends IBackupManager.Stub {
// done here as long as we're successful so far.
} else {
// Timeout waiting for the agent
- mStatus = BackupConstants.AGENT_ERROR;
+ mStatus = BackupTransport.AGENT_ERROR;
}
} catch (SecurityException ex) {
// Try for the next one.
Slog.d(TAG, "error in bind/backup", ex);
- mStatus = BackupConstants.AGENT_ERROR;
+ mStatus = BackupTransport.AGENT_ERROR;
addBackupTrace("agent SE");
}
} catch (NameNotFoundException e) {
Slog.d(TAG, "Package does not exist; skipping");
addBackupTrace("no such package");
- mStatus = BackupConstants.AGENT_UNKNOWN;
+ mStatus = BackupTransport.AGENT_UNKNOWN;
} finally {
mWakelock.setWorkSource(null);
// If there was an agent error, no timeout/completion handling will occur.
// That means we need to direct to the next state ourselves.
- if (mStatus != BackupConstants.TRANSPORT_OK) {
+ if (mStatus != BackupTransport.TRANSPORT_OK) {
BackupState nextState = BackupState.RUNNING_QUEUE;
mAgentBinder = null;
// An agent-level failure means we reenqueue this one agent for
// a later retry, but otherwise proceed normally.
- if (mStatus == BackupConstants.AGENT_ERROR) {
+ if (mStatus == BackupTransport.AGENT_ERROR) {
if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName
+ " - restaging");
dataChangedImpl(request.packageName);
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
if (mQueue.isEmpty()) nextState = BackupState.FINAL;
- } else if (mStatus == BackupConstants.AGENT_UNKNOWN) {
+ } else if (mStatus == BackupTransport.AGENT_UNKNOWN) {
// Failed lookup of the app, so we couldn't bring up an agent, but
// we're otherwise fine. Just drop it and go on to the next as usual.
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
} else {
// Transport-level failure means we reenqueue everything
revertAndEndBackup();
@@ -2297,7 +2297,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// If everything actually went through and this is the first time we've
// done a backup, we can now record what the current backup dataset token
// is.
- if ((mCurrentToken == 0) && (mStatus == BackupConstants.TRANSPORT_OK)) {
+ if ((mCurrentToken == 0) && (mStatus == BackupTransport.TRANSPORT_OK)) {
addBackupTrace("success; recording token");
try {
mCurrentToken = mTransport.getCurrentRestoreSet();
@@ -2314,7 +2314,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// state machine sequence and the wakelock is refcounted.
synchronized (mQueueLock) {
mBackupRunning = false;
- if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+ if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
// Make sure we back up everything and perform the one-time init
clearMetadata();
if (DEBUG) Slog.d(TAG, "Server requires init; rerunning");
@@ -2395,7 +2395,7 @@ public class BackupManagerService extends IBackupManager.Stub {
EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
e.toString());
agentErrorCleanup();
- return BackupConstants.AGENT_ERROR;
+ return BackupTransport.AGENT_ERROR;
}
// At this point the agent is off and running. The next thing to happen will
@@ -2403,7 +2403,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// for transport, or a timeout. Either way the next phase will happen in
// response to the TimeoutHandler interface callbacks.
addBackupTrace("invoke success");
- return BackupConstants.TRANSPORT_OK;
+ return BackupTransport.TRANSPORT_OK;
}
public void failAgent(IBackupAgent agent, String message) {
@@ -2484,11 +2484,11 @@ public class BackupManagerService extends IBackupManager.Stub {
addBackupTrace("operation complete");
ParcelFileDescriptor backupData = null;
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
try {
int size = (int) mBackupDataName.length();
if (size > 0) {
- if (mStatus == BackupConstants.TRANSPORT_OK) {
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
backupData = ParcelFileDescriptor.open(mBackupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
addBackupTrace("sending data to transport");
@@ -2501,7 +2501,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// renaming *all* the output state files (see below) until that happens.
addBackupTrace("data delivered: " + mStatus);
- if (mStatus == BackupConstants.TRANSPORT_OK) {
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
addBackupTrace("finishing op on transport");
mStatus = mTransport.finishBackup();
addBackupTrace("finished: " + mStatus);
@@ -2514,7 +2514,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// After successful transport, delete the now-stale data
// and juggle the files so that next time we supply the agent
// with the new state file it just created.
- if (mStatus == BackupConstants.TRANSPORT_OK) {
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
mBackupDataName.delete();
mNewStateName.renameTo(mSavedStateName);
EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
@@ -2525,7 +2525,7 @@ public class BackupManagerService extends IBackupManager.Stub {
} catch (Exception e) {
Slog.e(TAG, "Transport error backing up " + pkgName, e);
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
} finally {
try { if (backupData != null) backupData.close(); } catch (IOException e) {}
}
@@ -2533,7 +2533,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// If we encountered an error here it's a transport-level failure. That
// means we need to halt everything and reschedule everything for next time.
final BackupState nextState;
- if (mStatus != BackupConstants.TRANSPORT_OK) {
+ if (mStatus != BackupTransport.TRANSPORT_OK) {
revertAndEndBackup();
nextState = BackupState.FINAL;
} else {
@@ -4847,7 +4847,7 @@ public class BackupManagerService extends IBackupManager.Stub {
mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
// Assume error until we successfully init everything
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
try {
// TODO: Log this before getAvailableRestoreSets, somehow
@@ -4902,7 +4902,7 @@ public class BackupManagerService extends IBackupManager.Stub {
return;
}
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
executeNextState(RestoreState.DOWNLOAD_DATA);
}
@@ -4917,7 +4917,7 @@ public class BackupManagerService extends IBackupManager.Stub {
try {
mStatus = mTransport.startRestore(mToken,
mRestorePackages.toArray(new PackageInfo[0]));
- if (mStatus != BackupConstants.TRANSPORT_OK) {
+ if (mStatus != BackupTransport.TRANSPORT_OK) {
Slog.e(TAG, "Error starting restore operation");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
executeNextState(RestoreState.FINAL);
@@ -4926,7 +4926,7 @@ public class BackupManagerService extends IBackupManager.Stub {
} catch (RemoteException e) {
Slog.e(TAG, "Error communicating with transport for restore");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
executeNextState(RestoreState.FINAL);
return;
}
@@ -4941,14 +4941,14 @@ public class BackupManagerService extends IBackupManager.Stub {
if (packageName == null) {
Slog.e(TAG, "Error getting first restore package");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
executeNextState(RestoreState.FINAL);
return;
} else if (packageName.equals("")) {
Slog.i(TAG, "No restore data available");
int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis);
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
executeNextState(RestoreState.FINAL);
return;
} else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
@@ -4979,7 +4979,7 @@ public class BackupManagerService extends IBackupManager.Stub {
Slog.e(TAG, "No restore metadata available, so not restoring settings");
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
"Package manager restore metadata missing");
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
executeNextState(RestoreState.FINAL);
return;
@@ -4987,7 +4987,7 @@ public class BackupManagerService extends IBackupManager.Stub {
} catch (RemoteException e) {
Slog.e(TAG, "Error communicating with transport for restore");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
executeNextState(RestoreState.FINAL);
return;
@@ -5118,7 +5118,7 @@ public class BackupManagerService extends IBackupManager.Stub {
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to fetch restore data from transport");
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
executeNextState(RestoreState.FINAL);
}
}
@@ -5206,7 +5206,7 @@ public class BackupManagerService extends IBackupManager.Stub {
Slog.e(TAG, "SElinux restorecon failed for " + downloadFile);
}
- if (mTransport.getRestoreData(stage) != BackupConstants.TRANSPORT_OK) {
+ if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
// Transport-level failure, so we wind everything up and
// terminate the restore operation.
Slog.e(TAG, "Error getting restore data for " + packageName);
@@ -5450,12 +5450,12 @@ public class BackupManagerService extends IBackupManager.Stub {
long startRealtime = SystemClock.elapsedRealtime();
int status = transport.initializeDevice();
- if (status == BackupConstants.TRANSPORT_OK) {
+ if (status == BackupTransport.TRANSPORT_OK) {
status = transport.finishBackup();
}
// Okay, the wipe really happened. Clean up our local bookkeeping.
- if (status == BackupConstants.TRANSPORT_OK) {
+ if (status == BackupTransport.TRANSPORT_OK) {
Slog.i(TAG, "Device init successful");
int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d7a19ad..b2b4217 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -38,7 +38,6 @@ import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
-import static android.net.ConnectivityServiceProtocol.NetworkFactoryProtocol;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
@@ -80,6 +79,7 @@ import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkFactory;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
@@ -258,17 +258,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private NetworkStateTracker mNetTrackers[];
- /**
- * Holds references to all NetworkAgentInfos claiming to support the legacy
- * NetworkType. We used to have a static set of of NetworkStateTrackers
- * for each network type. This is the new model.
- * Supports synchronous inspection of state.
- * These are built out at startup such that an unsupported network
- * doesn't get an ArrayList instance, making this a tristate:
- * unsupported, supported but not active and active.
- */
- private ArrayList<NetworkAgentInfo> mNetworkAgentInfoForType[];
-
/* Handles captive portal check on a network */
private CaptivePortalTracker mCaptivePortalTracker;
@@ -516,6 +505,118 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static final int UID_UNUSED = -1;
+ /**
+ * Implements support for the legacy "one network per network type" model.
+ *
+ * We used to have a static array of NetworkStateTrackers, one for each
+ * network type, but that doesn't work any more now that we can have,
+ * for example, more that one wifi network. This class stores all the
+ * NetworkAgentInfo objects that support a given type, but the legacy
+ * API will only see the first one.
+ *
+ * It serves two main purposes:
+ *
+ * 1. Provide information about "the network for a given type" (since this
+ * API only supports one).
+ * 2. Send legacy connectivity change broadcasts. Broadcasts are sent if
+ * the first network for a given type changes, or if the default network
+ * changes.
+ */
+ private class LegacyTypeTracker {
+ /**
+ * Array of lists, one per legacy network type (e.g., TYPE_MOBILE_MMS).
+ * Each list holds references to all NetworkAgentInfos that are used to
+ * satisfy requests for that network type.
+ *
+ * This array is built out at startup such that an unsupported network
+ * doesn't get an ArrayList instance, making this a tristate:
+ * unsupported, supported but not active and active.
+ *
+ * The actual lists are populated when we scan the network types that
+ * are supported on this device.
+ */
+ private ArrayList<NetworkAgentInfo> mTypeLists[];
+
+ public LegacyTypeTracker() {
+ mTypeLists = (ArrayList<NetworkAgentInfo>[])
+ new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
+ }
+
+ public void addSupportedType(int type) {
+ if (mTypeLists[type] != null) {
+ throw new IllegalStateException(
+ "legacy list for type " + type + "already initialized");
+ }
+ mTypeLists[type] = new ArrayList<NetworkAgentInfo>();
+ }
+
+ private boolean isDefaultNetwork(NetworkAgentInfo nai) {
+ return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai;
+ }
+
+ public boolean isTypeSupported(int type) {
+ return isNetworkTypeValid(type) && mTypeLists[type] != null;
+ }
+
+ public NetworkAgentInfo getNetworkForType(int type) {
+ if (isTypeSupported(type) && !mTypeLists[type].isEmpty()) {
+ return mTypeLists[type].get(0);
+ } else {
+ return null;
+ }
+ }
+
+ public void add(int type, NetworkAgentInfo nai) {
+ if (!isTypeSupported(type)) {
+ return; // Invalid network type.
+ }
+ if (VDBG) log("Adding agent " + nai + " for legacy network type " + type);
+
+ ArrayList<NetworkAgentInfo> list = mTypeLists[type];
+ if (list.contains(nai)) {
+ loge("Attempting to register duplicate agent for type " + type + ": " + nai);
+ return;
+ }
+
+ if (list.isEmpty() || isDefaultNetwork(nai)) {
+ if (VDBG) log("Sending connected broadcast for type " + type +
+ "isDefaultNetwork=" + isDefaultNetwork(nai));
+ sendLegacyNetworkBroadcast(nai, true, type);
+ }
+ list.add(nai);
+ }
+
+ public void remove(NetworkAgentInfo nai) {
+ if (VDBG) log("Removing agent " + nai);
+ for (int type = 0; type < mTypeLists.length; type++) {
+ ArrayList<NetworkAgentInfo> list = mTypeLists[type];
+ if (list == null || list.isEmpty()) {
+ continue;
+ }
+
+ boolean wasFirstNetwork = false;
+ if (list.get(0).equals(nai)) {
+ // This network was the first in the list. Send broadcast.
+ wasFirstNetwork = true;
+ }
+ list.remove(nai);
+
+ if (wasFirstNetwork || isDefaultNetwork(nai)) {
+ if (VDBG) log("Sending disconnected broadcast for type " + type +
+ "isDefaultNetwork=" + isDefaultNetwork(nai));
+ sendLegacyNetworkBroadcast(nai, false, type);
+ }
+
+ if (!list.isEmpty() && wasFirstNetwork) {
+ if (VDBG) log("Other network available for type " + type +
+ ", sending connected broadcast");
+ sendLegacyNetworkBroadcast(list.get(0), false, type);
+ }
+ }
+ }
+ }
+ private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker();
+
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
// Currently, omitting a NetworkFactory will create one internally
@@ -531,7 +632,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
- mDefaultRequest = new NetworkRequest(netCap, true, nextNetworkRequestId());
+ mDefaultRequest = new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId());
NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(),
NetworkRequestInfo.REQUEST);
mNetworkRequests.put(mDefaultRequest, nri);
@@ -587,9 +688,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
com.android.internal.R.integer.config_networkTransitionTimeout);
- mNetworkAgentInfoForType = (ArrayList<NetworkAgentInfo>[])
- new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
-
mNetTrackers = new NetworkStateTracker[
ConnectivityManager.MAX_NETWORK_TYPE+1];
mCurrentLinkProperties = new LinkProperties[ConnectivityManager.MAX_NETWORK_TYPE+1];
@@ -644,7 +742,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
"radio " + n.radio + " in network type " + n.type);
continue;
}
- mNetworkAgentInfoForType[n.type] = new ArrayList<NetworkAgentInfo>();
+ mLegacyTypeTracker.addSupportedType(n.type);
mNetConfigs[n.type] = n;
mNetworksDefined++;
@@ -2843,7 +2941,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
- private int getRestoreDefaultNetworkDelay(int networkType) {
+ @Override
+ public int getRestoreDefaultNetworkDelay(int networkType) {
String restoreDefaultNetworkDelayStr = SystemProperties.get(
NETWORK_RESTORE_DELAY_PROP_NAME);
if(restoreDefaultNetworkDelayStr != null &&
@@ -2994,6 +3093,16 @@ public class ConnectivityService extends IConnectivityManager.Stub {
updateNetworkInfo(nai, info);
break;
}
+ case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_NETWORK_SCORE_CHANGED from unknown NetworkAgent");
+ break;
+ }
+ Integer score = (Integer) msg.obj;
+ updateNetworkScore(nai, score);
+ break;
+ }
case NetworkMonitor.EVENT_NETWORK_VALIDATED: {
NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
handleConnectionValidated(nai);
@@ -3098,7 +3207,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (nri.isRequest == false) continue;
NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
- ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK,
+ ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,
(nai != null ? nai.currentScore : 0), 0, nri.request);
}
} else {
@@ -3114,11 +3223,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} else {
loge("Error connecting NetworkAgent");
NetworkAgentInfo nai = mNetworkAgentInfos.remove(msg.replyTo);
- try {
- mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai);
- } catch (NullPointerException e) {}
if (nai != null) {
mNetworkForNetId.remove(nai.network.netId);
+ mLegacyTypeTracker.remove(nai);
}
}
}
@@ -3137,14 +3244,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} catch (Exception e) {
loge("Exception removing network: " + e);
}
+ // TODO - if we move the logic to the network agent (have them disconnect
+ // because they lost all their requests or because their score isn't good)
+ // then they would disconnect organically, report their new state and then
+ // disconnect the channel.
+ if (nai.networkInfo.isConnected()) {
+ nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
+ null, null);
+ }
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo);
updateClat(null, nai.linkProperties, nai);
- try {
- mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai);
- } catch (NullPointerException e) {}
-
+ mLegacyTypeTracker.remove(nai);
mNetworkForNetId.remove(nai.network.netId);
// Since we've lost the network, go through all the requests that
// it was satisfying and see if any other factory can satisfy them.
@@ -3154,7 +3266,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId);
if (VDBG) {
log(" checking request " + request + ", currentNetwork = " +
- currentNetwork != null ? currentNetwork.name() : "null");
+ (currentNetwork != null ? currentNetwork.name() : "null"));
}
if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
mNetworkForRequestId.remove(request.requestId);
@@ -3203,7 +3315,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
if (bestNetwork != null) {
if (VDBG) log("using " + bestNetwork.name());
- bestNetwork.networkRequests.put(nri.request.requestId, nri.request);
+ bestNetwork.addRequest(nri.request);
+ int legacyType = nri.request.legacyType;
+ if (legacyType != TYPE_NONE) {
+ mLegacyTypeTracker.add(legacyType, bestNetwork);
+ }
notifyNetworkCallback(bestNetwork, nri);
score = bestNetwork.currentScore;
}
@@ -3211,7 +3327,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (msg.what == EVENT_REGISTER_NETWORK_REQUEST) {
if (DBG) log("sending new NetworkRequest to factories");
for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
- nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request);
+ nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
+ 0, nri.request);
}
}
}
@@ -3233,7 +3350,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (nri.isRequest) {
for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
- nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_CANCEL_REQUEST, nri.request);
+ nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST,
+ nri.request);
}
if (affectedNetwork != null) {
@@ -5279,7 +5397,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
- Messenger messenger, int timeoutSec, IBinder binder) {
+ Messenger messenger, int timeoutSec, IBinder binder, int legacyType) {
if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
== false) {
enforceConnectivityInternalPermission();
@@ -5291,7 +5409,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
throw new IllegalArgumentException("Bad timeout specified");
}
NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
- networkCapabilities), false, nextNetworkRequestId());
+ networkCapabilities), legacyType, nextNetworkRequestId());
if (DBG) log("requestNetwork for " + networkRequest);
NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
NetworkRequestInfo.REQUEST);
@@ -5317,7 +5435,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
enforceAccessPermission();
NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
- networkCapabilities), false, nextNetworkRequestId());
+ networkCapabilities), TYPE_NONE, nextNetworkRequestId());
if (DBG) log("listenForNetwork for " + networkRequest);
NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
NetworkRequestInfo.LISTEN);
@@ -5392,18 +5510,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), nextNetId(),
new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler);
-
+ if (VDBG) log("registerNetworkAgent " + nai);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
}
private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
if (VDBG) log("Got NetworkAgent Messenger");
mNetworkAgentInfos.put(na.messenger, na);
- try {
- mNetworkAgentInfoForType[na.networkInfo.getType()].add(na);
- } catch (NullPointerException e) {
- loge("registered NetworkAgent for unsupported type: " + na);
- }
mNetworkForNetId.put(na.network.netId, na);
na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
NetworkInfo networkInfo = na.networkInfo;
@@ -5439,7 +5552,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mClat.stopClat();
}
// If the link requires clat to be running, then start the daemon now.
- if (newLp != null && na.networkInfo.isConnected()) {
+ if (na.networkInfo.isConnected()) {
mClat.startClat(na);
} else {
mClat.stopClat();
@@ -5555,7 +5668,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) {
if (VDBG) log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
- nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, networkRequest);
+ nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, 0,
+ networkRequest);
}
}
@@ -5658,7 +5772,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (VDBG) log(" accepting network in place of null");
}
mNetworkForRequestId.put(nri.request.requestId, newNetwork);
- newNetwork.networkRequests.put(nri.request.requestId, nri.request);
+ newNetwork.addRequest(nri.request);
+ int legacyType = nri.request.legacyType;
+ if (legacyType != TYPE_NONE) {
+ mLegacyTypeTracker.add(legacyType, newNetwork);
+ }
keep = true;
// TODO - this could get expensive if we have alot of requests for this
// network. Think about if there is a way to reduce this. Push
@@ -5672,6 +5790,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} else {
setDefaultDnsSystemProperties(new ArrayList<InetAddress>());
}
+ mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
}
}
}
@@ -5792,6 +5911,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
+ private void updateNetworkScore(NetworkAgentInfo nai, Integer scoreInteger) {
+ int score = scoreInteger.intValue();
+ // TODO
+ }
+
// notify only this one new request of the current state
protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) {
int notifyType = ConnectivityManager.CALLBACK_AVAILABLE;
@@ -5801,93 +5925,88 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// } else if (nai.networkMonitor.isEvaluating()) {
// notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType);
// }
- if (nri.request.needsBroadcasts) {
- // TODO
-// sendNetworkBroadcast(nai, notifyType);
- }
callCallbackForRequest(nri, nai, notifyType);
}
+ private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, boolean connected, int type) {
+ if (connected) {
+ NetworkInfo info = new NetworkInfo(nai.networkInfo);
+ info.setType(type);
+ sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
+ } else {
+ NetworkInfo info = new NetworkInfo(nai.networkInfo);
+ info.setType(type);
+ Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
+ if (info.isFailover()) {
+ intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
+ nai.networkInfo.setFailover(false);
+ }
+ if (info.getReason() != null) {
+ intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
+ }
+ if (info.getExtraInfo() != null) {
+ intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
+ }
+ NetworkAgentInfo newDefaultAgent = null;
+ if (nai.networkRequests.get(mDefaultRequest.requestId) != null) {
+ newDefaultAgent = mNetworkForRequestId.get(mDefaultRequest.requestId);
+ if (newDefaultAgent != null) {
+ intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
+ newDefaultAgent.networkInfo);
+ } else {
+ intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
+ }
+ }
+ intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION,
+ mDefaultInetConditionPublished);
+ final Intent immediateIntent = new Intent(intent);
+ immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
+ sendStickyBroadcast(immediateIntent);
+ sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
+ if (newDefaultAgent != null) {
+ sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo,
+ getConnectivityChangeDelay());
+ }
+ }
+ }
+
protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
if (VDBG) log("notifyType " + notifyType + " for " + networkAgent.name());
- boolean needsBroadcasts = false;
for (int i = 0; i < networkAgent.networkRequests.size(); i++) {
NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
if (VDBG) log(" sending notification for " + nr);
- if (nr.needsBroadcasts) needsBroadcasts = true;
callCallbackForRequest(nri, networkAgent, notifyType);
}
- if (needsBroadcasts) {
- if (notifyType == ConnectivityManager.CALLBACK_AVAILABLE) {
- sendConnectedBroadcastDelayed(networkAgent.networkInfo,
- getConnectivityChangeDelay());
- } else if (notifyType == ConnectivityManager.CALLBACK_LOST) {
- NetworkInfo info = new NetworkInfo(networkAgent.networkInfo);
- Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
- if (info.isFailover()) {
- intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
- networkAgent.networkInfo.setFailover(false);
- }
- if (info.getReason() != null) {
- intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
- }
- if (info.getExtraInfo() != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
- }
- NetworkAgentInfo newDefaultAgent = null;
- if (networkAgent.networkRequests.get(mDefaultRequest.requestId) != null) {
- newDefaultAgent = mNetworkForRequestId.get(mDefaultRequest.requestId);
- if (newDefaultAgent != null) {
- intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
- newDefaultAgent.networkInfo);
- } else {
- intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
- }
- }
- intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION,
- mDefaultInetConditionPublished);
- final Intent immediateIntent = new Intent(intent);
- immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
- sendStickyBroadcast(immediateIntent);
- sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
- if (newDefaultAgent != null) {
- sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo,
- getConnectivityChangeDelay());
- }
- }
- }
}
private LinkProperties getLinkPropertiesForTypeInternal(int networkType) {
- ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType];
- if (list == null) return null;
- try {
- return new LinkProperties(list.get(0).linkProperties);
- } catch (IndexOutOfBoundsException e) {
- return new LinkProperties();
- }
+ NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ return (nai != null) ?
+ new LinkProperties(nai.linkProperties) :
+ new LinkProperties();
}
private NetworkInfo getNetworkInfoForType(int networkType) {
- ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType];
- if (list == null) return null;
- try {
- return new NetworkInfo(list.get(0).networkInfo);
- } catch (IndexOutOfBoundsException e) {
- return new NetworkInfo(networkType, 0, "Unknown", "");
+ if (!mLegacyTypeTracker.isTypeSupported(networkType))
+ return null;
+
+ NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ if (nai != null) {
+ NetworkInfo result = new NetworkInfo(nai.networkInfo);
+ result.setType(networkType);
+ return result;
+ } else {
+ return new NetworkInfo(networkType, 0, "Unknown", "");
}
}
private NetworkCapabilities getNetworkCapabilitiesForType(int networkType) {
- ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType];
- if (list == null) return null;
- try {
- return new NetworkCapabilities(list.get(0).networkCapabilities);
- } catch (IndexOutOfBoundsException e) {
- return new NetworkCapabilities();
- }
+ NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ return (nai != null) ?
+ new NetworkCapabilities(nai.networkCapabilities) :
+ new NetworkCapabilities();
}
}
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index d5f045e..d31fb60 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2372,6 +2372,18 @@ class MountService extends IMountService.Stub
}
}
+ voldPath = maybeTranslatePathForVold(appPath,
+ userEnv.buildExternalStorageAppMediaDirs(callingPkg),
+ userEnv.buildExternalStorageAppMediaDirsForVold(callingPkg));
+ if (voldPath != null) {
+ try {
+ mConnector.execute("volume", "mkdirs", voldPath);
+ return 0;
+ } catch (NativeDaemonConnectorException e) {
+ return e.getCode();
+ }
+ }
+
throw new SecurityException("Invalid mkdirs path: " + appPath);
}
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index cd8c13f..d8ee8a1 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -246,7 +246,8 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
private void setDeviceStateLocked(int headset,
int headsetState, int prevHeadsetState, String headsetName) {
if ((headsetState & headset) != (prevHeadsetState & headset)) {
- int device;
+ int outDevice = 0;
+ int inDevice = 0;
int state;
if ((headsetState & headset) != 0) {
@@ -256,15 +257,16 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
}
if (headset == BIT_HEADSET) {
- device = AudioManager.DEVICE_OUT_WIRED_HEADSET;
+ outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET;
+ inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET;
} else if (headset == BIT_HEADSET_NO_MIC){
- device = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
+ outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
} else if (headset == BIT_USB_HEADSET_ANLG) {
- device = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
+ outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
} else if (headset == BIT_USB_HEADSET_DGTL) {
- device = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
+ outDevice = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
} else if (headset == BIT_HDMI_AUDIO) {
- device = AudioManager.DEVICE_OUT_HDMI;
+ outDevice = AudioManager.DEVICE_OUT_HDMI;
} else {
Slog.e(TAG, "setDeviceState() invalid headset type: "+headset);
return;
@@ -273,7 +275,12 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
if (LOG)
Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected"));
- mAudioManager.setWiredDeviceConnectionState(device, state, headsetName);
+ if (outDevice != 0) {
+ mAudioManager.setWiredDeviceConnectionState(outDevice, state, headsetName);
+ }
+ if (inDevice != 0) {
+ mAudioManager.setWiredDeviceConnectionState(inDevice, state, headsetName);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index eebb503..816e022 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -247,7 +247,8 @@ public final class ActiveServices {
maxBg = Integer.parseInt(SystemProperties.get("ro.config.max_starting_bg", "0"));
} catch(RuntimeException e) {
}
- mMaxStartingBackground = maxBg > 0 ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
+ mMaxStartingBackground = maxBg > 0
+ ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8;
}
ServiceRecord getServiceByName(ComponentName name, int callingUser) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 61ba6e0..aede797 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2978,6 +2978,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
buf.append("}");
+ if (requiredAbi != null) {
+ buf.append(" abi=");
+ buf.append(requiredAbi);
+ }
Slog.i(TAG, buf.toString());
app.setPid(startResult.pid);
app.usingWrapper = startResult.usingWrapper;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 16ad153..ba12374 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2537,7 +2537,9 @@ final class ActivityStack {
+ " who=" + r.resultWho + " req=" + r.requestCode
+ " res=" + resultCode + " data=" + resultData);
if (resultTo.userId != r.userId) {
- resultData.prepareToLeaveUser(r.userId);
+ if (resultData != null) {
+ resultData.prepareToLeaveUser(r.userId);
+ }
}
if (r.info.applicationInfo.uid > 0) {
mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 8102591..b03c247 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -45,7 +45,6 @@ public class NetworkAgentInfo {
public int currentScore;
public final NetworkMonitor networkMonitor;
-
// The list of NetworkRequests being satisfied by this Network.
public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
@@ -66,6 +65,10 @@ public class NetworkAgentInfo {
networkMonitor = new NetworkMonitor(context, handler, this);
}
+ public void addRequest(NetworkRequest networkRequest) {
+ networkRequests.put(networkRequest.requestId, networkRequest);
+ }
+
public String toString() {
return "NetworkAgentInfo{ ni{" + networkInfo + "} network{" +
network + "} lp{" +
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index d0b716d..5141d16 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -505,7 +505,7 @@ final class HdmiCecController {
// Reply <Feature Abort> to initiator (source) for all requests.
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
sourceAddress, message.getSource(), message.getOpcode(),
- HdmiCecMessageBuilder.ABORT_REFUSED);
+ HdmiConstants.ABORT_REFUSED);
sendCommand(cecMessage, null);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 9a76734..6c2be34 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -26,15 +26,6 @@ import java.util.Arrays;
* A helper class to build {@link HdmiCecMessage} from various cec commands.
*/
public class HdmiCecMessageBuilder {
- // TODO: move these values to HdmiCec.java once make it internal constant class.
- // CEC's ABORT reason values.
- static final int ABORT_UNRECOGNIZED_MODE = 0;
- static final int ABORT_NOT_IN_CORRECT_MODE = 1;
- static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
- static final int ABORT_INVALID_OPERAND = 3;
- static final int ABORT_REFUSED = 4;
- static final int ABORT_UNABLE_TO_DETERMINE = 5;
-
private static final int OSD_NAME_MAX_LENGTH = 13;
private HdmiCecMessageBuilder() {}
@@ -290,6 +281,64 @@ public class HdmiCecMessageBuilder {
}
/**
+ * Build &lt;System Audio Mode Request&gt; command.
+ *
+ * @param src source address of command
+ * @param avr destination address of command, it should be AVR
+ * @param avrPhysicalAddress physical address of AVR
+ * @param enableSystemAudio whether to enable System Audio Mode or not
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress,
+ boolean enableSystemAudio) {
+ if (enableSystemAudio) {
+ return buildCommand(src, avr, HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
+ physicalAddressToParam(avrPhysicalAddress));
+ } else {
+ return buildCommand(src, avr, HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST);
+ }
+ }
+
+ /**
+ * Build &lt;Give Audio Status&gt; command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildGiveAudioStatus(int src, int dest) {
+ return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_AUDIO_STATUS);
+ }
+
+ /**
+ * Build &lt;User Control Pressed&gt; command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param uiCommand keycode that user pressed
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) {
+ byte[] params = new byte[] {
+ (byte) uiCommand
+ };
+ return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_PRESSED, params);
+ }
+
+ /**
+ * Build &lt;User Control Released&gt; command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildUserControlReleased(int src, int dest) {
+ return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_RELEASED);
+ }
+
+ /***** Please ADD new buildXXX() methods above. ******/
+
+ /**
* Build a {@link HdmiCecMessage} without extra parameter.
*
* @param src source address of command
diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java
new file mode 100644
index 0000000..a83d1ed
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package com.android.server.hdmi;
+
+/**
+ * Defines constants related to HDMI-CEC protocol internal implementation.
+ * If a constant will be used in the public api, it should be located in
+ * {@link android.hardware.hdmi.HdmiCec}.
+ */
+final class HdmiConstants {
+
+ // Constants related to operands of HDMI CEC commands.
+ // Refer to CEC Table 29 in HDMI Spec v1.4b.
+ // [Abort Reason]
+ static final int ABORT_UNRECOGNIZED_MODE = 0;
+ static final int ABORT_NOT_IN_CORRECT_MODE = 1;
+ static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
+ static final int ABORT_INVALID_OPERAND = 3;
+ static final int ABORT_REFUSED = 4;
+ static final int ABORT_UNABLE_TO_DETERMINE = 5;
+
+ // [Audio Status]
+ static final int SYSTEM_AUDIO_STATUS_OFF = 0;
+ static final int SYSTEM_AUDIO_STATUS_ON = 1;
+
+ // Constants related to UI Command Codes.
+ // Refer to CEC Table 30 in HDMI Spec v1.4b.
+ static final int UI_COMMAND_MUTE = 0x43;
+ static final int UI_COMMAND_MUTE_FUNCTION = 0x65;
+ static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66;
+
+ private HdmiConstants() { /* cannot be instantiated */ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index d775733..0f3fc21 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -118,8 +118,11 @@ public final class HdmiControlService extends SystemService {
// TODO: it may need to hold lock if it's accessed from others.
private boolean mArcStatusEnabled = false;
+ // Whether SystemAudioMode is "On" or not.
+ private boolean mSystemAudioMode;
+
// Handler running on service thread. It's used to run a task in service thread.
- private Handler mHandler = new Handler();
+ private final Handler mHandler = new Handler();
public HdmiControlService(Context context) {
super(context);
@@ -158,6 +161,9 @@ public final class HdmiControlService extends SystemService {
}
publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
+
+ // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
+ // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
}
/**
@@ -211,35 +217,43 @@ public final class HdmiControlService extends SystemService {
* @param action {@link FeatureAction} to remove
*/
void removeAction(final FeatureAction action) {
- runOnServiceThread(new Runnable() {
- @Override
- public void run() {
- mActions.remove(action);
- }
- });
+ assertRunOnServiceThread();
+ mActions.remove(action);
}
// Remove all actions matched with the given Class type.
private <T extends FeatureAction> void removeAction(final Class<T> clazz) {
- runOnServiceThread(new Runnable() {
- @Override
- public void run() {
- Iterator<FeatureAction> iter = mActions.iterator();
- while (iter.hasNext()) {
- FeatureAction action = iter.next();
- if (action.getClass().equals(clazz)) {
- action.clear();
- mActions.remove(action);
- }
- }
+ removeActionExcept(clazz, null);
+ }
+
+ // Remove all actions matched with the given Class type besides |exception|.
+ <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+ final FeatureAction exception) {
+ assertRunOnServiceThread();
+ Iterator<FeatureAction> iter = mActions.iterator();
+ while (iter.hasNext()) {
+ FeatureAction action = iter.next();
+ if (action != exception && action.getClass().equals(clazz)) {
+ action.clear();
+ mActions.remove(action);
}
- });
+ }
}
private void runOnServiceThread(Runnable runnable) {
mHandler.post(runnable);
}
+ void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
+ mHandler.postAtFrontOfQueue(runnable);
+ }
+
+ private void assertRunOnServiceThread() {
+ if (Looper.myLooper() != mHandler.getLooper()) {
+ throw new IllegalStateException("Should run on service thread.");
+ }
+ }
+
/**
* Change ARC status into the given {@code enabled} status.
*
@@ -306,8 +320,12 @@ public final class HdmiControlService extends SystemService {
case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
handleReportPhysicalAddress(message);
return true;
- // TODO: Add remaining system information query such as
- // <Give Device Power Status> and <Request Active Source> handler.
+ case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+ handleSetSystemAudioMode(message);
+ return true;
+ case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
+ handleSystemAudioModeStatus(message);
+ return true;
default:
return dispatchMessageToAction(message);
}
@@ -413,7 +431,7 @@ public final class HdmiControlService extends SystemService {
sendCecCommand(
HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(),
message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE,
- HdmiCecMessageBuilder.ABORT_UNRECOGNIZED_MODE));
+ HdmiConstants.ABORT_UNRECOGNIZED_MODE));
return;
}
@@ -438,6 +456,33 @@ public final class HdmiControlService extends SystemService {
return false;
}
+ private void handleSetSystemAudioMode(HdmiCecMessage message) {
+ if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) {
+ return;
+ }
+ SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
+ message.getDestination(), message.getSource(),
+ HdmiUtils.parseCommandParamSystemAudioStatus(message));
+ addAndStartAction(action);
+ }
+
+ private void handleSystemAudioModeStatus(HdmiCecMessage message) {
+ if (!isMessageForSystemAudio(message)) {
+ return;
+ }
+ setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
+ }
+
+ private boolean isMessageForSystemAudio(HdmiCecMessage message) {
+ if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
+ || message.getDestination() != HdmiCec.ADDR_TV
+ || getAvrDeviceInfo() == null) {
+ Slog.w(TAG, "Skip abnormal CecMessage: " + message);
+ return false;
+ }
+ return true;
+ }
+
// Record class that monitors the event of the caller of being killed. Used to clean up
// the listener list and record list accordingly.
private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
@@ -627,4 +672,32 @@ public final class HdmiControlService extends SystemService {
Slog.e(TAG, "Invoking callback failed:" + e);
}
}
+
+ HdmiCecDeviceInfo getAvrDeviceInfo() {
+ return mCecController.getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
+ }
+
+ void setSystemAudioMode(boolean newMode) {
+ assertRunOnServiceThread();
+ if (newMode != mSystemAudioMode) {
+ // TODO: Need to set the preference for SystemAudioMode.
+ // TODO: Need to handle the notification of changing the mode and
+ // to identify the notification should be handled in the service or TvSettings.
+ mSystemAudioMode = newMode;
+ }
+ }
+
+ boolean getSystemAudioMode() {
+ assertRunOnServiceThread();
+ return mSystemAudioMode;
+ }
+
+ void setAudioStatus(boolean mute, int volume) {
+ // TODO: Hook up with AudioManager.
+ }
+
+ boolean isInPresetInstallationMode() {
+ // TODO: Implement this.
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
new file mode 100644
index 0000000..ef128ed1
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.util.Slog;
+
+/**
+ * Various utilities to handle HDMI CEC messages.
+ */
+final class HdmiUtils {
+
+ private HdmiUtils() { /* cannot be instantiated */ }
+
+ /**
+ * Verify if the given address is for the given device type. If not it will throw
+ * {@link IllegalArgumentException}.
+ *
+ * @param logicalAddress the logical address to verify
+ * @param deviceType the device type to check
+ * @throw IllegalArgumentException
+ */
+ static void verifyAddressType(int logicalAddress, int deviceType) {
+ int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress);
+ if (actualDeviceType != deviceType) {
+ throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
+ + ", Actual:" + actualDeviceType);
+ }
+ }
+
+ /**
+ * Check if the given CEC message come from the given address.
+ *
+ * @param cmd the CEC message to check
+ * @param expectedAddress the expected source address of the given message
+ * @param tag the tag of caller module (for log message)
+ * @return true if the CEC message comes from the given address
+ */
+ static boolean checkCommandSource(HdmiCecMessage cmd, int expectedAddress, String tag) {
+ int src = cmd.getSource();
+ if (src != expectedAddress) {
+ Slog.w(tag, "Invalid source [Expected:" + expectedAddress + ", Actual:" + src + "]");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Parse the parameter block of CEC message as [System Audio Status].
+ *
+ * @param cmd the CEC message to parse
+ * @return true if the given parameter has [ON] value
+ */
+ static boolean parseCommandParamSystemAudioStatus(HdmiCecMessage cmd) {
+ // TODO: Handle the exception when the length is wrong.
+ return cmd.getParams().length > 0
+ && cmd.getParams()[0] == HdmiConstants.SYSTEM_AUDIO_STATUS_ON;
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index 05614a4..08ca306 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -44,28 +44,15 @@ abstract class RequestArcAction extends FeatureAction {
*/
RequestArcAction(HdmiControlService service, int sourceAddress, int avrAddress) {
super(service, sourceAddress);
- verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
- verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
+ HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+ HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrAddress = avrAddress;
}
- private static void verifyAddressType(int logicalAddress, int deviceType) {
- int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress);
- if (actualDeviceType != deviceType) {
- throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
- + ", Actual:" + actualDeviceType);
- }
- }
-
@Override
boolean processCommand(HdmiCecMessage cmd) {
- if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) {
- return false;
- }
-
- int src = cmd.getSource();
- if (src != mAvrAddress) {
- Slog.w(TAG, "Invalid source [Expected:" + mAvrAddress + ", Actual:" + src + "]");
+ if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE
+ || !HdmiUtils.checkCommandSource(cmd, mAvrAddress, TAG)) {
return false;
}
int opcode = cmd.getOpcode();
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index e3525d8..d53d88d 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -46,21 +46,12 @@ final class SetArcTransmissionStateAction extends FeatureAction {
SetArcTransmissionStateAction(HdmiControlService service, int sourceAddress, int avrAddress,
boolean enabled) {
super(service, sourceAddress);
- verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
- verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
+ HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+ HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrAddress = avrAddress;
mEnabled = enabled;
}
- // TODO: extract it as separate utility class.
- private static void verifyAddressType(int logicalAddress, int deviceType) {
- int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress);
- if (actualDeviceType != deviceType) {
- throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
- + ", Actual:" + actualDeviceType);
- }
- }
-
@Override
boolean start() {
if (mEnabled) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
new file mode 100644
index 0000000..dde3342
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+
+/**
+ * Base feature action class for SystemAudioActionFromTv and SystemAudioActionFromAvr.
+ */
+abstract class SystemAudioAction extends FeatureAction {
+ private static final String TAG = "SystemAudioAction";
+
+ // State in which waits for <SetSystemAudioMode>.
+ private static final int STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE = 1;
+
+ // State in which waits for <ReportAudioStatus>.
+ private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 2;
+
+ private static final int MAX_SEND_RETRY_COUNT = 2;
+
+ private static final int ON_TIMEOUT_MS = 5000;
+ private static final int OFF_TIMEOUT_MS = TIMEOUT_MS;
+
+ // Logical address of AV Receiver.
+ protected final int mAvrLogicalAddress;
+
+ // The target audio status of the action, whether to enable the system audio mode or not.
+ protected boolean mTargetAudioStatus;
+
+ private int mSendRetryCount = 0;
+
+ /**
+ * Constructor
+ *
+ * @param service {@link HdmiControlService} instance
+ * @param sourceAddress logical address of source device (TV or STB).
+ * @param avrAddress logical address of AVR device
+ * @param targetStatus Whether to enable the system audio mode or not
+ * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
+ */
+ SystemAudioAction(HdmiControlService service, int sourceAddress, int avrAddress,
+ boolean targetStatus) {
+ super(service, sourceAddress);
+ HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
+ mAvrLogicalAddress = avrAddress;
+ mTargetAudioStatus = targetStatus;
+ }
+
+ protected void sendSystemAudioModeRequest() {
+ int avrPhysicalAddress = mService.getAvrDeviceInfo().getPhysicalAddress();
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(mSourceAddress,
+ mAvrLogicalAddress, avrPhysicalAddress, mTargetAudioStatus);
+ sendCommand(command, new HdmiControlService.SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
+ addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
+ } else {
+ setSystemAudioMode(false);
+ finish();
+ }
+ }
+ });
+ }
+
+ private void handleSendSystemAudioModeRequestTimeout() {
+ if (!mTargetAudioStatus // Don't retry for Off case.
+ || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
+ setSystemAudioMode(false);
+ finish();
+ return;
+ }
+ sendSystemAudioModeRequest();
+ }
+
+ protected void setSystemAudioMode(boolean mode) {
+ mService.setSystemAudioMode(mode);
+ }
+
+ protected void sendGiveAudioStatus() {
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(mSourceAddress,
+ mAvrLogicalAddress);
+ sendCommand(command, new HdmiControlService.SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ mState = STATE_WAIT_FOR_REPORT_AUDIO_STATUS;
+ addTimer(mState, TIMEOUT_MS);
+ } else {
+ handleSendGiveAudioStatusFailure();
+ }
+ }
+ });
+ }
+
+ private void handleSendGiveAudioStatusFailure() {
+ // TODO: Notify the failure status.
+
+ int uiCommand = mService.getSystemAudioMode()
+ ? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION // SystemAudioMode: ON
+ : HdmiConstants.UI_COMMAND_MUTE_FUNCTION; // SystemAudioMode: OFF
+ sendUserControlPressedAndReleased(uiCommand);
+ finish();
+ }
+
+ private void sendUserControlPressedAndReleased(int uiCommand) {
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
+ mSourceAddress, mAvrLogicalAddress, uiCommand));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
+ mSourceAddress, mAvrLogicalAddress));
+ }
+
+ @Override
+ final boolean processCommand(HdmiCecMessage cmd) {
+ switch (mState) {
+ case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
+ // TODO: Handle <FeatureAbort> of <SystemAudioModeRequest>
+ if (cmd.getOpcode() != HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE
+ || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) {
+ return false;
+ }
+ boolean receivedStatus = HdmiUtils.parseCommandParamSystemAudioStatus(cmd);
+ if (receivedStatus == mTargetAudioStatus) {
+ setSystemAudioMode(receivedStatus);
+ sendGiveAudioStatus();
+ } else {
+ // Unexpected response, consider the request is newly initiated by AVR.
+ // To return 'false' will initiate new SystemAudioActionFromAvr by the control
+ // service.
+ finish();
+ return false;
+ }
+ return true;
+
+ case STATE_WAIT_FOR_REPORT_AUDIO_STATUS:
+ // TODO: Handle <FeatureAbort> of <GiveAudioStatus>
+ if (cmd.getOpcode() != HdmiCec.MESSAGE_REPORT_AUDIO_STATUS
+ || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) {
+ return false;
+ }
+ byte[] params = cmd.getParams();
+ if (params.length > 0) {
+ boolean mute = (params[0] & 0x80) == 0x80;
+ int volume = params[0] & 0x7F;
+ mService.setAudioStatus(mute, volume);
+ if (mTargetAudioStatus && mute || !mTargetAudioStatus && !mute) {
+ // Toggle AVR's mute status to match with the system audio status.
+ sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE);
+ }
+ }
+ finish();
+ return true;
+ }
+ return false;
+ }
+
+ protected void removeSystemAudioActionInProgress() {
+ mService.removeActionExcept(SystemAudioActionFromTv.class, this);
+ mService.removeActionExcept(SystemAudioActionFromAvr.class, this);
+ }
+
+ @Override
+ final void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+ switch (mState) {
+ case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
+ handleSendSystemAudioModeRequestTimeout();
+ return;
+ case STATE_WAIT_FOR_REPORT_AUDIO_STATUS:
+ handleSendGiveAudioStatusFailure();
+ return;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
new file mode 100644
index 0000000..c5eb44b
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+
+/**
+ * Feature action that handles System Audio initiated by AVR devices.
+ */
+final class SystemAudioActionFromAvr extends SystemAudioAction {
+ /**
+ * Constructor
+ *
+ * @param service {@link HdmiControlService} instance
+ * @param tvAddress logical address of TV device
+ * @param avrAddress logical address of AVR device
+ * @param targetStatus Whether to enable the system audio mode or not
+ * @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid
+ */
+ SystemAudioActionFromAvr(HdmiControlService service, int tvAddress, int avrAddress,
+ boolean targetStatus) {
+ super(service, tvAddress, avrAddress, targetStatus);
+ HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+ }
+
+ @Override
+ boolean start() {
+ removeSystemAudioActionInProgress();
+ handleSystemAudioActionFromAvr();
+ return true;
+ }
+
+ private void handleSystemAudioActionFromAvr() {
+ if (mTargetAudioStatus == mService.getSystemAudioMode()) {
+ finish();
+ return;
+ }
+ if (mService.isInPresetInstallationMode()) {
+ sendCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ mSourceAddress, mAvrLogicalAddress,
+ HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE, HdmiConstants.ABORT_REFUSED));
+ mTargetAudioStatus = false;
+ sendSystemAudioModeRequest();
+ return;
+ }
+ // TODO: Stop the action for System Audio Mode initialization if it is running.
+ if (mTargetAudioStatus) {
+ setSystemAudioMode(true);
+ sendGiveAudioStatus();
+ } else {
+ setSystemAudioMode(false);
+ finish();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
new file mode 100644
index 0000000..9994de6
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+
+
+/**
+ * Feature action that handles System Audio initiated by TV devices.
+ */
+final class SystemAudioActionFromTv extends SystemAudioAction {
+ /**
+ * Constructor
+ *
+ * @param service {@link HdmiControlService} instance
+ * @param tvAddress logical address of TV device
+ * @param avrAddress logical address of AVR device
+ * @param targetStatus Whether to enable the system audio mode or not
+ * @throw IllegalArugmentException if device type of tvAddress is invalid
+ */
+ SystemAudioActionFromTv(HdmiControlService service, int tvAddress, int avrAddress,
+ boolean targetStatus) {
+ super(service, tvAddress, avrAddress, targetStatus);
+ HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+ }
+
+ @Override
+ boolean start() {
+ // TODO: Check HDMI-CEC is enabled.
+ // TODO: Move to the waiting state if currently a routing change is in progress.
+
+ removeSystemAudioActionInProgress();
+ sendSystemAudioModeRequest();
+ return true;
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 030e3ed..737ffda 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -25,6 +25,7 @@ import android.media.session.ISessionControllerCallback;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.MediaController;
+import android.media.session.RemoteVolumeProvider;
import android.media.session.RouteCommand;
import android.media.session.RouteInfo;
import android.media.session.RouteOptions;
@@ -33,6 +34,7 @@ import android.media.session.MediaSession;
import android.media.session.MediaSessionInfo;
import android.media.session.RouteInterface;
import android.media.session.PlaybackState;
+import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
import android.os.Bundle;
@@ -66,13 +68,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
* These are the playback states that count as currently active.
*/
private static final int[] ACTIVE_STATES = {
- PlaybackState.PLAYSTATE_FAST_FORWARDING,
- PlaybackState.PLAYSTATE_REWINDING,
- PlaybackState.PLAYSTATE_SKIPPING_BACKWARDS,
- PlaybackState.PLAYSTATE_SKIPPING_FORWARDS,
- PlaybackState.PLAYSTATE_BUFFERING,
- PlaybackState.PLAYSTATE_CONNECTING,
- PlaybackState.PLAYSTATE_PLAYING };
+ PlaybackState.STATE_FAST_FORWARDING,
+ PlaybackState.STATE_REWINDING,
+ PlaybackState.STATE_SKIPPING_TO_PREVIOUS,
+ PlaybackState.STATE_SKIPPING_TO_NEXT,
+ PlaybackState.STATE_BUFFERING,
+ PlaybackState.STATE_CONNECTING,
+ PlaybackState.STATE_PLAYING };
/**
* The length of time a session will still be considered active after
@@ -112,6 +114,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
private long mLastActiveTime;
// End TransportPerformer fields
+ // Volume handling fields
+ private int mPlaybackType = MediaSession.VOLUME_TYPE_LOCAL;
+ private int mAudioStream = AudioManager.STREAM_MUSIC;
+ private int mVolumeControlType = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+ private int mMaxVolume = 0;
+ private int mCurrentVolume = 0;
+ // End volume handling fields
+
private boolean mIsActive = false;
private boolean mDestroyed = false;
@@ -248,6 +258,27 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
/**
+ * Send a volume adjustment to the session owner.
+ *
+ * @param delta The amount to adjust the volume by.
+ */
+ public void adjustVolumeBy(int delta) {
+ if (mVolumeControlType == RemoteVolumeProvider.VOLUME_CONTROL_FIXED) {
+ // Nothing to do, the volume cannot be changed
+ return;
+ }
+ mSessionCb.adjustVolumeBy(delta);
+ }
+
+ public void setVolumeTo(int value) {
+ if (mVolumeControlType != RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
+ // Nothing to do. The volume can't be set directly.
+ return;
+ }
+ mSessionCb.setVolumeTo(value);
+ }
+
+ /**
* Set the connection to use for the selected route and notify the app it is
* now connected.
*
@@ -294,14 +325,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
* Check if the session is currently performing playback. This will also
* return true if the session was recently paused.
*
+ * @param includeRecentlyActive True if playback that was recently paused
+ * should count, false if it shouldn't.
* @return True if the session is performing playback, false otherwise.
*/
- public boolean isPlaybackActive() {
+ public boolean isPlaybackActive(boolean includeRecentlyActive) {
int state = mPlaybackState == null ? 0 : mPlaybackState.getState();
if (isActiveState(state)) {
return true;
}
- if (state == mPlaybackState.PLAYSTATE_PAUSED) {
+ if (includeRecentlyActive && state == mPlaybackState.STATE_PAUSED) {
long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime;
if (inactiveTime < ACTIVE_BUFFER) {
return true;
@@ -311,6 +344,54 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
/**
+ * Get the type of playback, either local or remote.
+ *
+ * @return The current type of playback.
+ */
+ public int getPlaybackType() {
+ return mPlaybackType;
+ }
+
+ /**
+ * Get the local audio stream being used. Only valid if playback type is
+ * local.
+ *
+ * @return The audio stream the session is using.
+ */
+ public int getAudioStream() {
+ return mAudioStream;
+ }
+
+ /**
+ * Get the type of volume control. Only valid if playback type is remote.
+ *
+ * @return The volume control type being used.
+ */
+ public int getVolumeControl() {
+ return mVolumeControlType;
+ }
+
+ /**
+ * Get the max volume that can be set. Only valid if playback type is
+ * remote.
+ *
+ * @return The max volume that can be set.
+ */
+ public int getMaxVolume() {
+ return mMaxVolume;
+ }
+
+ /**
+ * Get the current volume for this session. Only valid if playback type is
+ * remote.
+ *
+ * @return The current volume of the remote playback.
+ */
+ public int getCurrentVolume() {
+ return mCurrentVolume;
+ }
+
+ /**
* @return True if this session is currently connected to a route.
*/
public boolean isConnected() {
@@ -509,12 +590,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
PlaybackState result = null;
if (state != null) {
- if (state.getState() == PlaybackState.PLAYSTATE_PLAYING
- || state.getState() == PlaybackState.PLAYSTATE_FAST_FORWARDING
- || state.getState() == PlaybackState.PLAYSTATE_REWINDING) {
+ if (state.getState() == PlaybackState.STATE_PLAYING
+ || state.getState() == PlaybackState.STATE_FAST_FORWARDING
+ || state.getState() == PlaybackState.STATE_REWINDING) {
long updateTime = state.getLastPositionUpdateTime();
if (updateTime > 0) {
- long position = (long) (state.getRate()
+ long position = (long) (state.getPlaybackRate()
* (SystemClock.elapsedRealtime() - updateTime)) + state.getPosition();
if (duration >= 0 && position > duration) {
position = duration;
@@ -522,7 +603,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
position = 0;
}
result = new PlaybackState(state);
- result.setState(state.getState(), position, state.getRate());
+ result.setState(state.getState(), position, state.getPlaybackRate());
}
}
}
@@ -588,7 +669,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
public void setPlaybackState(PlaybackState state) {
int oldState = mPlaybackState == null ? 0 : mPlaybackState.getState();
int newState = state == null ? 0 : state.getState();
- if (isActiveState(oldState) && newState == PlaybackState.PLAYSTATE_PAUSED) {
+ if (isActiveState(oldState) && newState == PlaybackState.STATE_PAUSED) {
mLastActiveTime = SystemClock.elapsedRealtime();
}
mPlaybackState = state;
@@ -640,6 +721,40 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
mRequests.add(request);
}
}
+
+ @Override
+ public void setCurrentVolume(int volume) {
+ mCurrentVolume = volume;
+ }
+
+ @Override
+ public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException {
+ switch(type) {
+ case MediaSession.VOLUME_TYPE_LOCAL:
+ mPlaybackType = type;
+ int audioStream = arg1;
+ if (isValidStream(audioStream)) {
+ mAudioStream = audioStream;
+ } else {
+ Log.e(TAG, "Cannot set stream to " + audioStream + ". Using music stream");
+ mAudioStream = AudioManager.STREAM_MUSIC;
+ }
+ break;
+ case MediaSession.VOLUME_TYPE_REMOTE:
+ mPlaybackType = type;
+ mVolumeControlType = arg1;
+ mMaxVolume = arg2;
+ break;
+ default:
+ throw new IllegalArgumentException("Volume handling type " + type
+ + " not recognized.");
+ }
+ }
+
+ private boolean isValidStream(int stream) {
+ return stream >= AudioManager.STREAM_VOICE_CALL
+ && stream <= AudioManager.STREAM_NOTIFICATION;
+ }
}
class SessionCb {
@@ -649,14 +764,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
mCb = cb;
}
- public void sendMediaButton(KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
+ public boolean sendMediaButton(KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
try {
mCb.onMediaButton(mediaButtonIntent, sequenceId, cb);
+ return true;
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
}
+ return false;
}
public void sendCommand(String command, Bundle extras, ResultReceiver cb) {
@@ -778,6 +895,22 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
Slog.e(TAG, "Remote failure in rate.", e);
}
}
+
+ public void adjustVolumeBy(int delta) {
+ try {
+ mCb.onAdjustVolumeBy(delta);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in adjustVolumeBy.", e);
+ }
+ }
+
+ public void setVolumeTo(int value) {
+ try {
+ mCb.onSetVolumeTo(value);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in adjustVolumeBy.", e);
+ }
+ }
}
class ControllerStub extends ISessionController.Stub {
@@ -788,8 +921,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
@Override
- public void sendMediaButton(KeyEvent mediaButtonIntent) {
- mSessionCb.sendMediaButton(mediaButtonIntent, 0, null);
+ public boolean sendMediaButton(KeyEvent mediaButtonIntent) {
+ return mSessionCb.sendMediaButton(mediaButtonIntent, 0, null);
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 9d85167..87665e1 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -26,6 +26,8 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.media.AudioManager;
+import android.media.IAudioService;
import android.media.routeprovider.RouteRequest;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
@@ -40,6 +42,7 @@ import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.speech.RecognizerIntent;
@@ -79,6 +82,7 @@ public class MediaSessionService extends SystemService implements Monitor {
private final PowerManager.WakeLock mMediaEventWakeLock;
private KeyguardManager mKeyguardManager;
+ private IAudioService mAudioService;
private MediaSessionRecord mPrioritySession;
private int mCurrentUserId = -1;
@@ -105,6 +109,12 @@ public class MediaSessionService extends SystemService implements Monitor {
updateUser();
mKeyguardManager =
(KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
+ mAudioService = getAudioService();
+ }
+
+ private IAudioService getAudioService() {
+ IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+ return IAudioService.Stub.asInterface(b);
}
/**
@@ -703,6 +713,23 @@ public class MediaSessionService extends SystemService implements Monitor {
}
@Override
+ public void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags)
+ throws RemoteException {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ MediaSessionRecord session = mPriorityStack
+ .getDefaultVolumeSession(mCurrentUserId);
+ dispatchAdjustVolumeByLocked(suggestedStream, delta, flags, session);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -737,6 +764,49 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
+ private void dispatchAdjustVolumeByLocked(int suggestedStream, int delta, int flags,
+ MediaSessionRecord session) {
+ int direction = 0;
+ int steps = delta;
+ if (delta > 0) {
+ direction = 1;
+ } else if (delta < 0) {
+ direction = -1;
+ steps = -delta;
+ }
+ if (DEBUG) {
+ String sessionInfo = session == null ? null : session.getSessionInfo().toString();
+ Log.d(TAG, "Adjusting session " + sessionInfo + " by " + delta + ". flags=" + flags
+ + ", suggestedStream=" + suggestedStream);
+
+ }
+ if (session == null) {
+ for (int i = 0; i < steps; i++) {
+ try {
+ mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
+ flags, getContext().getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error adjusting default volume.", e);
+ }
+ }
+ } else {
+ if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_LOCAL) {
+ for (int i = 0; i < steps; i++) {
+ try {
+ mAudioService.adjustSuggestedStreamVolume(direction,
+ session.getAudioStream(), flags,
+ getContext().getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error adjusting volume for stream "
+ + session.getAudioStream(), e);
+ }
+ }
+ } else if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_REMOTE) {
+ session.adjustVolumeBy(delta);
+ }
+ }
+ }
+
private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
MediaSessionRecord session) {
if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 7ba9212..803dee2 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -33,18 +33,18 @@ public class MediaSessionStack {
* bump priority regardless of the old state.
*/
private static final int[] ALWAYS_PRIORITY_STATES = {
- PlaybackState.PLAYSTATE_FAST_FORWARDING,
- PlaybackState.PLAYSTATE_REWINDING,
- PlaybackState.PLAYSTATE_SKIPPING_BACKWARDS,
- PlaybackState.PLAYSTATE_SKIPPING_FORWARDS };
+ PlaybackState.STATE_FAST_FORWARDING,
+ PlaybackState.STATE_REWINDING,
+ PlaybackState.STATE_SKIPPING_TO_PREVIOUS,
+ PlaybackState.STATE_SKIPPING_TO_NEXT };
/**
* These are states that usually indicate the user took an action if they
* were entered from a non-priority state.
*/
private static final int[] TRANSITION_PRIORITY_STATES = {
- PlaybackState.PLAYSTATE_BUFFERING,
- PlaybackState.PLAYSTATE_CONNECTING,
- PlaybackState.PLAYSTATE_PLAYING };
+ PlaybackState.STATE_BUFFERING,
+ PlaybackState.STATE_CONNECTING,
+ PlaybackState.STATE_PLAYING };
private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
@@ -52,6 +52,7 @@ public class MediaSessionStack {
private MediaSessionRecord mCachedButtonReceiver;
private MediaSessionRecord mCachedDefault;
+ private MediaSessionRecord mCachedVolumeDefault;
private ArrayList<MediaSessionRecord> mCachedActiveList;
private ArrayList<MediaSessionRecord> mCachedTransportControlList;
@@ -93,6 +94,9 @@ public class MediaSessionStack {
mSessions.remove(record);
mSessions.add(0, record);
clearCache();
+ } else if (newState == PlaybackState.STATE_PAUSED) {
+ // Just clear the volume cache in this case
+ mCachedVolumeDefault = null;
}
}
@@ -177,6 +181,25 @@ public class MediaSessionStack {
return mCachedButtonReceiver;
}
+ public MediaSessionRecord getDefaultVolumeSession(int userId) {
+ if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
+ return mGlobalPrioritySession;
+ }
+ if (mCachedVolumeDefault != null) {
+ return mCachedVolumeDefault;
+ }
+ ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
+ int size = records.size();
+ for (int i = 0; i < size; i++) {
+ MediaSessionRecord record = records.get(i);
+ if (record.isPlaybackActive(false)) {
+ mCachedVolumeDefault = record;
+ return record;
+ }
+ }
+ return null;
+ }
+
public void dump(PrintWriter pw, String prefix) {
ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0,
UserHandle.USER_ALL);
@@ -237,7 +260,7 @@ public class MediaSessionStack {
lastLocalIndex++;
lastActiveIndex++;
lastPublishedIndex++;
- } else if (session.isPlaybackActive()) {
+ } else if (session.isPlaybackActive(true)) {
// TODO replace getRoute() == null with real local route check
if(session.getRoute() == null) {
// Active local sessions get top priority
@@ -284,6 +307,7 @@ public class MediaSessionStack {
private void clearCache() {
mCachedDefault = null;
+ mCachedVolumeDefault = null;
mCachedButtonReceiver = null;
mCachedActiveList = null;
mCachedTransportControlList = null;
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 49293d3..b30baea 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -21,11 +21,10 @@ import java.util.Comparator;
* Sorts notificaitons into attention-relelvant order.
*/
public class NotificationComparator
- implements Comparator<NotificationManagerService.NotificationRecord> {
+ implements Comparator<NotificationRecord> {
@Override
- public int compare(NotificationManagerService.NotificationRecord lhs,
- NotificationManagerService.NotificationRecord rhs) {
+ public int compare(NotificationRecord lhs, NotificationRecord rhs) {
if (lhs.isRecentlyIntrusive() != rhs.isRecentlyIntrusive()) {
return lhs.isRecentlyIntrusive() ? -1 : 1;
}
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index db17f3a..d8ab9d7 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -20,8 +20,6 @@ import android.app.Notification;
import android.content.Context;
import android.util.Slog;
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
/**
* This {@link com.android.server.notification.NotificationSignalExtractor} noticies noisy
* notifications and marks them to get a temporary ranking bump.
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bbc3091..cb78a45 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -43,7 +43,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.IRingtonePlayer;
import android.net.Uri;
@@ -60,13 +59,13 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
-import android.service.notification.INotificationListener;
+import android.service.notification.Condition;
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
+import android.service.notification.INotificationListener;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.StatusBarNotification;
-import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -87,7 +86,6 @@ import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import com.android.server.notification.ManagedServices.UserProfiles;
-import com.android.server.notification.NotificationUsageStats.SingleNotificationStats;
import com.android.server.statusbar.StatusBarManagerInternal;
import libcore.io.IoUtils;
@@ -103,10 +101,8 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.reflect.Array;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
@@ -211,8 +207,6 @@ public class NotificationManagerService extends SystemService {
private ConditionProviders mConditionProviders;
private NotificationUsageStats mUsageStats;
- private static final String EXTRA_INTERCEPT = "android.intercept";
-
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
private static final int REASON_DELEGATE_CLICK = 1;
@@ -425,144 +419,6 @@ public class NotificationManagerService extends SystemService {
return true;
}
- private static String idDebugString(Context baseContext, String packageName, int id) {
- Context c = null;
-
- if (packageName != null) {
- try {
- c = baseContext.createPackageContext(packageName, 0);
- } catch (NameNotFoundException e) {
- c = baseContext;
- }
- } else {
- c = baseContext;
- }
-
- String pkg;
- String type;
- String name;
-
- Resources r = c.getResources();
- try {
- return r.getResourceName(id);
- } catch (Resources.NotFoundException e) {
- return "<name unknown>";
- }
- }
-
-
-
- public static final class NotificationRecord {
- final StatusBarNotification sbn;
- SingleNotificationStats stats;
- boolean isCanceled;
-
- // These members are used by NotificationSignalExtractors
- // to communicate with the ranking module.
- private float mContactAffinity;
- private boolean mRecentlyIntrusive;
-
- NotificationRecord(StatusBarNotification sbn)
- {
- this.sbn = sbn;
- }
-
- public Notification getNotification() { return sbn.getNotification(); }
- public int getFlags() { return sbn.getNotification().flags; }
- public int getUserId() { return sbn.getUserId(); }
- public String getKey() { return sbn.getKey(); }
-
- void dump(PrintWriter pw, String prefix, Context baseContext) {
- final Notification notification = sbn.getNotification();
- pw.println(prefix + this);
- pw.println(prefix + " uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
- pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
- + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
- pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore());
- pw.println(prefix + " key=" + sbn.getKey());
- pw.println(prefix + " contentIntent=" + notification.contentIntent);
- pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
- pw.println(prefix + " tickerText=" + notification.tickerText);
- pw.println(prefix + " contentView=" + notification.contentView);
- pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x",
- notification.defaults, notification.flags));
- pw.println(prefix + " sound=" + notification.sound);
- pw.println(prefix + String.format(" color=0x%08x", notification.color));
- pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
- pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
- notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
- if (notification.actions != null && notification.actions.length > 0) {
- pw.println(prefix + " actions={");
- final int N = notification.actions.length;
- for (int i=0; i<N; i++) {
- final Notification.Action action = notification.actions[i];
- pw.println(String.format("%s [%d] \"%s\" -> %s",
- prefix,
- i,
- action.title,
- action.actionIntent.toString()
- ));
- }
- pw.println(prefix + " }");
- }
- if (notification.extras != null && notification.extras.size() > 0) {
- pw.println(prefix + " extras={");
- for (String key : notification.extras.keySet()) {
- pw.print(prefix + " " + key + "=");
- Object val = notification.extras.get(key);
- if (val == null) {
- pw.println("null");
- } else {
- pw.print(val.getClass().getSimpleName());
- if (val instanceof CharSequence || val instanceof String) {
- // redact contents from bugreports
- } else if (val instanceof Bitmap) {
- pw.print(String.format(" (%dx%d)",
- ((Bitmap) val).getWidth(),
- ((Bitmap) val).getHeight()));
- } else if (val.getClass().isArray()) {
- final int N = Array.getLength(val);
- pw.println(" (" + N + ")");
- } else {
- pw.print(" (" + String.valueOf(val) + ")");
- }
- pw.println();
- }
- }
- pw.println(prefix + " }");
- }
- pw.println(prefix + " stats=" + stats.toString());
- pw.println(prefix + " mContactAffinity=" + mContactAffinity);
- pw.println(prefix + " mRecentlyIntrusive=" + mRecentlyIntrusive);
- }
-
- @Override
- public final String toString() {
- return String.format(
- "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
- System.identityHashCode(this),
- this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
- this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
- this.sbn.getNotification());
- }
-
- public void setContactAffinity(float contactAffinity) {
- mContactAffinity = contactAffinity;
- }
-
- public float getContactAffinity() {
- return mContactAffinity;
- }
-
- public void setRecentlyIntusive(boolean recentlyIntrusive) {
- mRecentlyIntrusive = recentlyIntrusive;
- }
-
- public boolean isRecentlyIntrusive() {
- return mRecentlyIntrusive;
- }
- }
-
private static final class ToastRecord
{
final int pid;
@@ -1657,15 +1513,15 @@ public class NotificationManagerService extends SystemService {
return;
}
- // Is this notification intercepted by zen mode?
- final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification);
- notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
-
- // Should this notification make noise, vibe, or use the LED?
- final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
- if (DBG || intercept) Slog.v(TAG,
- "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept);
synchronized (mNotificationList) {
+ applyZenModeLocked(r);
+
+ // Should this notification make noise, vibe, or use the LED?
+ final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) &&
+ !r.isIntercepted();
+ if (DBG || r.isIntercepted()) Slog.v(TAG,
+ "pkg=" + pkg + " canInterrupt=" + canInterrupt +
+ " intercept=" + r.isIntercepted());
NotificationRecord old = null;
int index = indexOfNotificationLocked(n.getKey());
if (index < 0) {
@@ -1678,6 +1534,8 @@ public class NotificationManagerService extends SystemService {
// Make sure we don't lose the foreground service state.
notification.flags |=
old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
+ // Retain ranking information from previous record
+ r.copyRankingInformation(old);
mNotificationsByKey.remove(old.sbn.getKey());
}
mNotificationsByKey.put(n.getKey(), r);
@@ -1724,7 +1582,7 @@ public class NotificationManagerService extends SystemService {
sendAccessibilityEvent(notification, pkg);
}
- mListeners.notifyPostedLocked(r.sbn, cloneNotificationListLocked());
+ mListeners.notifyPostedLocked(r.sbn);
} else {
Slog.e(TAG, "Not posting notification with icon==0: " + notification);
if (old != null && !old.isCanceled) {
@@ -1735,7 +1593,7 @@ public class NotificationManagerService extends SystemService {
Binder.restoreCallingIdentity(identity);
}
- mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked());
+ mListeners.notifyRemovedLocked(r.sbn);
}
// ATTENTION: in a future release we will bail out here
// so that we do not play sounds, show lights, etc. for invalid
@@ -1992,23 +1850,32 @@ public class NotificationManagerService extends SystemService {
if (!(message.obj instanceof RankingReconsideration)) return;
RankingReconsideration recon = (RankingReconsideration) message.obj;
recon.run();
- boolean orderChanged;
+ boolean changed;
synchronized (mNotificationList) {
final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
if (record == null) {
return;
}
- int before = findNotificationRecordIndexLocked(record);
+ int indexBefore = findNotificationRecordIndexLocked(record);
+ boolean interceptBefore = record.isIntercepted();
recon.applyChangesLocked(record);
+ applyZenModeLocked(record);
Collections.sort(mNotificationList, mRankingComparator);
- int after = findNotificationRecordIndexLocked(record);
- orderChanged = before != after;
+ int indexAfter = findNotificationRecordIndexLocked(record);
+ boolean interceptAfter = record.isIntercepted();
+ changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
}
- if (orderChanged) {
+ if (changed) {
scheduleSendRankingUpdate();
}
}
+ // let zen mode evaluate this record and then make note of that for the future
+ private void applyZenModeLocked(NotificationRecord record) {
+ record.setIntercepted(mZenModeHelper.shouldIntercept(record, record.wasTouchedByZen()));
+ record.setTouchedByZen();
+ }
+
// lock on mNotificationList
private int findNotificationRecordIndexLocked(NotificationRecord target) {
return Collections.binarySearch(mNotificationList, target, mRankingComparator);
@@ -2022,19 +1889,10 @@ public class NotificationManagerService extends SystemService {
private void handleSendRankingUpdate() {
synchronized (mNotificationList) {
- mListeners.notifyRankingUpdateLocked(cloneNotificationListLocked());
+ mListeners.notifyRankingUpdateLocked();
}
}
- private ArrayList<StatusBarNotification> cloneNotificationListLocked() {
- final int N = mNotificationList.size();
- ArrayList<StatusBarNotification> sbns = new ArrayList<StatusBarNotification>(N);
- for (int i = 0; i < N; i++) {
- sbns.add(mNotificationList.get(i).sbn);
- }
- return sbns;
- }
-
private final class WorkerHandler extends Handler
{
@Override
@@ -2120,7 +1978,7 @@ public class NotificationManagerService extends SystemService {
Binder.restoreCallingIdentity(identity);
}
r.isCanceled = true;
- mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked());
+ mListeners.notifyRemovedLocked(r.sbn);
}
// sound
@@ -2441,22 +2299,25 @@ public class NotificationManagerService extends SystemService {
/**
* Generates a NotificationRankingUpdate from 'sbns', considering only
* notifications visible to the given listener.
+ *
+ * <p>Caller must hold a lock on mNotificationList.</p>
*/
- private static NotificationRankingUpdate makeRankingUpdateForListener(ManagedServiceInfo info,
- ArrayList<StatusBarNotification> sbns) {
+ private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
int speedBumpIndex = -1;
- ArrayList<String> keys = new ArrayList<String>(sbns.size());
- ArrayList<String> dndKeys = new ArrayList<String>(sbns.size());
- for (StatusBarNotification sbn: sbns) {
- if (!info.enabledAndUserMatches(sbn.getUserId())) {
+ final int N = mNotificationList.size();
+ ArrayList<String> keys = new ArrayList<String>(N);
+ ArrayList<String> dndKeys = new ArrayList<String>(N);
+ for (int i = 0; i < N; i++) {
+ NotificationRecord record = mNotificationList.get(i);
+ if (!info.enabledAndUserMatches(record.sbn.getUserId())) {
continue;
}
- keys.add(sbn.getKey());
- if (sbn.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) {
- dndKeys.add(sbn.getKey());
+ keys.add(record.sbn.getKey());
+ if (record.isIntercepted()) {
+ dndKeys.add(record.sbn.getKey());
}
if (speedBumpIndex == -1 &&
- sbn.getNotification().priority == Notification.PRIORITY_MIN) {
+ record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
speedBumpIndex = keys.size() - 1;
}
}
@@ -2491,12 +2352,12 @@ public class NotificationManagerService extends SystemService {
@Override
public void onServiceAdded(ManagedServiceInfo info) {
final INotificationListener listener = (INotificationListener) info.service;
- final ArrayList<StatusBarNotification> sbns;
+ final NotificationRankingUpdate update;
synchronized (mNotificationList) {
- sbns = cloneNotificationListLocked();
+ update = makeRankingUpdateLocked(info);
}
try {
- listener.onListenerConnected(makeRankingUpdateForListener(info, sbns));
+ listener.onListenerConnected(update);
} catch (RemoteException e) {
// we tried
}
@@ -2505,15 +2366,14 @@ public class NotificationManagerService extends SystemService {
/**
* asynchronously notify all listeners about a new notification
*/
- public void notifyPostedLocked(StatusBarNotification sbn,
- final ArrayList<StatusBarNotification> sbns) {
+ public void notifyPostedLocked(StatusBarNotification sbn) {
// make a copy in case changes are made to the underlying Notification object
final StatusBarNotification sbnClone = sbn.clone();
for (final ManagedServiceInfo info : mServices) {
if (!info.isEnabledForCurrentProfiles()) {
continue;
}
- final NotificationRankingUpdate update = makeRankingUpdateForListener(info, sbns);
+ final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
if (update.getOrderedKeys().length == 0) {
continue;
}
@@ -2529,8 +2389,7 @@ public class NotificationManagerService extends SystemService {
/**
* asynchronously notify all listeners about a removed notification
*/
- public void notifyRemovedLocked(StatusBarNotification sbn,
- final ArrayList<StatusBarNotification> sbns) {
+ public void notifyRemovedLocked(StatusBarNotification sbn) {
// make a copy in case changes are made to the underlying Notification object
// NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
// notification
@@ -2539,11 +2398,11 @@ public class NotificationManagerService extends SystemService {
if (!info.isEnabledForCurrentProfiles()) {
continue;
}
+ final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyRemovedIfUserMatch(info, sbnLight,
- makeRankingUpdateForListener(info, sbns));
+ notifyRemovedIfUserMatch(info, sbnLight, update);
}
});
}
@@ -2551,20 +2410,18 @@ public class NotificationManagerService extends SystemService {
/**
* asynchronously notify all listeners about a reordering of notifications
- * @param sbns an array of {@link StatusBarNotification}s to consider. This code
- * must not rely on mutable members of these objects, such as the
- * {@link Notification}.
*/
- public void notifyRankingUpdateLocked(final ArrayList<StatusBarNotification> sbns) {
+ public void notifyRankingUpdateLocked() {
for (final ManagedServiceInfo serviceInfo : mServices) {
if (!serviceInfo.isEnabledForCurrentProfiles()) {
continue;
}
+ final NotificationRankingUpdate update =
+ makeRankingUpdateLocked(serviceInfo);
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyRankingUpdate(serviceInfo,
- makeRankingUpdateForListener(serviceInfo, sbns));
+ notifyRankingUpdate(serviceInfo, update);
}
});
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
new file mode 100644
index 0000000..08f8eb4
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+package com.android.server.notification;
+
+import android.app.Notification;
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.service.notification.StatusBarNotification;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+
+/**
+ * Holds data about notifications that should not be shared with the
+ * {@link android.service.notification.NotificationListenerService}s.
+ *
+ * <p>These objects should not be mutated unless the code is synchronized
+ * on {@link NotificationManagerService#mNotificationList}, and any
+ * modification should be followed by a sorting of that list.</p>
+ *
+ * <p>Is sortable by {@link NotificationComparator}.</p>
+ *
+ * {@hide}
+ */
+public final class NotificationRecord {
+ final StatusBarNotification sbn;
+ NotificationUsageStats.SingleNotificationStats stats;
+ boolean isCanceled;
+
+ // These members are used by NotificationSignalExtractors
+ // to communicate with the ranking module.
+ private float mContactAffinity;
+ private boolean mRecentlyIntrusive;
+
+ // is this notification currently being intercepted by Zen Mode?
+ private boolean mIntercept;
+ // InterceptedNotifications needs to know if this has been previously evaluated.
+ private boolean mTouchedByZen;
+
+ NotificationRecord(StatusBarNotification sbn)
+ {
+ this.sbn = sbn;
+ }
+
+ // copy any notes that the ranking system may have made before the update
+ public void copyRankingInformation(NotificationRecord previous) {
+ mContactAffinity = previous.mContactAffinity;
+ mRecentlyIntrusive = previous.mRecentlyIntrusive;
+ mTouchedByZen = previous.mTouchedByZen;
+ mIntercept = previous.mIntercept;
+ }
+
+ public Notification getNotification() { return sbn.getNotification(); }
+ public int getFlags() { return sbn.getNotification().flags; }
+ public int getUserId() { return sbn.getUserId(); }
+ public String getKey() { return sbn.getKey(); }
+
+ void dump(PrintWriter pw, String prefix, Context baseContext) {
+ final Notification notification = sbn.getNotification();
+ pw.println(prefix + this);
+ pw.println(prefix + " uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
+ pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
+ + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
+ pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore());
+ pw.println(prefix + " key=" + sbn.getKey());
+ pw.println(prefix + " contentIntent=" + notification.contentIntent);
+ pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
+ pw.println(prefix + " tickerText=" + notification.tickerText);
+ pw.println(prefix + " contentView=" + notification.contentView);
+ pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x",
+ notification.defaults, notification.flags));
+ pw.println(prefix + " sound=" + notification.sound);
+ pw.println(prefix + String.format(" color=0x%08x", notification.color));
+ pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
+ pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
+ notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
+ if (notification.actions != null && notification.actions.length > 0) {
+ pw.println(prefix + " actions={");
+ final int N = notification.actions.length;
+ for (int i=0; i<N; i++) {
+ final Notification.Action action = notification.actions[i];
+ pw.println(String.format("%s [%d] \"%s\" -> %s",
+ prefix,
+ i,
+ action.title,
+ action.actionIntent.toString()
+ ));
+ }
+ pw.println(prefix + " }");
+ }
+ if (notification.extras != null && notification.extras.size() > 0) {
+ pw.println(prefix + " extras={");
+ for (String key : notification.extras.keySet()) {
+ pw.print(prefix + " " + key + "=");
+ Object val = notification.extras.get(key);
+ if (val == null) {
+ pw.println("null");
+ } else {
+ pw.print(val.getClass().getSimpleName());
+ if (val instanceof CharSequence || val instanceof String) {
+ // redact contents from bugreports
+ } else if (val instanceof Bitmap) {
+ pw.print(String.format(" (%dx%d)",
+ ((Bitmap) val).getWidth(),
+ ((Bitmap) val).getHeight()));
+ } else if (val.getClass().isArray()) {
+ final int N = Array.getLength(val);
+ pw.println(" (" + N + ")");
+ } else {
+ pw.print(" (" + String.valueOf(val) + ")");
+ }
+ pw.println();
+ }
+ }
+ pw.println(prefix + " }");
+ }
+ pw.println(prefix + " stats=" + stats.toString());
+ pw.println(prefix + " mContactAffinity=" + mContactAffinity);
+ pw.println(prefix + " mRecentlyIntrusive=" + mRecentlyIntrusive);
+ pw.println(prefix + " mIntercept=" + mIntercept);
+ }
+
+
+ static String idDebugString(Context baseContext, String packageName, int id) {
+ Context c;
+
+ if (packageName != null) {
+ try {
+ c = baseContext.createPackageContext(packageName, 0);
+ } catch (NameNotFoundException e) {
+ c = baseContext;
+ }
+ } else {
+ c = baseContext;
+ }
+
+ Resources r = c.getResources();
+ try {
+ return r.getResourceName(id);
+ } catch (Resources.NotFoundException e) {
+ return "<name unknown>";
+ }
+ }
+
+ @Override
+ public final String toString() {
+ return String.format(
+ "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
+ System.identityHashCode(this),
+ this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
+ this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
+ this.sbn.getNotification());
+ }
+
+ public void setContactAffinity(float contactAffinity) {
+ mContactAffinity = contactAffinity;
+ }
+
+ public float getContactAffinity() {
+ return mContactAffinity;
+ }
+
+ public void setRecentlyIntusive(boolean recentlyIntrusive) {
+ mRecentlyIntrusive = recentlyIntrusive;
+ }
+
+ public boolean isRecentlyIntrusive() {
+ return mRecentlyIntrusive;
+ }
+
+ public boolean setIntercepted(boolean intercept) {
+ mIntercept = intercept;
+ return mIntercept;
+ }
+
+ public boolean isIntercepted() {
+ return mIntercept;
+ }
+
+ public boolean wasTouchedByZen() {
+ return mTouchedByZen;
+ }
+
+ public void setTouchedByZen() {
+ mTouchedByZen = true;
+ }
+
+}
diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
index 71c819e..1537ea9 100644
--- a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
@@ -18,11 +18,9 @@ package com.android.server.notification;
import android.content.Context;
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
/**
* Extracts signals that will be useful to the {@link NotificationComparator} and caches them
- * on the {@link NotificationManagerService.NotificationRecord} object. These annotations will
+ * on the {@link NotificationRecord} object. These annotations will
* not be passed on to {@link android.service.notification.NotificationListenerService}s.
*/
public interface NotificationSignalExtractor {
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index 009943f..5081bf7 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -16,8 +16,6 @@
package com.android.server.notification;
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
diff --git a/services/core/java/com/android/server/notification/RankingReconsideration.java b/services/core/java/com/android/server/notification/RankingReconsideration.java
index cf5e210..057f0f1 100644
--- a/services/core/java/com/android/server/notification/RankingReconsideration.java
+++ b/services/core/java/com/android/server/notification/RankingReconsideration.java
@@ -15,8 +15,6 @@
*/
package com.android.server.notification;
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
import java.util.concurrent.TimeUnit;
/**
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index a3858d0..4ac2dcc 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -28,8 +28,6 @@ import android.text.TextUtils;
import android.util.LruCache;
import android.util.Slog;
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
import java.util.ArrayList;
import java.util.LinkedList;
@@ -51,9 +49,20 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
private static final int MAX_PEOPLE = 10;
private static final int PEOPLE_CACHE_SIZE = 200;
- private static final float NONE = 0f;
- private static final float VALID_CONTACT = 0.5f;
- private static final float STARRED_CONTACT = 1f;
+ /** Indicates that the notification does not reference any valid contacts. */
+ static final float NONE = 0f;
+
+ /**
+ * Affinity will be equal to or greater than this value on notifications
+ * that reference a valid contact.
+ */
+ static final float VALID_CONTACT = 0.5f;
+
+ /**
+ * Affinity will be equal to or greater than this value on notifications
+ * that reference a starred contact.
+ */
+ static final float STARRED_CONTACT = 1f;
protected boolean mEnabled;
private Context mContext;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 154ac96..50a32c4 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -18,7 +18,6 @@ package com.android.server.notification;
import android.app.AlarmManager;
import android.app.AppOpsManager;
-import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -78,11 +77,13 @@ public class ZenModeHelper {
// temporary, until we update apps to provide metadata
private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
"com.google.android.dialer",
- "com.android.phone"
+ "com.android.phone",
+ "com.android.example.notificationshowcase"
));
private static final Set<String> MESSAGE_PACKAGES = new HashSet<String>(Arrays.asList(
"com.google.android.talk",
- "com.android.mms"
+ "com.android.mms",
+ "com.android.example.notificationshowcase"
));
private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList(
"com.google.android.deskclock"
@@ -123,15 +124,23 @@ public class ZenModeHelper {
mCallbacks.add(callback);
}
- public boolean shouldIntercept(String pkg, Notification n) {
+ public boolean shouldIntercept(NotificationRecord record, boolean previouslySeen) {
if (mZenMode != Global.ZEN_MODE_OFF) {
- if (isAlarm(pkg, n)) {
+ if (previouslySeen && !record.isIntercepted()) {
+ // notifications never transition from not intercepted to intercepted
return false;
}
- if (isCall(pkg, n)) {
+ if (isAlarm(record)) {
+ return false;
+ }
+ // audience has veto power over all following rules
+ if (!audienceMatches(record)) {
+ return true;
+ }
+ if (isCall(record)) {
return !mConfig.allowCalls;
}
- if (isMessage(pkg, n)) {
+ if (isMessage(record)) {
return !mConfig.allowMessages;
}
return true;
@@ -176,7 +185,8 @@ public class ZenModeHelper {
}
public boolean allowDisable(int what, IBinder token, String pkg) {
- if (isCall(pkg, null)) {
+ // TODO(cwren): delete this API before the next release. Bug:15344099
+ if (CALL_PACKAGES.contains(pkg)) {
return mZenMode == Global.ZEN_MODE_OFF || mConfig.allowCalls;
}
return true;
@@ -229,16 +239,30 @@ public class ZenModeHelper {
}
}
- private boolean isAlarm(String pkg, Notification n) {
- return ALARM_PACKAGES.contains(pkg);
+ private boolean isAlarm(NotificationRecord record) {
+ return ALARM_PACKAGES.contains(record.sbn.getPackageName());
}
- private boolean isCall(String pkg, Notification n) {
- return CALL_PACKAGES.contains(pkg);
+ private boolean isCall(NotificationRecord record) {
+ return CALL_PACKAGES.contains(record.sbn.getPackageName());
}
- private boolean isMessage(String pkg, Notification n) {
- return MESSAGE_PACKAGES.contains(pkg);
+ private boolean isMessage(NotificationRecord record) {
+ return MESSAGE_PACKAGES.contains(record.sbn.getPackageName());
+ }
+
+ private boolean audienceMatches(NotificationRecord record) {
+ switch (mConfig.allowFrom) {
+ case ZenModeConfig.SOURCE_ANYONE:
+ return true;
+ case ZenModeConfig.SOURCE_CONTACT:
+ return record.getContactAffinity() >= ValidateNotificationPeople.VALID_CONTACT;
+ case ZenModeConfig.SOURCE_STAR:
+ return record.getContactAffinity() >= ValidateNotificationPeople.STARRED_CONTACT;
+ default:
+ Slog.w(TAG, "Encountered unknown source: " + mConfig.allowFrom);
+ return true;
+ }
}
private void updateAlarms() {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 5e3325c..25ebfc0 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -40,6 +40,7 @@ import android.util.Log;
import android.util.Slog;
import com.android.internal.content.PackageMonitor;
+import com.android.server.SystemService;
import java.util.ArrayList;
import java.util.List;
@@ -48,358 +49,374 @@ import java.util.List;
* Service that manages requests and callbacks for launchers that support
* managed profiles.
*/
-public class LauncherAppsService extends ILauncherApps.Stub {
- private static final boolean DEBUG = false;
- private static final String TAG = "LauncherAppsService";
- private final Context mContext;
- private final PackageManager mPm;
- private final UserManager mUm;
- private final PackageCallbackList<IOnAppsChangedListener> mListeners
- = new PackageCallbackList<IOnAppsChangedListener>();
- private MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+public class LauncherAppsService extends SystemService {
+
+ private final LauncherAppsImpl mLauncherAppsImpl;
public LauncherAppsService(Context context) {
- mContext = context;
- mPm = mContext.getPackageManager();
- mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ super(context);
+ mLauncherAppsImpl = new LauncherAppsImpl(context);
}
- /*
- * @see android.content.pm.ILauncherApps#addOnAppsChangedListener(
- * android.content.pm.IOnAppsChangedListener)
- */
@Override
- public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException {
- synchronized (mListeners) {
- if (DEBUG) {
- Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle());
- }
- if (mListeners.getRegisteredCallbackCount() == 0) {
+ public void onStart() {
+ publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
+ }
+
+ class LauncherAppsImpl extends ILauncherApps.Stub {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "LauncherAppsService";
+ private final Context mContext;
+ private final PackageManager mPm;
+ private final UserManager mUm;
+ private final PackageCallbackList<IOnAppsChangedListener> mListeners
+ = new PackageCallbackList<IOnAppsChangedListener>();
+
+ private MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+
+ public LauncherAppsImpl(Context context) {
+ mContext = context;
+ mPm = mContext.getPackageManager();
+ mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ }
+
+ /*
+ * @see android.content.pm.ILauncherApps#addOnAppsChangedListener(
+ * android.content.pm.IOnAppsChangedListener)
+ */
+ @Override
+ public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException {
+ synchronized (mListeners) {
if (DEBUG) {
- Log.d(TAG, "Starting package monitoring");
+ Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle());
}
- startWatchingPackageBroadcasts();
+ if (mListeners.getRegisteredCallbackCount() == 0) {
+ if (DEBUG) {
+ Log.d(TAG, "Starting package monitoring");
+ }
+ startWatchingPackageBroadcasts();
+ }
+ mListeners.unregister(listener);
+ mListeners.register(listener, Binder.getCallingUserHandle());
}
- mListeners.unregister(listener);
- mListeners.register(listener, Binder.getCallingUserHandle());
}
- }
- /*
- * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener(
- * android.content.pm.IOnAppsChangedListener)
- */
- @Override
- public void removeOnAppsChangedListener(IOnAppsChangedListener listener)
- throws RemoteException {
- synchronized (mListeners) {
- if (DEBUG) {
- Log.d(TAG, "Removing listener from " + Binder.getCallingUserHandle());
- }
- mListeners.unregister(listener);
- if (mListeners.getRegisteredCallbackCount() == 0) {
- stopWatchingPackageBroadcasts();
+ /*
+ * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener(
+ * android.content.pm.IOnAppsChangedListener)
+ */
+ @Override
+ public void removeOnAppsChangedListener(IOnAppsChangedListener listener)
+ throws RemoteException {
+ synchronized (mListeners) {
+ if (DEBUG) {
+ Log.d(TAG, "Removing listener from " + Binder.getCallingUserHandle());
+ }
+ mListeners.unregister(listener);
+ if (mListeners.getRegisteredCallbackCount() == 0) {
+ stopWatchingPackageBroadcasts();
+ }
}
}
- }
-
- /**
- * Register a receiver to watch for package broadcasts
- */
- private void startWatchingPackageBroadcasts() {
- mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
- }
- /**
- * Unregister package broadcast receiver
- */
- private void stopWatchingPackageBroadcasts() {
- if (DEBUG) {
- Log.d(TAG, "Stopped watching for packages");
+ /**
+ * Register a receiver to watch for package broadcasts
+ */
+ private void startWatchingPackageBroadcasts() {
+ mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
}
- mPackageMonitor.unregister();
- }
- void checkCallbackCount() {
- synchronized (mListeners) {
+ /**
+ * Unregister package broadcast receiver
+ */
+ private void stopWatchingPackageBroadcasts() {
if (DEBUG) {
- Log.d(TAG, "Callback count = " + mListeners.getRegisteredCallbackCount());
- }
- if (mListeners.getRegisteredCallbackCount() == 0) {
- stopWatchingPackageBroadcasts();
+ Log.d(TAG, "Stopped watching for packages");
}
+ mPackageMonitor.unregister();
}
- }
- /**
- * Checks if the caller is in the same group as the userToCheck.
- */
- private void ensureInUserProfiles(UserHandle userToCheck, String message) {
- final int callingUserId = UserHandle.getCallingUserId();
- final int targetUserId = userToCheck.getIdentifier();
-
- if (targetUserId == callingUserId) return;
-
- long ident = Binder.clearCallingIdentity();
- try {
- UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
- UserInfo targetUserInfo = mUm.getUserInfo(targetUserId);
- if (targetUserInfo == null
- || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
- || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) {
- throw new SecurityException(message);
+ void checkCallbackCount() {
+ synchronized (mListeners) {
+ if (DEBUG) {
+ Log.d(TAG, "Callback count = " + mListeners.getRegisteredCallbackCount());
+ }
+ if (mListeners.getRegisteredCallbackCount() == 0) {
+ stopWatchingPackageBroadcasts();
+ }
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
- }
- /**
- * Checks if the user is enabled.
- */
- private boolean isUserEnabled(UserHandle user) {
- long ident = Binder.clearCallingIdentity();
- try {
- UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier());
- return targetUserInfo != null && targetUserInfo.isEnabled();
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
+ /**
+ * Checks if the caller is in the same group as the userToCheck.
+ */
+ private void ensureInUserProfiles(UserHandle userToCheck, String message) {
+ final int callingUserId = UserHandle.getCallingUserId();
+ final int targetUserId = userToCheck.getIdentifier();
- @Override
- public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
- throws RemoteException {
- ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
- if (!isUserEnabled(user)) {
- return new ArrayList<ResolveInfo>();
- }
+ if (targetUserId == callingUserId) return;
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- mainIntent.setPackage(packageName);
- long ident = Binder.clearCallingIdentity();
- try {
- List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0,
- user.getIdentifier());
- return apps;
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
- public ResolveInfo resolveActivity(Intent intent, UserHandle user)
- throws RemoteException {
- ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
- if (!isUserEnabled(user)) {
- return null;
+ long ident = Binder.clearCallingIdentity();
+ try {
+ UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
+ UserInfo targetUserInfo = mUm.getUserInfo(targetUserId);
+ if (targetUserInfo == null
+ || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
+ || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) {
+ throw new SecurityException(message);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- long ident = Binder.clearCallingIdentity();
- try {
- ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier());
- return app;
- } finally {
- Binder.restoreCallingIdentity(ident);
+ /**
+ * Checks if the user is enabled.
+ */
+ private boolean isUserEnabled(UserHandle user) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier());
+ return targetUserInfo != null && targetUserInfo.isEnabled();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- }
- @Override
- public boolean isPackageEnabled(String packageName, UserHandle user)
- throws RemoteException {
- ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
- if (!isUserEnabled(user)) {
- return false;
- }
+ @Override
+ public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
+ throws RemoteException {
+ ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
+ if (!isUserEnabled(user)) {
+ return new ArrayList<ResolveInfo>();
+ }
- long ident = Binder.clearCallingIdentity();
- try {
- IPackageManager pm = AppGlobals.getPackageManager();
- PackageInfo info = pm.getPackageInfo(packageName, 0, user.getIdentifier());
- return info != null && info.applicationInfo.enabled;
- } finally {
- Binder.restoreCallingIdentity(ident);
+ final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ mainIntent.setPackage(packageName);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0,
+ user.getIdentifier());
+ return apps;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- }
- @Override
- public boolean isActivityEnabled(ComponentName component, UserHandle user)
- throws RemoteException {
- ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user);
- if (!isUserEnabled(user)) {
- return false;
- }
+ @Override
+ public ResolveInfo resolveActivity(Intent intent, UserHandle user)
+ throws RemoteException {
+ ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
+ if (!isUserEnabled(user)) {
+ return null;
+ }
- long ident = Binder.clearCallingIdentity();
- try {
- IPackageManager pm = AppGlobals.getPackageManager();
- ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier());
- return info != null && info.isEnabled();
- } finally {
- Binder.restoreCallingIdentity(ident);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier());
+ return app;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- }
- @Override
- public void startActivityAsUser(ComponentName component, Rect sourceBounds,
- Bundle opts, UserHandle user) throws RemoteException {
- ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
- if (!isUserEnabled(user)) {
- throw new IllegalStateException("Cannot start activity for disabled profile " + user);
- }
+ @Override
+ public boolean isPackageEnabled(String packageName, UserHandle user)
+ throws RemoteException {
+ ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
+ if (!isUserEnabled(user)) {
+ return false;
+ }
- Intent launchIntent = new Intent(Intent.ACTION_MAIN);
- launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- launchIntent.setComponent(component);
- launchIntent.setSourceBounds(sourceBounds);
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- long ident = Binder.clearCallingIdentity();
- try {
- mContext.startActivityAsUser(launchIntent, opts, user);
- } finally {
- Binder.restoreCallingIdentity(ident);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ PackageInfo info = pm.getPackageInfo(packageName, 0, user.getIdentifier());
+ return info != null && info.applicationInfo.enabled;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- }
- private class MyPackageMonitor extends PackageMonitor {
-
- /** Checks if user is a profile of or same as listeningUser.
- * and the user is enabled. */
- private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
- String debugMsg) {
- if (user.getIdentifier() == listeningUser.getIdentifier()) {
- if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
- return true;
+ @Override
+ public boolean isActivityEnabled(ComponentName component, UserHandle user)
+ throws RemoteException {
+ ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user);
+ if (!isUserEnabled(user)) {
+ return false;
}
+
long ident = Binder.clearCallingIdentity();
try {
- UserInfo userInfo = mUm.getUserInfo(user.getIdentifier());
- UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
- if (userInfo == null || listeningUserInfo == null
- || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
- || userInfo.profileGroupId != listeningUserInfo.profileGroupId
- || !userInfo.isEnabled()) {
- if (DEBUG) {
- Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":"
- + debugMsg);
- }
- return false;
- } else {
- if (DEBUG) {
- Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":"
- + debugMsg);
- }
- return true;
- }
+ IPackageManager pm = AppGlobals.getPackageManager();
+ ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier());
+ return info != null && info.isEnabled();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
- public void onPackageAdded(String packageName, int uid) {
- UserHandle user = new UserHandle(getChangingUserId());
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue;
- try {
- listener.onPackageAdded(user, packageName);
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
- }
+ public void startActivityAsUser(ComponentName component, Rect sourceBounds,
+ Bundle opts, UserHandle user) throws RemoteException {
+ ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
+ if (!isUserEnabled(user)) {
+ throw new IllegalStateException("Cannot start activity for disabled profile " + user);
}
- mListeners.finishBroadcast();
- super.onPackageAdded(packageName, uid);
+ Intent launchIntent = new Intent(Intent.ACTION_MAIN);
+ launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ launchIntent.setComponent(component);
+ launchIntent.setSourceBounds(sourceBounds);
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startActivityAsUser(launchIntent, opts, user);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- UserHandle user = new UserHandle(getChangingUserId());
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue;
+ private class MyPackageMonitor extends PackageMonitor {
+
+ /** Checks if user is a profile of or same as listeningUser.
+ * and the user is enabled. */
+ private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
+ String debugMsg) {
+ if (user.getIdentifier() == listeningUser.getIdentifier()) {
+ if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
+ return true;
+ }
+ long ident = Binder.clearCallingIdentity();
try {
- listener.onPackageRemoved(user, packageName);
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
+ UserInfo userInfo = mUm.getUserInfo(user.getIdentifier());
+ UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
+ if (userInfo == null || listeningUserInfo == null
+ || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
+ || userInfo.profileGroupId != listeningUserInfo.profileGroupId
+ || !userInfo.isEnabled()) {
+ if (DEBUG) {
+ Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":"
+ + debugMsg);
+ }
+ return false;
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":"
+ + debugMsg);
+ }
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
- mListeners.finishBroadcast();
- super.onPackageRemoved(packageName, uid);
- }
-
- @Override
- public void onPackageModified(String packageName) {
- UserHandle user = new UserHandle(getChangingUserId());
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue;
- try {
- listener.onPackageChanged(user, packageName);
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ UserHandle user = new UserHandle(getChangingUserId());
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue;
+ try {
+ listener.onPackageAdded(user, packageName);
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
}
+ mListeners.finishBroadcast();
+
+ super.onPackageAdded(packageName, uid);
}
- mListeners.finishBroadcast();
- super.onPackageModified(packageName);
- }
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ UserHandle user = new UserHandle(getChangingUserId());
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue;
+ try {
+ listener.onPackageRemoved(user, packageName);
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
+ }
+ mListeners.finishBroadcast();
- @Override
- public void onPackagesAvailable(String[] packages) {
- UserHandle user = new UserHandle(getChangingUserId());
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
- try {
- listener.onPackagesAvailable(user, packages, isReplacing());
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
+ super.onPackageRemoved(packageName, uid);
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ UserHandle user = new UserHandle(getChangingUserId());
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue;
+ try {
+ listener.onPackageChanged(user, packageName);
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
}
+ mListeners.finishBroadcast();
+
+ super.onPackageModified(packageName);
}
- mListeners.finishBroadcast();
- super.onPackagesAvailable(packages);
- }
+ @Override
+ public void onPackagesAvailable(String[] packages) {
+ UserHandle user = new UserHandle(getChangingUserId());
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
+ try {
+ listener.onPackagesAvailable(user, packages, isReplacing());
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
+ }
+ mListeners.finishBroadcast();
- @Override
- public void onPackagesUnavailable(String[] packages) {
- UserHandle user = new UserHandle(getChangingUserId());
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
- try {
- listener.onPackagesUnavailable(user, packages, isReplacing());
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
+ super.onPackagesAvailable(packages);
+ }
+
+ @Override
+ public void onPackagesUnavailable(String[] packages) {
+ UserHandle user = new UserHandle(getChangingUserId());
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
+ try {
+ listener.onPackagesUnavailable(user, packages, isReplacing());
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
}
+ mListeners.finishBroadcast();
+
+ super.onPackagesUnavailable(packages);
}
- mListeners.finishBroadcast();
- super.onPackagesUnavailable(packages);
}
- }
-
- class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
- @Override
- public void onCallbackDied(T callback, Object cookie) {
- checkCallbackCount();
+ class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
+ @Override
+ public void onCallbackDied(T callback, Object cookie) {
+ checkCallbackCount();
+ }
}
}
-}
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 13cc98c..bb93663 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -40,6 +40,7 @@ import com.android.internal.R;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.content.NativeLibraryHelper.ApkHandle;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
@@ -4148,7 +4149,7 @@ public class PackageManagerService extends IPackageManager.Stub {
continue;
}
PackageParser.Package pkg = scanPackageLI(file,
- flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
+ flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null, null);
// Don't mess around with apps in system partition.
if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
@@ -4215,7 +4216,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* Returns null in case of errors and the error code is stored in mLastScanError
*/
private PackageParser.Package scanPackageLI(File scanFile,
- int parseFlags, int scanMode, long currentTime, UserHandle user) {
+ int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
String scanPath = scanFile.getPath();
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath);
@@ -4283,7 +4284,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
return null;
} else {
- // The current app on the system partion is better than
+ // The current app on the system partition is better than
// what we have updated to on the data partition; switch
// back to the system partition version.
// At this point, its safely assumed that package installation for
@@ -4402,7 +4403,7 @@ public class PackageManagerService extends IPackageManager.Stub {
setApplicationInfoPaths(pkg, codePath, resPath);
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
- | SCAN_UPDATE_SIGNATURE, currentTime, user);
+ | SCAN_UPDATE_SIGNATURE, currentTime, user, abiOverride);
/*
* If the system app should be overridden by a previously installed
@@ -4945,7 +4946,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
- int parseFlags, int scanMode, long currentTime, UserHandle user) {
+ int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
File scanFile = new File(pkg.mScanPath);
if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
pkg.applicationInfo.publicSourceDir == null) {
@@ -5395,7 +5396,22 @@ public class PackageManagerService extends IPackageManager.Stub {
* only for non-system apps and system app upgrades.
*/
if (pkg.applicationInfo.nativeLibraryDir != null) {
+ final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
try {
+ // Enable gross and lame hacks for apps that are built with old
+ // SDK tools. We must scan their APKs for renderscript bitcode and
+ // not launch them if it's present. Don't bother checking on devices
+ // that don't have 64 bit support.
+ String[] abiList = Build.SUPPORTED_ABIS;
+ boolean hasLegacyRenderscriptBitcode = false;
+ if (abiOverride != null) {
+ abiList = new String[] { abiOverride };
+ } else if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
+ NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ hasLegacyRenderscriptBitcode = true;
+ }
+
File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
final String dataPathString = dataPath.getCanonicalPath();
@@ -5411,21 +5427,26 @@ public class PackageManagerService extends IPackageManager.Stub {
Log.i(TAG, "removed obsolete native libraries for system package "
+ path);
}
-
- setInternalAppAbi(pkg, pkgSetting);
+ if (abiOverride != null || hasLegacyRenderscriptBitcode) {
+ pkg.applicationInfo.cpuAbi = abiList[0];
+ pkgSetting.cpuAbiString = abiList[0];
+ } else {
+ setInternalAppAbi(pkg, pkgSetting);
+ }
} else {
if (!isForwardLocked(pkg) && !isExternal(pkg)) {
/*
- * Update native library dir if it starts with
- * /data/data
- */
+ * Update native library dir if it starts with
+ * /data/data
+ */
if (nativeLibraryDir.getPath().startsWith(dataPathString)) {
setInternalAppNativeLibraryPath(pkg, pkgSetting);
nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
}
try {
- int copyRet = copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir);
+ int copyRet = copyNativeLibrariesForInternalApp(handle,
+ nativeLibraryDir, abiList);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
Slog.e(TAG, "Unable to copy native libraries");
mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
@@ -5435,7 +5456,9 @@ public class PackageManagerService extends IPackageManager.Stub {
// We've successfully copied native libraries across, so we make a
// note of what ABI we're using
if (copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
- pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[copyRet];
+ pkg.applicationInfo.cpuAbi = abiList[copyRet];
+ } else if (abiOverride != null || hasLegacyRenderscriptBitcode) {
+ pkg.applicationInfo.cpuAbi = abiList[0];
} else {
pkg.applicationInfo.cpuAbi = null;
}
@@ -5452,20 +5475,22 @@ public class PackageManagerService extends IPackageManager.Stub {
// to clean this up but we'll need to change the interface between this service
// and IMediaContainerService (but doing so will spread this logic out, rather
// than centralizing it).
- final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
- final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
+ final int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList);
if (abi >= 0) {
- pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[abi];
+ pkg.applicationInfo.cpuAbi = abiList[abi];
} else if (abi == PackageManager.NO_NATIVE_LIBRARIES) {
// Note that (non upgraded) system apps will not have any native
// libraries bundled in their APK, but we're guaranteed not to be
// such an app at this point.
- pkg.applicationInfo.cpuAbi = null;
+ if (abiOverride != null || hasLegacyRenderscriptBitcode) {
+ pkg.applicationInfo.cpuAbi = abiList[0];
+ } else {
+ pkg.applicationInfo.cpuAbi = null;
+ }
} else {
mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
return null;
}
- handle.close();
}
if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
@@ -5482,8 +5507,12 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
}
+
+ pkgSetting.cpuAbiString = pkg.applicationInfo.cpuAbi;
} catch (IOException ioe) {
Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
+ } finally {
+ handle.close();
}
}
pkg.mScanPath = path;
@@ -6175,8 +6204,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- private static int copyNativeLibrariesForInternalApp(File scanFile, final File nativeLibraryDir)
- throws IOException {
+ private static int copyNativeLibrariesForInternalApp(ApkHandle handle,
+ final File nativeLibraryDir, String[] abiList) throws IOException {
if (!nativeLibraryDir.isDirectory()) {
nativeLibraryDir.delete();
@@ -6198,21 +6227,16 @@ public class PackageManagerService extends IPackageManager.Stub {
* If this is an internal application or our nativeLibraryPath points to
* the app-lib directory, unpack the libraries if necessary.
*/
- final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
- try {
- int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
- if (abi >= 0) {
- int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
- nativeLibraryDir, Build.SUPPORTED_ABIS[abi]);
- if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
- return copyRet;
- }
+ int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList);
+ if (abi >= 0) {
+ int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
+ nativeLibraryDir, Build.SUPPORTED_ABIS[abi]);
+ if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
+ return copyRet;
}
-
- return abi;
- } finally {
- handle.close();
}
+
+ return abi;
}
private void killApplication(String pkgName, int appId, String reason) {
@@ -7536,7 +7560,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
p = scanPackageLI(fullPath, flags,
SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
- System.currentTimeMillis(), UserHandle.ALL);
+ System.currentTimeMillis(), UserHandle.ALL, null);
if (p != null) {
/*
* TODO this seems dangerous as the package may have
@@ -7657,6 +7681,16 @@ public class PackageManagerService extends IPackageManager.Stub {
if (observer == null && observer2 == null) {
throw new IllegalArgumentException("No install observer supplied");
}
+ installPackageWithVerificationEncryptionAndAbiOverrideEtc(packageURI, observer, observer2,
+ flags, installerPackageName, verificationParams, encryptionParams, null);
+ }
+
+ @Override
+ public void installPackageWithVerificationEncryptionAndAbiOverrideEtc(Uri packageURI,
+ IPackageInstallObserver observer, IPackageInstallObserver2 observer2,
+ int flags, String installerPackageName,
+ VerificationParams verificationParams, ContainerEncryptionParams encryptionParams,
+ String packageAbiOverride) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
null);
@@ -7696,7 +7730,8 @@ public class PackageManagerService extends IPackageManager.Stub {
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(packageURI, observer, observer2, filteredFlags,
- installerPackageName, verificationParams, encryptionParams, user);
+ installerPackageName, verificationParams, encryptionParams, user,
+ packageAbiOverride);
mHandler.sendMessage(msg);
}
@@ -8405,11 +8440,14 @@ public class PackageManagerService extends IPackageManager.Stub {
private int mRet;
private File mTempPackage;
final ContainerEncryptionParams encryptionParams;
+ final String packageAbiOverride;
+ final String packageInstructionSetOverride;
InstallParams(Uri packageURI,
IPackageInstallObserver observer, IPackageInstallObserver2 observer2,
int flags, String installerPackageName, VerificationParams verificationParams,
- ContainerEncryptionParams encryptionParams, UserHandle user) {
+ ContainerEncryptionParams encryptionParams, UserHandle user,
+ String packageAbiOverride) {
super(user);
this.mPackageURI = packageURI;
this.flags = flags;
@@ -8418,6 +8456,9 @@ public class PackageManagerService extends IPackageManager.Stub {
this.installerPackageName = installerPackageName;
this.verificationParams = verificationParams;
this.encryptionParams = encryptionParams;
+ this.packageAbiOverride = packageAbiOverride;
+ this.packageInstructionSetOverride = (packageAbiOverride == null) ?
+ packageAbiOverride : VMRuntime.getInstructionSet(packageAbiOverride);
}
@Override
@@ -8563,7 +8604,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Remote call to find out default install location
final String packageFilePath = packageFile.getAbsolutePath();
pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags,
- lowThreshold);
+ lowThreshold, packageAbiOverride);
/*
* If we have too little free space, try to free cache
@@ -8572,10 +8613,10 @@ public class PackageManagerService extends IPackageManager.Stub {
if (pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
final long size = mContainerService.calculateInstalledSize(
- packageFilePath, isForwardLocked());
+ packageFilePath, isForwardLocked(), packageAbiOverride);
if (mInstaller.freeCache(size + lowThreshold) >= 0) {
pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath,
- flags, lowThreshold);
+ flags, lowThreshold, packageAbiOverride);
}
/*
* The cache free must have deleted the file we
@@ -8995,11 +9036,12 @@ public class PackageManagerService extends IPackageManager.Stub {
final ManifestDigest manifestDigest;
final UserHandle user;
final String instructionSet;
+ final String abiOverride;
InstallArgs(Uri packageURI,
IPackageInstallObserver observer, IPackageInstallObserver2 observer2,
int flags, String installerPackageName, ManifestDigest manifestDigest,
- UserHandle user, String instructionSet) {
+ UserHandle user, String instructionSet, String abiOverride) {
this.packageURI = packageURI;
this.flags = flags;
this.observer = observer;
@@ -9008,6 +9050,7 @@ public class PackageManagerService extends IPackageManager.Stub {
this.manifestDigest = manifestDigest;
this.user = user;
this.instructionSet = instructionSet;
+ this.abiOverride = abiOverride;
}
abstract void createCopyFile();
@@ -9063,12 +9106,13 @@ public class PackageManagerService extends IPackageManager.Stub {
FileInstallArgs(InstallParams params) {
super(params.getPackageUri(), params.observer, params.observer2, params.flags,
params.installerPackageName, params.getManifestDigest(),
- params.getUser(), null /* instruction set */);
+ params.getUser(), params.packageInstructionSetOverride,
+ params.packageAbiOverride);
}
FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
String instructionSet) {
- super(null, null, null, 0, null, null, null, instructionSet);
+ super(null, null, null, 0, null, null, null, instructionSet, null);
File codeFile = new File(fullCodePath);
installDir = codeFile.getParentFile();
codeFileName = fullCodePath;
@@ -9077,7 +9121,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
FileInstallArgs(Uri packageURI, String pkgName, String dataDir, String instructionSet) {
- super(packageURI, null, null, 0, null, null, null, instructionSet);
+ super(packageURI, null, null, 0, null, null, null, instructionSet, null);
installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
String apkName = getNextCodePath(null, pkgName, ".apk");
codeFileName = new File(installDir, apkName + ".apk").getPath();
@@ -9181,14 +9225,26 @@ public class PackageManagerService extends IPackageManager.Stub {
NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);
nativeLibraryFile.delete();
}
+
+ final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codeFile);
+ String[] abiList = (abiOverride != null) ?
+ new String[] { abiOverride } : Build.SUPPORTED_ABIS;
try {
- int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile);
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
+ abiOverride == null &&
+ NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ }
+
+ int copyRet = copyNativeLibrariesForInternalApp(handle, nativeLibraryFile, abiList);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
return copyRet;
}
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ } finally {
+ handle.close();
}
return ret;
@@ -9403,14 +9459,15 @@ public class PackageManagerService extends IPackageManager.Stub {
AsecInstallArgs(InstallParams params) {
super(params.getPackageUri(), params.observer, params.observer2, params.flags,
params.installerPackageName, params.getManifestDigest(),
- params.getUser(), null /* instruction set */);
+ params.getUser(), params.packageInstructionSetOverride,
+ params.packageAbiOverride);
}
AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
String instructionSet, boolean isExternal, boolean isForwardLocked) {
super(null, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
| (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
- null, null, null, instructionSet);
+ null, null, null, instructionSet, null);
// Extract cid from fullCodePath
int eidx = fullCodePath.lastIndexOf("/");
String subStr1 = fullCodePath.substring(0, eidx);
@@ -9422,7 +9479,7 @@ public class PackageManagerService extends IPackageManager.Stub {
AsecInstallArgs(String cid, String instructionSet, boolean isForwardLocked) {
super(null, null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0)
| (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
- null, null, null, instructionSet);
+ null, null, null, instructionSet, null);
this.cid = cid;
setCachePath(PackageHelper.getSdDir(cid));
}
@@ -9431,7 +9488,7 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean isExternal, boolean isForwardLocked) {
super(packageURI, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
| (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
- null, null, null, instructionSet);
+ null, null, null, instructionSet, null);
this.cid = cid;
}
@@ -9443,7 +9500,7 @@ public class PackageManagerService extends IPackageManager.Stub {
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
- return imcs.checkExternalFreeStorage(packageURI, isFwdLocked());
+ return imcs.checkExternalFreeStorage(packageURI, isFwdLocked(), abiOverride);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -9469,7 +9526,8 @@ public class PackageManagerService extends IPackageManager.Stub {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
newCachePath = imcs.copyResourceToContainer(packageURI, cid, getEncryptKey(),
- RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked());
+ RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked(),
+ abiOverride);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -9777,7 +9835,7 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
private void installNewPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, UserHandle user,
- String installerPackageName, PackageInstalledInfo res) {
+ String installerPackageName, PackageInstalledInfo res, String abiOverride) {
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageName;
@@ -9805,7 +9863,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
- System.currentTimeMillis(), user);
+ System.currentTimeMillis(), user, abiOverride);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -9832,7 +9890,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private void replacePackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, UserHandle user,
- String installerPackageName, PackageInstalledInfo res) {
+ String installerPackageName, PackageInstalledInfo res, String abiOverride) {
PackageParser.Package oldPackage;
String pkgName = pkg.packageName;
@@ -9861,17 +9919,19 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
- user, allUsers, perUserInstalled, installerPackageName, res);
+ user, allUsers, perUserInstalled, installerPackageName, res,
+ abiOverride);
} else {
replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
- user, allUsers, perUserInstalled, installerPackageName, res);
+ user, allUsers, perUserInstalled, installerPackageName, res,
+ abiOverride);
}
}
private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
int[] allUsers, boolean[] perUserInstalled,
- String installerPackageName, PackageInstalledInfo res) {
+ String installerPackageName, PackageInstalledInfo res, String abiOverride) {
PackageParser.Package newPackage = null;
String pkgName = deletedPackage.packageName;
boolean deletedPkg = true;
@@ -9896,7 +9956,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Successfully deleted the old package. Now proceed with re-installation
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME,
- System.currentTimeMillis(), user);
+ System.currentTimeMillis(), user, abiOverride);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -9925,7 +9985,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
// Since we failed to install the new package we need to restore the old
// package that we deleted.
- if(deletedPkg) {
+ if (deletedPkg) {
if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
File restoreFile = new File(deletedPackage.mPath);
// Parse old package
@@ -9936,7 +9996,7 @@ public class PackageManagerService extends IPackageManager.Stub {
int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE
| SCAN_UPDATE_TIME;
if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode,
- origUpdateTime, null) == null) {
+ origUpdateTime, null, null) == null) {
Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade");
return;
}
@@ -9956,7 +10016,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
int[] allUsers, boolean[] perUserInstalled,
- String installerPackageName, PackageInstalledInfo res) {
+ String installerPackageName, PackageInstalledInfo res, String abiOverride) {
if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+ ", old=" + deletedPackage);
PackageParser.Package newPackage = null;
@@ -10010,7 +10070,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Successfully disabled the old package. Now proceed with re-installation
res.returnCode = mLastScanError = PackageManager.INSTALL_SUCCEEDED;
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
- newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user);
+ newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user, abiOverride);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -10044,7 +10104,7 @@ public class PackageManagerService extends IPackageManager.Stub {
removeInstalledPackageLI(newPackage, true);
}
// Add back the old system package
- scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user);
+ scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user, null);
// Restore the old system information in Settings
synchronized(mPackages) {
if (updatedSettings) {
@@ -10278,10 +10338,10 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
if (replace) {
replacePackageLI(pkg, parseFlags, scanMode, args.user,
- installerPackageName, res);
+ installerPackageName, res, args.abiOverride);
} else {
installNewPackageLI(pkg, parseFlags, scanMode | SCAN_DELETE_DATA_ON_FAILURES, args.user,
- installerPackageName, res);
+ installerPackageName, res, args.abiOverride);
}
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
@@ -10698,7 +10758,7 @@ public class PackageManagerService extends IPackageManager.Stub {
parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
}
PackageParser.Package newPkg = scanPackageLI(disabledPs.codePath,
- parseFlags, SCAN_MONITOR | SCAN_NO_PATHS, 0, null);
+ parseFlags, SCAN_MONITOR | SCAN_NO_PATHS, 0, null, null);
if (newPkg == null) {
Slog.w(TAG, "Failed to restore system package:" + newPs.name
@@ -12511,7 +12571,7 @@ public class PackageManagerService extends IPackageManager.Stub {
doGc = true;
synchronized (mInstallLock) {
final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags,
- 0, 0, null);
+ 0, 0, null, null);
// Scan the package
if (pkg != null) {
/*
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index d70c725..c78249b 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -52,25 +52,49 @@ public final class SELinuxMMAC {
private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false;
// Signature seinfo values read from policy.
- private static HashMap<Signature, Policy> sSigSeinfo =
- new HashMap<Signature, Policy>();
+ private static HashMap<Signature, Policy> sSigSeinfo = new HashMap<Signature, Policy>();
// Default seinfo read from policy.
private static String sDefaultSeinfo = null;
- // Locations of potential install policy files.
- private static final File[] INSTALL_POLICY_FILE = {
- new File(Environment.getDataDirectory(), "security/mac_permissions.xml"),
- new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
- null};
+ // Data policy override version file.
+ private static final String DATA_VERSION_FILE =
+ Environment.getDataDirectory() + "/security/current/selinux_version";
- // Location of seapp_contexts policy file.
- private static final String SEAPP_CONTEXTS_FILE = "/seapp_contexts";
+ // Base policy version file.
+ private static final String BASE_VERSION_FILE = "/selinux_version";
+
+ // Whether override security policies should be loaded.
+ private static final boolean USE_OVERRIDE_POLICY = useOverridePolicy();
+
+ // Data override mac_permissions.xml policy file.
+ private static final String DATA_MAC_PERMISSIONS =
+ Environment.getDataDirectory() + "/security/current/mac_permissions.xml";
+
+ // Base mac_permissions.xml policy file.
+ private static final String BASE_MAC_PERMISSIONS =
+ Environment.getRootDirectory() + "/etc/security/mac_permissions.xml";
+
+ // Determine which mac_permissions.xml file to use.
+ private static final String MAC_PERMISSIONS = USE_OVERRIDE_POLICY ?
+ DATA_MAC_PERMISSIONS : BASE_MAC_PERMISSIONS;
+
+ // Data override seapp_contexts policy file.
+ private static final String DATA_SEAPP_CONTEXTS =
+ Environment.getDataDirectory() + "/security/current/seapp_contexts";
+
+ // Base seapp_contexts policy file.
+ private static final String BASE_SEAPP_CONTEXTS = "/seapp_contexts";
+
+ // Determine which seapp_contexts file to use.
+ private static final String SEAPP_CONTEXTS = USE_OVERRIDE_POLICY ?
+ DATA_SEAPP_CONTEXTS : BASE_SEAPP_CONTEXTS;
// Stores the hash of the last used seapp_contexts file.
private static final String SEAPP_HASH_FILE =
Environment.getDataDirectory().toString() + "/system/seapp_hash";
+
// Signature policy stanzas
static class Policy {
private String seinfo;
@@ -112,51 +136,17 @@ public final class SELinuxMMAC {
sDefaultSeinfo = null;
}
- /**
- * Parses an MMAC install policy from a predefined list of locations.
- * @return boolean indicating whether an install policy was correctly parsed.
- */
public static boolean readInstallPolicy() {
-
- return readInstallPolicy(INSTALL_POLICY_FILE);
- }
-
- /**
- * Parses an MMAC install policy given as an argument.
- * @param policyFile object representing the path of the policy.
- * @return boolean indicating whether the install policy was correctly parsed.
- */
- public static boolean readInstallPolicy(File policyFile) {
-
- return readInstallPolicy(new File[]{policyFile,null});
- }
-
- private static boolean readInstallPolicy(File[] policyFiles) {
// Temp structures to hold the rules while we parse the xml file.
// We add all the rules together once we know there's no structural problems.
HashMap<Signature, Policy> sigSeinfo = new HashMap<Signature, Policy>();
String defaultSeinfo = null;
FileReader policyFile = null;
- int i = 0;
- while (policyFile == null && policyFiles != null && policyFiles[i] != null) {
- try {
- policyFile = new FileReader(policyFiles[i]);
- break;
- } catch (FileNotFoundException e) {
- Slog.d(TAG,"Couldn't find install policy " + policyFiles[i].getPath());
- }
- i++;
- }
-
- if (policyFile == null) {
- Slog.d(TAG, "No policy file found. All seinfo values will be null.");
- return false;
- }
-
- Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath());
-
try {
+ policyFile = new FileReader(MAC_PERMISSIONS);
+ Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS);
+
XmlPullParser parser = Xml.newPullParser();
parser.setInput(policyFile);
@@ -199,20 +189,14 @@ public final class SELinuxMMAC {
XmlUtils.skipCurrentTag(parser);
}
}
- } catch (XmlPullParserException e) {
- // An error outside of a stanza means a structural problem
- // with the xml file. So ignore it.
- Slog.w(TAG, "Got exception parsing ", e);
+ } catch (XmlPullParserException xpe) {
+ Slog.w(TAG, "Got exception parsing " + MAC_PERMISSIONS, xpe);
return false;
- } catch (IOException e) {
- Slog.w(TAG, "Got exception parsing ", e);
+ } catch (IOException ioe) {
+ Slog.w(TAG, "Got exception parsing " + MAC_PERMISSIONS, ioe);
return false;
} finally {
- try {
- policyFile.close();
- } catch (IOException e) {
- //omit
- }
+ IoUtils.closeQuietly(policyFile);
}
flushInstallPolicy();
@@ -412,7 +396,7 @@ public final class SELinuxMMAC {
// Any error with the seapp_contexts file should be fatal
byte[] currentHash = null;
try {
- currentHash = returnHash(SEAPP_CONTEXTS_FILE);
+ currentHash = returnHash(SEAPP_CONTEXTS);
} catch (IOException ioe) {
Slog.e(TAG, "Error with hashing seapp_contexts.", ioe);
return false;
@@ -434,7 +418,7 @@ public final class SELinuxMMAC {
*/
public static void setRestoreconDone() {
try {
- final byte[] currentHash = returnHash(SEAPP_CONTEXTS_FILE);
+ final byte[] currentHash = returnHash(SEAPP_CONTEXTS);
dumpHash(new File(SEAPP_HASH_FILE), currentHash);
} catch (IOException ioe) {
Slog.e(TAG, "Error with saving hash to " + SEAPP_HASH_FILE, ioe);
@@ -485,4 +469,21 @@ public final class SELinuxMMAC {
throw new RuntimeException(nsae); // impossible
}
}
+
+ private static boolean useOverridePolicy() {
+ try {
+ final String overrideVersion = IoUtils.readFileAsString(DATA_VERSION_FILE);
+ final String baseVersion = IoUtils.readFileAsString(BASE_VERSION_FILE);
+ if (overrideVersion.equals(baseVersion)) {
+ return true;
+ }
+ Slog.e(TAG, "Override policy version '" + overrideVersion + "' doesn't match " +
+ "base version '" + baseVersion + "'. Skipping override policy files.");
+ } catch (FileNotFoundException fnfe) {
+ // Override version file doesn't have to exist so silently ignore.
+ } catch (IOException ioe) {
+ Slog.w(TAG, "Skipping override policy files.", ioe);
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1bf40e0..7162683 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -107,6 +107,7 @@ public class UserManagerService extends IUserManager.Stub {
private static final String ATTR_TYPE_STRING_ARRAY = "sa";
private static final String ATTR_TYPE_STRING = "s";
private static final String ATTR_TYPE_BOOLEAN = "b";
+ private static final String ATTR_TYPE_INTEGER = "i";
private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
@@ -1532,16 +1533,18 @@ public class UserManagerService extends IUserManager.Stub {
String [] valueStrings = new String[values.size()];
values.toArray(valueStrings);
restrictions.putStringArray(key, valueStrings);
- } else if (ATTR_TYPE_BOOLEAN.equals(valType)) {
- restrictions.putBoolean(key, Boolean.parseBoolean(
- parser.nextText().trim()));
} else {
String value = parser.nextText().trim();
- restrictions.putString(key, value);
+ if (ATTR_TYPE_BOOLEAN.equals(valType)) {
+ restrictions.putBoolean(key, Boolean.parseBoolean(value));
+ } else if (ATTR_TYPE_INTEGER.equals(valType)) {
+ restrictions.putInt(key, Integer.parseInt(value));
+ } else {
+ restrictions.putString(key, value);
+ }
}
}
}
-
} catch (IOException ioe) {
} catch (XmlPullParserException pe) {
} finally {
@@ -1581,6 +1584,9 @@ public class UserManagerService extends IUserManager.Stub {
if (value instanceof Boolean) {
serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN);
serializer.text(value.toString());
+ } else if (value instanceof Integer) {
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_INTEGER);
+ serializer.text(value.toString());
} else if (value == null || value instanceof String) {
serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING);
serializer.text(value != null ? (String) value : "");
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 6c38a4c..e52f218 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -33,6 +33,7 @@ import android.database.Cursor;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -338,6 +339,41 @@ public final class TvInputManagerService extends SystemService {
channels[0].dispose();
}
}
+
+ @Override
+ public void onVideoSizeChanged(int width, int height) throws RemoteException {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onVideoSizeChanged(" + width + ", " + height + ")");
+ }
+ if (sessionState.mSession == null || sessionState.mClient == null) {
+ return;
+ }
+ try {
+ sessionState.mClient.onVideoSizeChanged(width, height, sessionState.mSeq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onSessionEvent");
+ }
+ }
+ }
+
+ @Override
+ public void onSessionEvent(String eventType, Bundle eventArgs) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
+ }
+ if (sessionState.mSession == null || sessionState.mClient == null) {
+ return;
+ }
+ try {
+ sessionState.mClient.onSessionEvent(eventType, eventArgs,
+ sessionState.mSeq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onSessionEvent");
+ }
+ }
+ }
};
// Create a session. When failed, send a null token immediately.
diff --git a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
index 3c960c7..55dd4ab 100644
--- a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
@@ -40,12 +40,20 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver {
private static final String fileContextsPath = "file_contexts";
private static final String propertyContextsPath = "property_contexts";
private static final String seappContextsPath = "seapp_contexts";
+ private static final String versionPath = "selinux_version";
+ private static final String macPermissionsPath = "mac_permissions.xml";
public SELinuxPolicyInstallReceiver() {
super("/data/security/bundle", "sepolicy_bundle", "metadata/", "version");
}
private void backupContexts(File contexts) {
+ new File(contexts, versionPath).renameTo(
+ new File(contexts, versionPath + "_backup"));
+
+ new File(contexts, macPermissionsPath).renameTo(
+ new File(contexts, macPermissionsPath + "_backup"));
+
new File(contexts, seappContextsPath).renameTo(
new File(contexts, seappContextsPath + "_backup"));
@@ -60,6 +68,8 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver {
}
private void copyUpdate(File contexts) {
+ new File(updateDir, versionPath).renameTo(new File(contexts, versionPath));
+ new File(updateDir, macPermissionsPath).renameTo(new File(contexts, macPermissionsPath));
new File(updateDir, seappContextsPath).renameTo(new File(contexts, seappContextsPath));
new File(updateDir, propertyContextsPath).renameTo(new File(contexts, propertyContextsPath));
new File(updateDir, fileContextsPath).renameTo(new File(contexts, fileContextsPath));
@@ -75,11 +85,13 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver {
}
private int[] readChunkLengths(BufferedInputStream bundle) throws IOException {
- int[] chunks = new int[4];
+ int[] chunks = new int[6];
chunks[0] = readInt(bundle);
chunks[1] = readInt(bundle);
chunks[2] = readInt(bundle);
chunks[3] = readInt(bundle);
+ chunks[4] = readInt(bundle);
+ chunks[5] = readInt(bundle);
return chunks;
}
@@ -94,10 +106,12 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver {
BufferedInputStream stream = new BufferedInputStream(new FileInputStream(updateContent));
try {
int[] chunkLengths = readChunkLengths(stream);
- installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[0]);
- installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[1]);
- installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[2]);
- installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[3]);
+ installFile(new File(updateDir, versionPath), stream, chunkLengths[0]);
+ installFile(new File(updateDir, macPermissionsPath), stream, chunkLengths[1]);
+ installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[2]);
+ installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[3]);
+ installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[4]);
+ installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[5]);
} finally {
IoUtils.closeQuietly(stream);
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 81db8b3..a354c45 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -174,14 +174,14 @@ public class TaskStack {
stackNdx = 0;
} else {
stackNdx = mTasks.size();
- final int currentUserId = mService.mCurrentUserId;
- if (task.mUserId != currentUserId) {
+ if (!mService.isCurrentProfileLocked(task.mUserId)) {
// Place the task below all current user tasks.
while (--stackNdx >= 0) {
- if (currentUserId != mTasks.get(stackNdx).mUserId) {
+ if (!mService.isCurrentProfileLocked(mTasks.get(stackNdx).mUserId)) {
break;
}
}
+ // Put it above first non-current user task.
++stackNdx;
}
}
@@ -352,7 +352,7 @@ public class TaskStack {
int top = mTasks.size();
for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
Task task = mTasks.get(taskNdx);
- if (task.mUserId == userId) {
+ if (mService.isCurrentProfileLocked(task.mUserId)) {
mTasks.remove(taskNdx);
mTasks.add(task);
--top;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 6fdd535..008d2fc 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -284,7 +284,7 @@ public class WindowAnimator {
} else if (mKeyguardGoingAway && !nowAnimating) {
// Timeout!!
Slog.e(TAG, "Timeout waiting for animation to startup");
- mPolicy.startKeyguardExitAnimation(0);
+ mPolicy.startKeyguardExitAnimation(0, 0);
mKeyguardGoingAway = false;
}
if (win.isReadyForDisplay()) {
@@ -392,7 +392,9 @@ public class WindowAnimator {
winAnimator.mAnimationIsEntrance = true;
if (startKeyguardExit) {
// Do one time only.
- mPolicy.startKeyguardExitAnimation(a.getStartOffset());
+ mPolicy.startKeyguardExitAnimation(mCurrentTime + a.getStartOffset(),
+ a.getDuration());
+ mKeyguardGoingAway = false;
startKeyguardExit = false;
}
}
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index 163225e..9a5079d 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -18,6 +18,7 @@
#include "jni.h"
#include "JNIHelp.h"
+#include "android/graphics/GraphicsJNI.h"
#include <android_view_GraphicBuffer.h>
#include <cutils/log.h>
@@ -46,7 +47,7 @@ namespace android {
// ----------------------------------------------------------------------------
static struct {
- jmethodID safeCanvasSwap;
+ jmethodID setNativeBitmap;
} gCanvasClassInfo;
#define INVOKEV(object, method, ...) \
@@ -63,9 +64,7 @@ static jlong com_android_server_AssetAtlasService_acquireCanvas(JNIEnv* env, job
bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
bitmap->allocPixels();
bitmap->eraseColor(0);
-
- SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (*bitmap));
- INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+ INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(bitmap));
return reinterpret_cast<jlong>(bitmap);
}
@@ -74,8 +73,7 @@ static void com_android_server_AssetAtlasService_releaseCanvas(JNIEnv* env, jobj
jobject canvas, jlong bitmapHandle) {
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkCanvas* nativeCanvas = SkNEW(SkCanvas);
- INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+ INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
delete bitmap;
}
@@ -244,7 +242,7 @@ int register_android_server_AssetAtlasService(JNIEnv* env) {
jclass clazz;
FIND_CLASS(clazz, "android/graphics/Canvas");
- GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V");
+ GET_METHOD_ID(gCanvasClassInfo.setNativeBitmap, clazz, "setNativeBitmap", "(J)V");
return jniRegisterNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d78fb13..a52396e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -130,6 +130,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final boolean DBG = false;
+ private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
+
final Context mContext;
final UserManager mUserManager;
final PowerManager.WakeLock mWakeLock;
@@ -190,6 +192,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// This is the list of component allowed to start lock task mode.
final List<ComponentName> mLockTaskComponents = new ArrayList<ComponentName>();
+ ComponentName mRestrictionsProvider;
+
public DevicePolicyData(int userHandle) {
mUserHandle = userHandle;
}
@@ -944,6 +948,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.startDocument(null, true);
out.startTag(null, "policies");
+ if (policy.mRestrictionsProvider != null) {
+ out.attribute(null, ATTR_PERMISSION_PROVIDER,
+ policy.mRestrictionsProvider.flattenToString());
+ }
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
@@ -1039,6 +1047,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
throw new XmlPullParserException(
"Settings do not start with policies tag: found " + tag);
}
+
+ // Extract the permission provider component name if available
+ String permissionProvider = parser.getAttributeValue(null, ATTR_PERMISSION_PROVIDER);
+ if (permissionProvider != null) {
+ policy.mRestrictionsProvider = ComponentName.unflattenFromString(permissionProvider);
+ }
+
type = parser.next();
int outerDepth = parser.getDepth();
policy.mLockTaskComponents.clear();
@@ -3303,6 +3318,32 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ @Override
+ public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+ int userHandle = UserHandle.getCallingUserId();
+ DevicePolicyData userData = getUserData(userHandle);
+ userData.mRestrictionsProvider = permissionProvider;
+ saveSettingsLocked(userHandle);
+ }
+ }
+
+ @Override
+ public ComponentName getRestrictionsProvider(int userHandle) {
+ synchronized (this) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system can query the permission provider");
+ }
+ DevicePolicyData userData = getUserData(userHandle);
+ return userData != null ? userData.mRestrictionsProvider : null;
+ }
+ }
+
public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) {
int callingUserId = UserHandle.getCallingUserId();
synchronized (this) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c0f60c7..164fe05 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -80,6 +80,7 @@ import com.android.server.pm.PackageManagerService;
import com.android.server.pm.UserManagerService;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
+import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.search.SearchManagerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
@@ -126,7 +127,7 @@ public final class SystemServer {
private static final String WIFI_SERVICE_CLASS =
"com.android.server.wifi.WifiService";
private static final String WIFI_PASSPOINT_SERVICE_CLASS =
- "com.android.server.wifi.passpoint.PasspointService";
+ "com.android.server.wifi.passpoint.WifiPasspointService";
private static final String WIFI_P2P_SERVICE_CLASS =
"com.android.server.wifi.p2p.WifiP2pService";
private static final String HDMI_CEC_SERVICE_CLASS =
@@ -643,15 +644,15 @@ public final class SystemServer {
}
try {
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+ mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
} catch (Throwable e) {
- reportWtf("starting Wi-Fi Service", e);
+ reportWtf("starting Wi-Fi PasspointService", e);
}
try {
- mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
+ mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
} catch (Throwable e) {
- reportWtf("starting Wi-Fi PasspointService", e);
+ reportWtf("starting Wi-Fi Service", e);
}
try {
@@ -941,6 +942,12 @@ public final class SystemServer {
}
try {
+ mSystemServiceManager.startService(RestrictionsManagerService.class);
+ } catch (Throwable e) {
+ reportWtf("starting RestrictionsManagerService", e);
+ }
+
+ try {
mSystemServiceManager.startService(MediaSessionService.class);
} catch (Throwable e) {
reportWtf("starting MediaSessionService", e);
@@ -991,8 +998,7 @@ public final class SystemServer {
try {
Slog.i(TAG, "LauncherAppsService");
- LauncherAppsService las = new LauncherAppsService(context);
- ServiceManager.addService(Context.LAUNCHER_APPS_SERVICE, las);
+ mSystemServiceManager.startService(LauncherAppsService.class);
} catch (Throwable t) {
reportWtf("starting LauncherAppsService", t);
}
diff --git a/services/restrictions/Android.mk b/services/restrictions/Android.mk
new file mode 100644
index 0000000..fcf8626
--- /dev/null
+++ b/services/restrictions/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := services.restrictions
+
+LOCAL_SRC_FILES += \
+ $(call all-java-files-under,java)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
new file mode 100644
index 0000000..e1f77b3
--- /dev/null
+++ b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+package com.android.server.restrictions;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.admin.IDevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IRestrictionsManager;
+import android.content.RestrictionsManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IUserManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemService;
+
+/**
+ * SystemService wrapper for the RestrictionsManager implementation. Publishes the
+ * Context.RESTRICTIONS_SERVICE.
+ */
+
+public final class RestrictionsManagerService extends SystemService {
+ private final RestrictionsManagerImpl mRestrictionsManagerImpl;
+
+ public RestrictionsManagerService(Context context) {
+ super(context);
+ mRestrictionsManagerImpl = new RestrictionsManagerImpl(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.RESTRICTIONS_SERVICE, mRestrictionsManagerImpl);
+ }
+
+ class RestrictionsManagerImpl extends IRestrictionsManager.Stub {
+ private final Context mContext;
+ private final IUserManager mUm;
+ private final IDevicePolicyManager mDpm;
+
+ public RestrictionsManagerImpl(Context context) {
+ mContext = context;
+ mUm = (IUserManager) getBinderService(Context.USER_SERVICE);
+ mDpm = (IDevicePolicyManager) getBinderService(Context.DEVICE_POLICY_SERVICE);
+ }
+
+ @Override
+ public Bundle getApplicationRestrictions(String packageName) throws RemoteException {
+ return mUm.getApplicationRestrictions(packageName);
+ }
+
+ @Override
+ public boolean hasRestrictionsProvider() throws RemoteException {
+ int userHandle = UserHandle.getCallingUserId();
+ if (mDpm != null) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mDpm.getRestrictionsProvider(userHandle) != null;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void requestPermission(String packageName, String requestTemplate,
+ Bundle requestData) throws RemoteException {
+ int callingUid = Binder.getCallingUid();
+ int userHandle = UserHandle.getUserId(callingUid);
+ if (mDpm != null) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ComponentName restrictionsProvider =
+ mDpm.getRestrictionsProvider(userHandle);
+ // Check if there is a restrictions provider
+ if (restrictionsProvider == null) {
+ throw new IllegalStateException(
+ "Cannot request permission without a restrictions provider registered");
+ }
+ // Check that the packageName matches the caller.
+ enforceCallerMatchesPackage(callingUid, packageName, "Package name does not" +
+ " match caller ");
+ // Prepare and broadcast the intent to the provider
+ Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_PERMISSION);
+ intent.setComponent(restrictionsProvider);
+ intent.putExtra(RestrictionsManager.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(RestrictionsManager.EXTRA_TEMPLATE_ID, requestTemplate);
+ intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, requestData);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ private void enforceCallerMatchesPackage(int callingUid, String packageName,
+ String message) {
+ try {
+ String[] pkgs = AppGlobals.getPackageManager().getPackagesForUid(callingUid);
+ if (pkgs != null) {
+ if (!ArrayUtils.contains(pkgs, packageName)) {
+ throw new SecurityException(message + callingUid);
+ }
+ }
+ } catch (RemoteException re) {
+ // Shouldn't happen
+ }
+ }
+
+ @Override
+ public void notifyPermissionResponse(String packageName, Bundle response)
+ throws RemoteException {
+ // Check caller
+ int callingUid = Binder.getCallingUid();
+ int userHandle = UserHandle.getUserId(callingUid);
+ if (mDpm != null) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ComponentName permProvider = mDpm.getRestrictionsProvider(userHandle);
+ if (permProvider == null) {
+ throw new SecurityException("No restrictions provider registered for user");
+ }
+ enforceCallerMatchesPackage(callingUid, permProvider.getPackageName(),
+ "Restrictions provider does not match caller ");
+
+ // Post the response to target package
+ Intent responseIntent = new Intent(
+ RestrictionsManager.ACTION_PERMISSION_RESPONSE_RECEIVED);
+ responseIntent.setPackage(packageName);
+ responseIntent.putExtra(RestrictionsManager.EXTRA_RESPONSE_BUNDLE, response);
+ mContext.sendBroadcastAsUser(responseIntent, new UserHandle(userHandle));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+ }
+}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 7848b1d..636dd4d 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -35,7 +35,8 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
-
+ <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
+
<application>
<uses-library android:name="android.test.runner" />
@@ -53,6 +54,15 @@
</intent-filter>
</service>
+ <receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin_sample" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
+
</application>
<instrumentation
diff --git a/services/tests/servicestests/res/xml/device_admin_sample.xml b/services/tests/servicestests/res/xml/device_admin_sample.xml
new file mode 100644
index 0000000..032debb
--- /dev/null
+++ b/services/tests/servicestests/res/xml/device_admin_sample.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-policies>
+ <limit-password />
+ <watch-login />
+ <reset-password />
+ <force-lock />
+ <wipe-data />
+ <expire-password />
+ <encrypted-storage />
+ <disable-camera />
+ <disable-keyguard-features />
+ </uses-policies>
+</device-admin>
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
new file mode 100644
index 0000000..8e8e4e6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Tests for application restrictions persisting via profile owner:
+ * make -j FrameworksServicesTests
+ * runtest --path frameworks/base/services/tests/servicestests/ \
+ * src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
+ */
+public class ApplicationRestrictionsTest extends AndroidTestCase {
+
+ static DevicePolicyManager sDpm;
+ static ComponentName sAdminReceiver;
+ private static final String RESTRICTED_APP = "com.example.restrictedApp";
+ static boolean sAddBack = false;
+
+ public static class AdminReceiver extends DeviceAdminReceiver {
+
+ @Override
+ public void onDisabled(Context context, Intent intent) {
+ if (sAddBack) {
+ sDpm.setActiveAdmin(sAdminReceiver, false);
+ sAddBack = false;
+ }
+
+ super.onDisabled(context, intent);
+ }
+ }
+
+ @Override
+ public void setUp() {
+ final Context context = getContext();
+ sAdminReceiver = new ComponentName(mContext.getPackageName(),
+ AdminReceiver.class.getName());
+ sDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ Settings.Secure.putInt(context.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0);
+ sDpm.setProfileOwner(context.getPackageName(), "Test", UserHandle.myUserId());
+ Settings.Secure.putInt(context.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 1);
+ // Remove the admin if already registered. It's async, so add it back
+ // when the admin gets a broadcast. Otherwise add it back right away.
+ if (sDpm.isAdminActive(sAdminReceiver)) {
+ sAddBack = true;
+ sDpm.removeActiveAdmin(sAdminReceiver);
+ } else {
+ sDpm.setActiveAdmin(sAdminReceiver, false);
+ }
+ }
+
+ @Override
+ public void tearDown() {
+ Settings.Secure.putInt(getContext().getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0);
+ sDpm.removeActiveAdmin(sAdminReceiver);
+ Settings.Secure.putInt(getContext().getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 1);
+ }
+
+ public void testSettingRestrictions() {
+ Bundle restrictions = new Bundle();
+ restrictions.putString("KEY_STRING", "Foo");
+ assertNotNull(sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP));
+ sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
+ Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
+ assertNotNull(returned);
+ assertEquals(returned.size(), 1);
+ assertEquals(returned.get("KEY_STRING"), "Foo");
+ sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle());
+ returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
+ assertEquals(returned.size(), 0);
+ }
+
+ public void testRestrictionTypes() {
+ Bundle restrictions = new Bundle();
+ restrictions.putString("KEY_STRING", "Foo");
+ restrictions.putInt("KEY_INT", 7);
+ restrictions.putBoolean("KEY_BOOLEAN", true);
+ restrictions.putBoolean("KEY_BOOLEAN_2", false);
+ restrictions.putString("KEY_STRING_2", "Bar");
+ restrictions.putStringArray("KEY_STR_ARRAY", new String[] { "Foo", "Bar" });
+ sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
+ Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
+ assertTrue(returned.getBoolean("KEY_BOOLEAN"));
+ assertFalse(returned.getBoolean("KEY_BOOLEAN_2"));
+ assertFalse(returned.getBoolean("KEY_BOOLEAN_3"));
+ assertEquals(returned.getInt("KEY_INT"), 7);
+ assertTrue(returned.get("KEY_BOOLEAN") instanceof Boolean);
+ assertTrue(returned.get("KEY_INT") instanceof Integer);
+ assertEquals(returned.get("KEY_STRING"), "Foo");
+ assertEquals(returned.get("KEY_STRING_2"), "Bar");
+ assertTrue(returned.getStringArray("KEY_STR_ARRAY") instanceof String[]);
+ sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle());
+ }
+
+ public void testTextEscaping() {
+ String fancyText = "<This contains XML/> <JSON> "
+ + "{ \"One\": { \"OneOne\": \"11\", \"OneTwo\": \"12\" }, \"Two\": \"2\" } <JSON/>";
+ Bundle restrictions = new Bundle();
+ restrictions.putString("KEY_FANCY_TEXT", fancyText);
+ sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
+ Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
+ assertEquals(returned.getString("KEY_FANCY_TEXT"), fancyText);
+ }
+} \ No newline at end of file
diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java
index 51f10c1..d452172 100644
--- a/telecomm/java/android/telecomm/CallService.java
+++ b/telecomm/java/android/telecomm/CallService.java
@@ -27,6 +27,8 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.telecomm.ICallService;
import com.android.internal.telecomm.ICallServiceAdapter;
+import java.util.List;
+
/**
* Base implementation of CallService which can be used to provide calls for the system
* in-call UI. CallService is a one-way service from the framework's CallsManager to any app
@@ -59,6 +61,8 @@ public abstract class CallService extends Service {
private static final int MSG_ON_AUDIO_STATE_CHANGED = 11;
private static final int MSG_PLAY_DTMF_TONE = 12;
private static final int MSG_STOP_DTMF_TONE = 13;
+ private static final int MSG_ADD_TO_CONFERENCE = 14;
+ private static final int MSG_SPLIT_FROM_CONFERENCE = 15;
/**
* Default Handler used to consolidate binder method calls onto a single thread.
@@ -123,6 +127,29 @@ public abstract class CallService extends Service {
case MSG_STOP_DTMF_TONE:
stopDtmfTone((String) msg.obj);
break;
+ case MSG_ADD_TO_CONFERENCE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ @SuppressWarnings("unchecked")
+ List<String> callIds = (List<String>) args.arg2;
+ String conferenceCallId = (String) args.arg1;
+ addToConference(conferenceCallId, callIds);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SPLIT_FROM_CONFERENCE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String conferenceCallId = (String) args.arg1;
+ String callId = (String) args.arg2;
+ splitFromConference(conferenceCallId, callId);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
default:
break;
}
@@ -204,6 +231,22 @@ public abstract class CallService extends Service {
args.arg2 = audioState;
mMessageHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, args).sendToTarget();
}
+
+ @Override
+ public void addToConference(String conferenceCallId, List<String> callsToConference) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = conferenceCallId;
+ args.arg2 = callsToConference;
+ mMessageHandler.obtainMessage(MSG_ADD_TO_CONFERENCE, args).sendToTarget();
+ }
+
+ @Override
+ public void splitFromConference(String conferenceCallId, String callId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = conferenceCallId;
+ args.arg2 = callId;
+ mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, args).sendToTarget();
+ }
}
/**
@@ -359,4 +402,24 @@ public abstract class CallService extends Service {
* @param audioState The new {@link CallAudioState}.
*/
public abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState);
+
+ /**
+ * Adds the specified calls to the specified conference call.
+ *
+ * @param conferenceCallId The unique ID of the conference call onto which the specified calls
+ * should be added.
+ * @param callIds The calls to add to the conference call.
+ * @hide
+ */
+ public abstract void addToConference(String conferenceCallId, List<String> callIds);
+
+ /**
+ * Removes the specified call from the specified conference call. This is a no-op if the call
+ * is not already part of the conference call.
+ *
+ * @param conferenceCallId The conference call.
+ * @param callId The call to remove from the conference call
+ * @hide
+ */
+ public abstract void splitFromConference(String conferenceCallId, String callId);
}
diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java
index d5bb989..7396808 100644
--- a/telecomm/java/android/telecomm/CallServiceAdapter.java
+++ b/telecomm/java/android/telecomm/CallServiceAdapter.java
@@ -20,6 +20,8 @@ import android.os.RemoteException;
import com.android.internal.telecomm.ICallServiceAdapter;
+import java.util.List;
+
/**
* Provides methods for ICallService implementations to interact with the system phone app.
* TODO(santoscordon): Need final public-facing comments in this file.
@@ -156,5 +158,59 @@ public final class CallServiceAdapter {
}
}
+ /**
+ * Asks Telecomm to start or stop a ringback tone for a call.
+ *
+ * @param callId The unique ID of the call whose ringback is being changed.
+ * @param ringback Whether Telecomm should start playing a ringback tone.
+ */
+ public void setRequestingRingback(String callId, boolean ringback) {
+ try {
+ mAdapter.setRequestingRingback(callId, ringback);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Indicates that the specified call can conference with any of the specified list of calls.
+ *
+ * @param callId The unique ID of the call.
+ * @param conferenceCapableCallIds The unique IDs of the calls which can be conferenced.
+ * @hide
+ */
+ public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) {
+ try {
+ mAdapter.setCanConferenceWith(callId, conferenceCapableCallIds);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Indicates whether or not the specified call is currently conferenced into the specified
+ * conference call.
+ *
+ * @param conferenceCallId The unique ID of the conference call.
+ * @param callId The unique ID of the call being conferenced.
+ * @hide
+ */
+ public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) {
+ try {
+ mAdapter.setIsConferenced(conferenceCallId, callId, isConferenced);
+ } catch (RemoteException ignored) {
+ }
+ }
+ /**
+ * Indicates that the call no longer exists. Can be used with either a call or a conference
+ * call.
+ *
+ * @param callId The unique ID of the call.
+ * @hide
+ */
+ public void removeCall(String callId) {
+ try {
+ mAdapter.removeCall(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
}
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index 88de17a..8cce8e6 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -33,6 +33,7 @@ public abstract class Connection {
void onHandleChanged(Connection c, Uri newHandle);
void onSignalChanged(Connection c, Bundle details);
void onDisconnected(Connection c, int cause, String message);
+ void onRequestingRingback(Connection c, boolean ringback);
void onDestroyed(Connection c);
}
@@ -60,6 +61,10 @@ public abstract class Connection {
/** {@inheritDoc} */
@Override
public void onDestroyed(Connection c) {}
+
+ /** {@inheritDoc} */
+ @Override
+ public void onRequestingRingback(Connection c, boolean ringback) {}
}
public final class State {
@@ -77,6 +82,7 @@ public abstract class Connection {
private int mState = State.NEW;
private CallAudioState mCallAudioState;
private Uri mHandle;
+ private boolean mRequestingRingback = false;
/**
* Create a new Connection.
@@ -268,6 +274,14 @@ public abstract class Connection {
}
/**
+ * @return Whether this connection is requesting that the system play a ringback tone
+ * on its behalf.
+ */
+ public boolean isRequestingRingback() {
+ return mRequestingRingback;
+ }
+
+ /**
* Sets the value of the {@link #getHandle()} property and notifies listeners.
*
* @param handle The new handle.
@@ -286,6 +300,7 @@ public abstract class Connection {
* communicate).
*/
protected void setActive() {
+ setRequestingRingback(false);
setState(State.ACTIVE);
}
@@ -329,6 +344,21 @@ public abstract class Connection {
}
/**
+ * Requests that the framework play a ringback tone. This is to be invoked by implementations
+ * that do not play a ringback tone themselves in the call's audio stream.
+ *
+ * @param ringback Whether the ringback tone is to be played.
+ */
+ protected void setRequestingRingback(boolean ringback) {
+ if (mRequestingRingback != ringback) {
+ mRequestingRingback = ringback;
+ for (Listener l : mListeners) {
+ l.onRequestingRingback(this, ringback);
+ }
+ }
+ }
+
+ /**
* Notifies this Connection and listeners that the {@link #getCallAudioState()} property
* has a new value.
*
@@ -336,7 +366,7 @@ public abstract class Connection {
*/
protected void onSetAudioState(CallAudioState state) {
// TODO: Enforce super called
- this.mCallAudioState = state;
+ mCallAudioState = state;
for (Listener l : mListeners) {
l.onAudioStateChanged(this, state);
}
@@ -356,6 +386,21 @@ public abstract class Connection {
}
/**
+ * Notifies this Connection of an internal state change. This method is called before the
+ * state is actually changed. Overriding implementations must call
+ * {@code super.onSetState(state)}.
+ *
+ * @param state The new state, a {@link Connection.State} member.
+ */
+ protected void onSetState(int state) {
+ // TODO: Enforce super called
+ this.mState = state;
+ for (Listener l : mListeners) {
+ l.onStateChanged(this, state);
+ }
+ }
+
+ /**
* Notifies this Connection of a request to play a DTMF tone.
*
* @param c A DTMF character.
@@ -401,9 +446,6 @@ public abstract class Connection {
private void setState(int state) {
Log.d(this, "setState: %s", stateToString(state));
- this.mState = state;
- for (Listener l : mListeners) {
- l.onStateChanged(this, state);
- }
+ onSetState(state);
}
}
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 492b08e..aeb1c33 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -20,6 +20,8 @@ import android.net.Uri;
import android.os.Bundle;
import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
/**
@@ -89,6 +91,13 @@ public abstract class ConnectionService extends CallService {
public void onDestroyed(Connection c) {
removeConnection(c);
}
+
+ @Override
+ public void onRequestingRingback(Connection c, boolean ringback) {
+ String id = mIdByConnection.get(c);
+ Log.d(this, "Adapter onRingback %b", ringback);
+ getAdapter().setRequestingRingback(id, ringback);
+ }
};
@Override
@@ -241,6 +250,39 @@ public abstract class ConnectionService extends CallService {
findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState);
}
+ /** @hide */
+ @Override
+ public final void addToConference(String conferenceCallId, List<String> callIds) {
+ Log.d(this, "addToConference %s, %s", conferenceCallId, callIds);
+
+ List<Connection> connections = new LinkedList<>();
+ for (String id : callIds) {
+ Connection connection = findConnectionForAction(id, "addToConference");
+ if (connection == NULL_CONNECTION) {
+ Log.w(this, "Connection missing in conference request %s.", id);
+ return;
+ }
+ connections.add(connection);
+ }
+
+ // TODO(santoscordon): Find an existing conference call or create a new one. Then call
+ // conferenceWith on it.
+ }
+
+ /** @hide */
+ @Override
+ public final void splitFromConference(String conferenceCallId, String callId) {
+ Log.d(this, "splitFromConference(%s, %s)", conferenceCallId, callId);
+
+ Connection connection = findConnectionForAction(callId, "splitFromConference");
+ if (connection == NULL_CONNECTION) {
+ Log.w(this, "Connection missing in conference request %s.", callId);
+ return;
+ }
+
+ // TODO(santoscordon): Find existing conference call and invoke split(connection).
+ }
+
/**
* Find a set of Subscriptions matching a given handle (e.g. phone number).
*
@@ -335,4 +377,4 @@ public abstract class ConnectionService extends CallService {
Log.w(this, "%s - Cannot find Connection %s", action, callId);
return NULL_CONNECTION;
}
-} \ No newline at end of file
+}
diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java
index e41d3f6..6838ede 100644
--- a/telecomm/java/android/telecomm/InCallAdapter.java
+++ b/telecomm/java/android/telecomm/InCallAdapter.java
@@ -196,4 +196,32 @@ public final class InCallAdapter {
} catch (RemoteException e) {
}
}
+
+ /**
+ * Instructs Telecomm to conference the specified calls together.
+ *
+ * @param callId The unique ID of the call.
+ * @param callIdToConference The unique ID of the call to conference with.
+ * @hide
+ */
+ void conferenceWith(String callId, String callIdToConference) {
+ try {
+ mAdapter.conferenceWith(callId, callIdToConference);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs Telecomm to split the specified call from any conference call with which it may be
+ * connected.
+ *
+ * @param callId The unique ID of the call.
+ * @hide
+ */
+ void splitFromConference(String callId) {
+ try {
+ mAdapter.splitFromConference(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
}
diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java
index c3b2ae7..346d207 100644
--- a/telecomm/java/android/telecomm/InCallCall.java
+++ b/telecomm/java/android/telecomm/InCallCall.java
@@ -17,13 +17,13 @@
package android.telecomm;
import android.net.Uri;
-import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.DisconnectCause;
-import java.util.Date;
-import java.util.UUID;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Information about a call that is used between InCallService and Telecomm.
@@ -38,8 +38,12 @@ public final class InCallCall implements Parcelable {
private final GatewayInfo mGatewayInfo;
private final CallServiceDescriptor mCurrentCallServiceDescriptor;
private final CallServiceDescriptor mHandoffCallServiceDescriptor;
+ private final List<String> mConferenceCapableCallIds;
+ private final String mParentCallId;
+ private final List<String> mChildCallIds;
/** @hide */
+ @SuppressWarnings("unchecked")
public InCallCall(
String id,
CallState state,
@@ -50,6 +54,25 @@ public final class InCallCall implements Parcelable {
GatewayInfo gatewayInfo,
CallServiceDescriptor descriptor,
CallServiceDescriptor handoffDescriptor) {
+ this(id, state, disconnectCause, capabilities, connectTimeMillis, handle, gatewayInfo,
+ descriptor, handoffDescriptor, Collections.EMPTY_LIST, null,
+ Collections.EMPTY_LIST);
+ }
+
+ /** @hide */
+ public InCallCall(
+ String id,
+ CallState state,
+ int disconnectCause,
+ int capabilities,
+ long connectTimeMillis,
+ Uri handle,
+ GatewayInfo gatewayInfo,
+ CallServiceDescriptor descriptor,
+ CallServiceDescriptor handoffDescriptor,
+ List<String> conferenceCapableCallIds,
+ String parentCallId,
+ List<String> childCallIds) {
mId = id;
mState = state;
mDisconnectCause = disconnectCause;
@@ -59,6 +82,9 @@ public final class InCallCall implements Parcelable {
mGatewayInfo = gatewayInfo;
mCurrentCallServiceDescriptor = descriptor;
mHandoffCallServiceDescriptor = handoffDescriptor;
+ mConferenceCapableCallIds = conferenceCapableCallIds;
+ mParentCallId = parentCallId;
+ mChildCallIds = childCallIds;
}
/** The unique ID of the call. */
@@ -112,6 +138,31 @@ public final class InCallCall implements Parcelable {
return mHandoffCallServiceDescriptor;
}
+ /**
+ * The calls with which this call can conference.
+ * @hide
+ */
+ public List<String> getConferenceCapableCallIds() {
+ return mConferenceCapableCallIds;
+ }
+
+ /**
+ * The conference call to which this call is conferenced. Null if not conferenced.
+ * @hide
+ */
+ public String getParentCallId() {
+ return mParentCallId;
+ }
+
+ /**
+ * The child call-IDs if this call is a conference call. Returns an empty list if this is not
+ * a conference call or if the conference call contains no children.
+ * @hide
+ */
+ public List<String> getChildCallIds() {
+ return mChildCallIds;
+ }
+
/** Responsible for creating InCallCall objects for deserialized Parcels. */
public static final Parcelable.Creator<InCallCall> CREATOR =
new Parcelable.Creator<InCallCall> () {
@@ -127,8 +178,14 @@ public final class InCallCall implements Parcelable {
GatewayInfo gatewayInfo = source.readParcelable(classLoader);
CallServiceDescriptor descriptor = source.readParcelable(classLoader);
CallServiceDescriptor handoffDescriptor = source.readParcelable(classLoader);
+ List<String> conferenceCapableCallIds = new ArrayList<>();
+ source.readList(conferenceCapableCallIds, classLoader);
+ String parentCallId = source.readString();
+ List<String> childCallIds = new ArrayList<>();
+ source.readList(childCallIds, classLoader);
return new InCallCall(id, state, disconnectCause, capabilities, connectTimeMillis,
- handle, gatewayInfo, descriptor, handoffDescriptor);
+ handle, gatewayInfo, descriptor, handoffDescriptor, conferenceCapableCallIds,
+ parentCallId, childCallIds);
}
@Override
@@ -155,5 +212,8 @@ public final class InCallCall implements Parcelable {
destination.writeParcelable(mGatewayInfo, 0);
destination.writeParcelable(mCurrentCallServiceDescriptor, 0);
destination.writeParcelable(mHandoffCallServiceDescriptor, 0);
+ destination.writeList(mConferenceCapableCallIds);
+ destination.writeString(mParentCallId);
+ destination.writeList(mChildCallIds);
}
}
diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java
index 63b2020..3a63077 100644
--- a/telecomm/java/android/telecomm/InCallService.java
+++ b/telecomm/java/android/telecomm/InCallService.java
@@ -42,6 +42,7 @@ public abstract class InCallService extends Service {
private static final int MSG_SET_POST_DIAL = 4;
private static final int MSG_SET_POST_DIAL_WAIT = 5;
private static final int MSG_ON_AUDIO_STATE_CHANGED = 6;
+ private static final int MSG_BRING_TO_FOREGROUND = 7;
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -83,6 +84,9 @@ public abstract class InCallService extends Service {
case MSG_ON_AUDIO_STATE_CHANGED:
onAudioStateChanged((CallAudioState) msg.obj);
break;
+ case MSG_BRING_TO_FOREGROUND:
+ bringToForeground(msg.arg1 == 1);
+ break;
default:
break;
}
@@ -130,6 +134,12 @@ public abstract class InCallService extends Service {
public void onAudioStateChanged(CallAudioState audioState) {
mHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, audioState).sendToTarget();
}
+
+ /** {@inheritDoc} */
+ @Override
+ public void bringToForeground(boolean showDialpad) {
+ mHandler.obtainMessage(MSG_BRING_TO_FOREGROUND, showDialpad ? 1 : 0, 0).sendToTarget();
+ }
}
private final InCallServiceBinder mBinder;
@@ -206,4 +216,11 @@ public abstract class InCallService extends Service {
* @param audioState The new {@link CallAudioState}.
*/
protected abstract void onAudioStateChanged(CallAudioState audioState);
+
+ /**
+ * Brings the in-call screen to the foreground.
+ *
+ * @param showDialpad If true, put up the dialpad when the screen is shown.
+ */
+ protected abstract void bringToForeground(boolean showDialpad);
}
diff --git a/telecomm/java/com/android/internal/telecomm/ICallService.aidl b/telecomm/java/com/android/internal/telecomm/ICallService.aidl
index cc0641c..771a3ae 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallService.aidl
@@ -55,4 +55,8 @@ oneway interface ICallService {
void playDtmfTone(String callId, char digit);
void stopDtmfTone(String callId);
+
+ void addToConference(String conferenceCallId, in List<String> callIds);
+
+ void splitFromConference(String conferenceCallId, String callId);
}
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
index dfdaa75..a92b176 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
@@ -43,4 +43,12 @@ oneway interface ICallServiceAdapter {
void setDisconnected(String callId, int disconnectCause, String disconnectMessage);
void setOnHold(String callId);
+
+ void setRequestingRingback(String callId, boolean ringing);
+
+ void setCanConferenceWith(String callId, in List<String> conferenceCapableCallIds);
+
+ void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced);
+
+ void removeCall(String callId);
}
diff --git a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
index 512e898..6a27217 100644
--- a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
@@ -47,4 +47,8 @@ oneway interface IInCallAdapter {
void postDialContinue(String callId);
void handoffCall(String callId);
+
+ void conferenceWith(String callId, String callIdToConference);
+
+ void splitFromConference(String callId);
}
diff --git a/telecomm/java/com/android/internal/telecomm/IInCallService.aidl b/telecomm/java/com/android/internal/telecomm/IInCallService.aidl
index ccf7e3f..1635053 100644
--- a/telecomm/java/com/android/internal/telecomm/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IInCallService.aidl
@@ -40,4 +40,6 @@ oneway interface IInCallService {
void setPostDialWait(String callId, String remaining);
void onAudioStateChanged(in CallAudioState audioState);
+
+ void bringToForeground(boolean showDialpad);
}
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index c439211..0e94ffb 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -21,7 +21,7 @@ package com.android.internal.telecomm;
* commands that were previously handled by ITelephony.
* {@hide}
*/
-oneway interface ITelecommService {
+interface ITelecommService {
/**
* Silence the ringer if an incoming call is currently ringing.
@@ -31,4 +31,11 @@ oneway interface ITelecommService {
* even if there's no incoming call. (If so, this method will do nothing.)
*/
void silenceRinger();
+
+ /**
+ * Brings the in-call screen to the foreground if there is an active call.
+ *
+ * @param showDialpad if true, make the dialpad visible initially.
+ */
+ void showCallScreen(boolean showDialpad);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2483419..ffa9a4e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1988,9 +1988,10 @@ public class TelephonyManager {
@PrivateApi
public boolean showCallScreen() {
try {
- return getITelephony().showCallScreen();
+ getTelecommService().showCallScreen(false);
+ return true;
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#showCallScreen", e);
+ Log.e(TAG, "Error calling ITelecommService#showCallScreen", e);
}
return false;
}
@@ -1999,9 +2000,10 @@ public class TelephonyManager {
@PrivateApi
public boolean showCallScreenWithDialpad(boolean showDialpad) {
try {
- return getITelephony().showCallScreenWithDialpad(showDialpad);
+ getTelecommService().showCallScreen(showDialpad);
+ return true;
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#showCallScreenWithDialpad", e);
+ Log.e(TAG, "Error calling ITelecommService#showCallScreen(" + showDialpad + ")", e);
}
return false;
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 6d7f158..acaa8de 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -49,28 +49,6 @@ interface ITelephony {
void call(String callingPackage, String number);
/**
- * If there is currently a call in progress, show the call screen.
- * The DTMF dialpad may or may not be visible initially, depending on
- * whether it was up when the user last exited the InCallScreen.
- *
- * @return true if the call screen was shown.
- */
- boolean showCallScreen();
-
- /**
- * Variation of showCallScreen() that also specifies whether the
- * DTMF dialpad should be initially visible when the InCallScreen
- * comes up.
- *
- * @param showDialpad if true, make the dialpad visible initially,
- * otherwise hide the dialpad initially.
- * @return true if the call screen was shown.
- *
- * @see showCallScreen
- */
- boolean showCallScreenWithDialpad(boolean showDialpad);
-
- /**
* End call if there is a call in progress, otherwise does nothing.
*
* @return whether it hung up
diff --git a/test-runner/src/android/test/MoreAsserts.java b/test-runner/src/android/test/MoreAsserts.java
index fb0faba..3364895 100644
--- a/test-runner/src/android/test/MoreAsserts.java
+++ b/test-runner/src/android/test/MoreAsserts.java
@@ -128,6 +128,33 @@ public final class MoreAsserts {
}
/**
+ * @hide Asserts that array {@code actual} is the same size and every element equals
+ * those in array {@code expected}. On failure, message indicates first
+ * specific element mismatch.
+ */
+ public static void assertEquals(
+ String message, long[] expected, long[] actual) {
+ if (expected.length != actual.length) {
+ failWrongLength(message, expected.length, actual.length);
+ }
+ for (int i = 0; i < expected.length; i++) {
+ if (expected[i] != actual[i]) {
+ failWrongElement(message, i, expected[i], actual[i]);
+ }
+ }
+ }
+
+ /**
+ * @hide Asserts that array {@code actual} is the same size and every element equals
+ * those in array {@code expected}. On failure, message indicates first
+ * specific element mismatch.
+ */
+ public static void assertEquals(long[] expected, long[] actual) {
+ assertEquals(null, expected, actual);
+ }
+
+
+ /**
* Asserts that array {@code actual} is the same size and every element equals
* those in array {@code expected}. On failure, message indicates first
* specific element mismatch.
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index c162bf2..a54936b 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -608,4 +608,9 @@ public class MockContext extends Context {
public File[] getExternalCacheDirs() {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public File[] getExternalMediaDirs() {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
index 5b0aa66..a81e063 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
@@ -118,19 +118,25 @@ public class CirclePropActivity extends Activity {
mRadius, mToggle ? 250.0f : 150.0f));
mRunningAnimations.add(new RenderNodeAnimator(
- mPaint, RenderNodeAnimator.PAINT_ALPHA,
- mToggle ? 64.0f : 255.0f));
-
- mRunningAnimations.add(new RenderNodeAnimator(
mPaint, RenderNodeAnimator.PAINT_STROKE_WIDTH,
mToggle ? 5.0f : 60.0f));
- TimeInterpolator interp = new OvershootInterpolator(3.0f);
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mPaint, RenderNodeAnimator.PAINT_ALPHA, 64.0f));
+
+ // Will be "chained" to run after the above
+ mRunningAnimations.add(new RenderNodeAnimator(
+ mPaint, RenderNodeAnimator.PAINT_ALPHA, 255.0f));
+
for (int i = 0; i < mRunningAnimations.size(); i++) {
RenderNodeAnimator anim = mRunningAnimations.get(i);
- anim.setInterpolator(interp);
anim.setDuration(1000);
anim.setTarget(this);
+ if (i == (mRunningAnimations.size() - 1)) {
+ // "chain" test
+ anim.setStartValue(64.0f);
+ anim.setStartDelay(anim.getDuration());
+ }
anim.start();
}
diff --git a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
index 158f5e4..ee407ad 100644
--- a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
+++ b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
@@ -99,10 +99,10 @@ public class OnePlayerActivity extends Activity {
switch (v.getId()) {
case R.id.play_button:
Log.d(TAG, "Play button pressed, in state " + mPlaybackState);
- if (mPlaybackState == PlaybackState.PLAYSTATE_PAUSED
- || mPlaybackState == PlaybackState.PLAYSTATE_STOPPED) {
+ if (mPlaybackState == PlaybackState.STATE_PAUSED
+ || mPlaybackState == PlaybackState.STATE_STOPPED) {
mPlayer.play();
- } else if (mPlaybackState == PlaybackState.PLAYSTATE_PLAYING) {
+ } else if (mPlaybackState == PlaybackState.STATE_PLAYING) {
mPlayer.pause();
}
break;
@@ -126,31 +126,31 @@ public class OnePlayerActivity extends Activity {
boolean enableControls = true;
StringBuilder statusBuilder = new StringBuilder();
switch (mPlaybackState) {
- case PlaybackState.PLAYSTATE_PLAYING:
+ case PlaybackState.STATE_PLAYING:
statusBuilder.append("playing");
mPlayButton.setText("Pause");
enablePlay = true;
break;
- case PlaybackState.PLAYSTATE_PAUSED:
+ case PlaybackState.STATE_PAUSED:
statusBuilder.append("paused");
mPlayButton.setText("Play");
enablePlay = true;
break;
- case PlaybackState.PLAYSTATE_STOPPED:
+ case PlaybackState.STATE_STOPPED:
statusBuilder.append("ended");
mPlayButton.setText("Play");
enablePlay = true;
break;
- case PlaybackState.PLAYSTATE_ERROR:
+ case PlaybackState.STATE_ERROR:
statusBuilder.append("error: ").append(state.getErrorMessage());
break;
- case PlaybackState.PLAYSTATE_BUFFERING:
+ case PlaybackState.STATE_BUFFERING:
statusBuilder.append("buffering");
break;
- case PlaybackState.PLAYSTATE_NONE:
+ case PlaybackState.STATE_NONE:
statusBuilder.append("none");
break;
- case PlaybackState.PLAYSTATE_CONNECTING:
+ case PlaybackState.STATE_CONNECTING:
statusBuilder.append("connecting");
enableControls = false;
break;
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerController.java b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
index 9f7bb26..145b389 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerController.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
@@ -21,7 +21,6 @@ import android.media.session.MediaController;
import android.media.session.RouteInfo;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
-import android.media.session.TransportController;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -42,12 +41,11 @@ public class PlayerController {
protected MediaController mController;
protected IPlayerService mBinder;
- protected TransportController mTransportControls;
+ protected MediaController.TransportControls mTransportControls;
private final Intent mServiceIntent;
private Context mContext;
private Listener mListener;
- private TransportListener mTransportListener = new TransportListener();
private SessionCallback mControllerCb;
private MediaSessionManager mManager;
private Handler mHandler = new Handler();
@@ -161,16 +159,13 @@ public class PlayerController {
return;
}
mController.addCallback(mControllerCb, mHandler);
- mTransportControls = mController.getTransportController();
- if (mTransportControls != null) {
- mTransportControls.addStateListener(mTransportListener);
- }
+ mTransportControls = mController.getTransportControls();
Log.d(TAG, "Ready to use PlayerService");
if (mListener != null) {
mListener.onConnectionStateChange(STATE_CONNECTED);
if (mTransportControls != null) {
- mListener.onPlaybackStateChange(mTransportControls.getPlaybackState());
+ mListener.onPlaybackStateChange(mController.getPlaybackState());
}
}
}
@@ -181,9 +176,7 @@ public class PlayerController {
public void onRouteChanged(RouteInfo route) {
// TODO
}
- }
- private class TransportListener extends TransportController.TransportStateListener {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
if (state == null) {
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerService.java b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
index 0ad6dd1..934f4ef 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerService.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
@@ -103,11 +103,11 @@ public class PlayerService extends Service {
@Override
public void onPlayStateChanged(PlaybackState state) {
switch (state.getState()) {
- case PlaybackState.PLAYSTATE_PLAYING:
+ case PlaybackState.STATE_PLAYING:
onPlaybackStarted();
break;
- case PlaybackState.PLAYSTATE_STOPPED:
- case PlaybackState.PLAYSTATE_ERROR:
+ case PlaybackState.STATE_STOPPED:
+ case PlaybackState.STATE_ERROR:
onPlaybackEnded();
break;
}
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index 94d0851..d6f8118 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -25,7 +25,6 @@ import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.MediaSessionToken;
import android.media.session.PlaybackState;
-import android.media.session.TransportPerformer;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
@@ -45,7 +44,6 @@ public class PlayerSession {
protected Renderer mRenderer;
protected MediaSession.Callback mCallback;
protected Renderer.Listener mRenderListener;
- protected TransportPerformer mPerformer;
protected PlaybackState mPlaybackState;
protected Listener mListener;
@@ -84,9 +82,8 @@ public class PlayerSession {
Log.d(TAG, "Creating session for package " + mContext.getBasePackageName());
mSession = man.createSession("OneMedia");
mSession.addCallback(mCallback);
- mPerformer = mSession.getTransportPerformer();
- mPerformer.addListener(new TransportListener());
- mPerformer.setPlaybackState(mPlaybackState);
+ mSession.addTransportControlsCallback(new TransportCallback());
+ mSession.setPlaybackState(mPlaybackState);
mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
mSession.setRouteOptions(mRouteOptions);
mSession.setActive(true);
@@ -120,10 +117,10 @@ public class PlayerSession {
}
private void updateState(int newState) {
- float rate = newState == PlaybackState.PLAYSTATE_PLAYING ? 1 : 0;
+ float rate = newState == PlaybackState.STATE_PLAYING ? 1 : 0;
long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
mPlaybackState.setState(newState, position, rate);
- mPerformer.setPlaybackState(mPlaybackState);
+ mSession.setPlaybackState(mPlaybackState);
}
public interface Listener {
@@ -135,11 +132,11 @@ public class PlayerSession {
@Override
public void onError(int type, int extra, Bundle extras, Throwable error) {
Log.d(TAG, "Sending onError with type " + type + " and extra " + extra);
- mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, -1, 0);
+ mPlaybackState.setState(PlaybackState.STATE_ERROR, -1, 0);
if (error != null) {
mPlaybackState.setErrorMessage(error.getLocalizedMessage());
}
- mPerformer.setPlaybackState(mPlaybackState);
+ mSession.setPlaybackState(mPlaybackState);
if (mListener != null) {
mListener.onPlayStateChanged(mPlaybackState);
}
@@ -157,27 +154,27 @@ public class PlayerSession {
switch (newState) {
case Renderer.STATE_ENDED:
case Renderer.STATE_STOPPED:
- mPlaybackState.setState(PlaybackState.PLAYSTATE_STOPPED, position, 0);
+ mPlaybackState.setState(PlaybackState.STATE_STOPPED, position, 0);
break;
case Renderer.STATE_INIT:
case Renderer.STATE_PREPARING:
- mPlaybackState.setState(PlaybackState.PLAYSTATE_BUFFERING, position, 0);
+ mPlaybackState.setState(PlaybackState.STATE_BUFFERING, position, 0);
break;
case Renderer.STATE_ERROR:
- mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, position, 0);
+ mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
break;
case Renderer.STATE_PAUSED:
- mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED, position, 0);
+ mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0);
break;
case Renderer.STATE_PLAYING:
- mPlaybackState.setState(PlaybackState.PLAYSTATE_PLAYING, position, 1);
+ mPlaybackState.setState(PlaybackState.STATE_PLAYING, position, 1);
break;
default:
- mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, position, 0);
+ mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
mPlaybackState.setErrorMessage("unkown state");
break;
}
- mPerformer.setPlaybackState(mPlaybackState);
+ mSession.setPlaybackState(mPlaybackState);
if (mListener != null) {
mListener.onPlayStateChanged(mPlaybackState);
}
@@ -191,8 +188,8 @@ public class PlayerSession {
public void onFocusLost() {
Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
- mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED, position, 0);
- mPerformer.setPlaybackState(mPlaybackState);
+ mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0);
+ mSession.setPlaybackState(mPlaybackState);
if (mListener != null) {
mListener.onPlayStateChanged(mPlaybackState);
}
@@ -206,7 +203,7 @@ public class PlayerSession {
private class SessionCb extends MediaSession.Callback {
@Override
- public void onMediaButton(Intent mediaRequestIntent) {
+ public void onMediaButtonEvent(Intent mediaRequestIntent) {
if (Intent.ACTION_MEDIA_BUTTON.equals(mediaRequestIntent.getAction())) {
KeyEvent event = (KeyEvent) mediaRequestIntent
.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
@@ -233,12 +230,12 @@ public class PlayerSession {
mRoute = null;
mRenderer = new LocalRenderer(mContext, null);
mRenderer.registerListener(mRenderListener);
- updateState(PlaybackState.PLAYSTATE_NONE);
+ updateState(PlaybackState.STATE_NONE);
} else {
// Use remote route
mSession.connect(route, mRouteOptions.get(0));
mRenderer = null;
- updateState(PlaybackState.PLAYSTATE_CONNECTING);
+ updateState(PlaybackState.STATE_CONNECTING);
}
}
@@ -249,7 +246,7 @@ public class PlayerSession {
mRouteControls.addListener(mRouteListener);
Log.d(TAG, "Connected to route, registering listener");
mRenderer = new OneMRPRenderer(mRouteControls);
- updateState(PlaybackState.PLAYSTATE_NONE);
+ updateState(PlaybackState.STATE_NONE);
}
@Override
@@ -258,7 +255,7 @@ public class PlayerSession {
}
}
- private class TransportListener extends TransportPerformer.Listener {
+ private class TransportCallback extends MediaSession.TransportControlsCallback {
@Override
public void onPlay() {
mRenderer.onPlay();
diff --git a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
index 6537d49..f2d691c 100644
--- a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
+++ b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
@@ -149,7 +149,7 @@ public class OneMediaRouteProvider extends RouteProviderService {
public void onError(int type, int extra, Bundle extras, Throwable error) {
Log.d(TAG, "Sending onError with type " + type + " and extra " + extra);
if (mControls != null) {
- mControls.sendPlaybackChangeEvent(PlaybackState.PLAYSTATE_ERROR);
+ mControls.sendPlaybackChangeEvent(PlaybackState.STATE_ERROR);
}
}
@@ -165,23 +165,23 @@ public class OneMediaRouteProvider extends RouteProviderService {
switch (newState) {
case Renderer.STATE_ENDED:
case Renderer.STATE_STOPPED:
- mPlaybackState.setState(PlaybackState.PLAYSTATE_STOPPED, position, 0);
+ mPlaybackState.setState(PlaybackState.STATE_STOPPED, position, 0);
break;
case Renderer.STATE_INIT:
case Renderer.STATE_PREPARING:
- mPlaybackState.setState(PlaybackState.PLAYSTATE_BUFFERING, position, 0);
+ mPlaybackState.setState(PlaybackState.STATE_BUFFERING, position, 0);
break;
case Renderer.STATE_ERROR:
- mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, position, 0);
+ mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
break;
case Renderer.STATE_PAUSED:
- mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED, position, 0);
+ mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0);
break;
case Renderer.STATE_PLAYING:
- mPlaybackState.setState(PlaybackState.PLAYSTATE_PLAYING, position, 1);
+ mPlaybackState.setState(PlaybackState.STATE_PLAYING, position, 1);
break;
default:
- mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, position, 0);
+ mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
mPlaybackState.setErrorMessage("unkown state");
break;
}
@@ -196,7 +196,7 @@ public class OneMediaRouteProvider extends RouteProviderService {
@Override
public void onFocusLost() {
Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
- mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED, mRenderer.getSeekPosition(), 0);
+ mPlaybackState.setState(PlaybackState.STATE_PAUSED, mRenderer.getSeekPosition(), 0);
mRenderer.onPause();
}
diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
index c7715ad..fc5426c 100644
--- a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
+++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
@@ -43,7 +43,6 @@ public class MainActivity extends Activity implements OnItemClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- HardwareRenderer.sUseRenderThread = true;
setContentView(R.layout.activity_main);
ListView lv = (ListView) findViewById(android.R.id.list);
lv.setDrawSelectorOnTop(true);
diff --git a/tests/TtsTests/src/com/android/speech/tts/AbstractTtsSemioticClassTest.java b/tests/TtsTests/src/com/android/speech/tts/AbstractTtsSemioticClassTest.java
new file mode 100644
index 0000000..31484f4
--- /dev/null
+++ b/tests/TtsTests/src/com/android/speech/tts/AbstractTtsSemioticClassTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+package com.android.speech.tts;
+
+import android.test.InstrumentationTestCase;
+
+import android.speech.tts.Markup;
+import android.speech.tts.Utterance;
+import android.speech.tts.Utterance.AbstractTtsSemioticClass;
+
+public class AbstractTtsSemioticClassTest extends InstrumentationTestCase {
+
+ public static class TtsMock extends AbstractTtsSemioticClass<TtsMock> {
+ public TtsMock() {
+ super();
+ }
+
+ public TtsMock(Markup markup) {
+ super();
+ }
+
+ public void setType(String type) {
+ mMarkup.setType(type);
+ }
+ }
+
+ public void testFluentAPI() {
+ new TtsMock()
+ .setPlainText("a plaintext") // from AbstractTts
+ .setGender(Utterance.GENDER_MALE) // from AbstractTtsSemioticClass
+ .setType("test"); // from TtsMock
+ }
+
+ public void testDefaultConstructor() {
+ new TtsMock();
+ }
+
+ public void testMarkupConstructor() {
+ Markup markup = new Markup();
+ new TtsMock(markup);
+ }
+
+ public void testGetType() {
+ TtsMock t = new TtsMock();
+ t.setType("type1");
+ assertEquals("type1", t.getType());
+ t.setType(null);
+ assertEquals(null, t.getType());
+ t.setType("type2");
+ assertEquals("type2", t.getType());
+ }
+
+
+ public void testDefaultGender() {
+ assertEquals(Utterance.GENDER_UNKNOWN, new TtsMock().getGender());
+ }
+
+ public void testSetGender() {
+ assertEquals(Utterance.GENDER_MALE,
+ new TtsMock().setGender(Utterance.GENDER_MALE).getGender());
+ }
+
+ public void testSetGenderNegative() {
+ try {
+ new TtsMock().setGender(-1);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testSetGenderOutOfBounds() {
+ try {
+ new TtsMock().setGender(4);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testDefaultAnimacy() {
+ assertEquals(Utterance.ANIMACY_UNKNOWN, new TtsMock().getAnimacy());
+ }
+
+ public void testSetAnimacy() {
+ assertEquals(Utterance.ANIMACY_ANIMATE,
+ new TtsMock().setAnimacy(Utterance.ANIMACY_ANIMATE).getAnimacy());
+ }
+
+ public void testSetAnimacyNegative() {
+ try {
+ new TtsMock().setAnimacy(-1);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testSetAnimacyOutOfBounds() {
+ try {
+ new TtsMock().setAnimacy(4);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testDefaultMultiplicity() {
+ assertEquals(Utterance.MULTIPLICITY_UNKNOWN, new TtsMock().getMultiplicity());
+ }
+
+ public void testSetMultiplicity() {
+ assertEquals(Utterance.MULTIPLICITY_DUAL,
+ new TtsMock().setMultiplicity(Utterance.MULTIPLICITY_DUAL).getMultiplicity());
+ }
+
+ public void testSetMultiplicityNegative() {
+ try {
+ new TtsMock().setMultiplicity(-1);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testSetMultiplicityOutOfBounds() {
+ try {
+ new TtsMock().setMultiplicity(4);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testDefaultCase() {
+ assertEquals(Utterance.CASE_UNKNOWN, new TtsMock().getCase());
+ }
+
+ public void testSetCase() {
+ assertEquals(Utterance.CASE_VOCATIVE,
+ new TtsMock().setCase(Utterance.CASE_VOCATIVE).getCase());
+ }
+
+ public void testSetCaseNegative() {
+ try {
+ new TtsMock().setCase(-1);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testSetCaseOutOfBounds() {
+ try {
+ new TtsMock().setCase(9);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testToString() {
+ TtsMock t = new TtsMock()
+ .setAnimacy(Utterance.ANIMACY_INANIMATE)
+ .setCase(Utterance.CASE_INSTRUMENTAL)
+ .setGender(Utterance.GENDER_FEMALE)
+ .setMultiplicity(Utterance.MULTIPLICITY_PLURAL);
+ String str =
+ "animacy: \"2\" " +
+ "case: \"8\" " +
+ "gender: \"3\" " +
+ "multiplicity: \"3\"";
+ assertEquals(str, t.toString());
+ }
+
+ public void testToStringSetToUnkown() {
+ TtsMock t = new TtsMock()
+ .setAnimacy(Utterance.ANIMACY_INANIMATE)
+ .setCase(Utterance.CASE_INSTRUMENTAL)
+ .setGender(Utterance.GENDER_FEMALE)
+ .setMultiplicity(Utterance.MULTIPLICITY_PLURAL)
+ // set back to unknown
+ .setAnimacy(Utterance.ANIMACY_UNKNOWN)
+ .setCase(Utterance.CASE_UNKNOWN)
+ .setGender(Utterance.GENDER_UNKNOWN)
+ .setMultiplicity(Utterance.MULTIPLICITY_UNKNOWN);
+ String str = "";
+ assertEquals(str, t.toString());
+ }
+
+}
diff --git a/tests/TtsTests/src/com/android/speech/tts/AbstractTtsTest.java b/tests/TtsTests/src/com/android/speech/tts/AbstractTtsTest.java
new file mode 100644
index 0000000..281c97f
--- /dev/null
+++ b/tests/TtsTests/src/com/android/speech/tts/AbstractTtsTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+package com.android.speech.tts;
+
+import android.test.InstrumentationTestCase;
+
+import android.speech.tts.Markup;
+import android.speech.tts.Utterance.AbstractTts;
+
+public class AbstractTtsTest extends InstrumentationTestCase {
+
+ public static class TtsMock extends AbstractTts<TtsMock> {
+ public TtsMock() {
+ super();
+ }
+
+ public TtsMock(Markup markup) {
+ super();
+ }
+
+ public void setType(String type) {
+ mMarkup.setType(type);
+ }
+
+ @Override
+ public TtsMock setParameter(String key, String value) {
+ return super.setParameter(key, value);
+ }
+
+ @Override
+ public TtsMock removeParameter(String key) {
+ return super.removeParameter(key);
+ }
+ }
+
+ public void testDefaultConstructor() {
+ new TtsMock();
+ }
+
+ public void testMarkupConstructor() {
+ Markup markup = new Markup();
+ new TtsMock(markup);
+ }
+
+ public void testGetType() {
+ TtsMock t = new TtsMock();
+ t.setType("type1");
+ assertEquals("type1", t.getType());
+ t.setType(null);
+ assertEquals(null, t.getType());
+ t.setType("type2");
+ assertEquals("type2", t.getType());
+ }
+
+ public void testGeneratePlainText() {
+ assertNull(new TtsMock().generatePlainText());
+ }
+
+ public void testToString() {
+ TtsMock t = new TtsMock();
+ t.setType("a_type");
+ t.setPlainText("a plaintext");
+ t.setParameter("key1", "value1");
+ t.setParameter("aaa", "value2");
+ String str =
+ "type: \"a_type\" " +
+ "plain_text: \"a plaintext\" " +
+ "aaa: \"value2\" " +
+ "key1: \"value1\"";
+ assertEquals(str, t.toString());
+ }
+
+ public void testRemoveParameter() {
+ TtsMock t = new TtsMock();
+ t.setParameter("key1", "value 1");
+ t.setParameter("aaa", "value a");
+ t.removeParameter("key1");
+ String str =
+ "aaa: \"value a\"";
+ assertEquals(str, t.toString());
+ }
+
+ public void testRemoveParameterBySettingNull() {
+ TtsMock t = new TtsMock();
+ t.setParameter("key1", "value 1");
+ t.setParameter("aaa", "value a");
+ t.setParameter("aaa", null);
+ String str =
+ "key1: \"value 1\"";
+ assertEquals(str, t.toString());
+ }
+}
diff --git a/tests/TtsTests/src/com/android/speech/tts/MarkupTest.java b/tests/TtsTests/src/com/android/speech/tts/MarkupTest.java
new file mode 100644
index 0000000..7ef93ce
--- /dev/null
+++ b/tests/TtsTests/src/com/android/speech/tts/MarkupTest.java
@@ -0,0 +1,510 @@
+/*
+ * 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.
+ */
+
+package com.android.speech.tts;
+
+import junit.framework.Assert;
+import android.os.Parcel;
+import android.test.InstrumentationTestCase;
+
+import android.speech.tts.Markup;
+
+public class MarkupTest extends InstrumentationTestCase {
+
+ public void testEmptyMarkup() {
+ Markup markup = new Markup();
+ assertNull(markup.getType());
+ assertNull(markup.getPlainText());
+ assertEquals(0, markup.parametersSize());
+ assertEquals(0, markup.nestedMarkupSize());
+ }
+
+ public void testGetSetType() {
+ Markup markup = new Markup();
+ markup.setType("one");
+ assertEquals("one", markup.getType());
+ markup.setType(null);
+ assertNull(markup.getType());
+ markup.setType("two");
+ assertEquals("two", markup.getType());
+ }
+
+ public void testGetSetPlainText() {
+ Markup markup = new Markup();
+ markup.setPlainText("one");
+ assertEquals("one", markup.getPlainText());
+ markup.setPlainText(null);
+ assertNull(markup.getPlainText());
+ markup.setPlainText("two");
+ assertEquals("two", markup.getPlainText());
+ }
+
+ public void testParametersSize1() {
+ Markup markup = new Markup();
+ markup.addNestedMarkup(new Markup());
+ assertEquals(1, markup.nestedMarkupSize());
+ }
+
+ public void testParametersSize2() {
+ Markup markup = new Markup();
+ markup.addNestedMarkup(new Markup());
+ markup.addNestedMarkup(new Markup());
+ assertEquals(2, markup.nestedMarkupSize());
+ }
+
+ public void testRemoveParameter() {
+ Markup m = new Markup("type");
+ m.setParameter("key1", "value1");
+ m.setParameter("key2", "value2");
+ m.setParameter("key3", "value3");
+ assertEquals(3, m.parametersSize());
+ m.removeParameter("key1");
+ assertEquals(2, m.parametersSize());
+ m.removeParameter("key3");
+ assertEquals(1, m.parametersSize());
+ assertNull(m.getParameter("key1"));
+ assertEquals("value2", m.getParameter("key2"));
+ assertNull(m.getParameter("key3"));
+ }
+
+ public void testEmptyEqual() {
+ Markup m1 = new Markup();
+ Markup m2 = new Markup();
+ assertTrue(m1.equals(m2));
+ }
+
+ public void testFilledEqual() {
+ Markup m1 = new Markup();
+ m1.setType("type");
+ m1.setPlainText("plain text");
+ m1.setParameter("key1", "value1");
+ m1.addNestedMarkup(new Markup());
+ Markup m2 = new Markup();
+ m2.setType("type");
+ m2.setPlainText("plain text");
+ m2.setParameter("key1", "value1");
+ m2.addNestedMarkup(new Markup());
+ assertTrue(m1.equals(m2));
+ }
+
+ public void testDifferentTypeEqual() {
+ Markup m1 = new Markup();
+ m1.setType("type1");
+ Markup m2 = new Markup();
+ m2.setType("type2");
+ assertFalse(m1.equals(m2));
+ }
+
+ public void testDifferentPlainTextEqual() {
+ Markup m1 = new Markup();
+ m1.setPlainText("plainText1");
+ Markup m2 = new Markup();
+ m2.setPlainText("plainText2");
+ assertFalse(m1.equals(m2));
+ }
+
+ public void testDifferentParamEqual() {
+ Markup m1 = new Markup();
+ m1.setParameter("test", "value1");
+ Markup m2 = new Markup();
+ m2.setParameter("test", "value2");
+ assertFalse(m1.equals(m2));
+ }
+
+ public void testDifferentParameterKeyEqual() {
+ Markup m1 = new Markup();
+ m1.setParameter("test1", "value");
+ Markup m2 = new Markup();
+ m2.setParameter("test2", "value");
+ assertFalse(m1.equals(m2));
+ }
+
+ public void testDifferentParameterValueEqual() {
+ Markup m1 = new Markup();
+ m1.setParameter("test", "value1");
+ Markup m2 = new Markup();
+ m2.setParameter("test", "value2");
+ assertFalse(m1.equals(m2));
+ }
+
+ public void testDifferentNestedMarkupEqual() {
+ Markup m1 = new Markup();
+ Markup nested = new Markup();
+ nested.setParameter("key", "value");
+ m1.addNestedMarkup(nested);
+ Markup m2 = new Markup();
+ m2.addNestedMarkup(new Markup());
+ assertFalse(m1.equals(m2));
+ }
+
+ public void testEmptyToFromString() {
+ Markup m1 = new Markup();
+ String str = m1.toString();
+ assertEquals("", str);
+
+ Markup m2 = Markup.markupFromString(str);
+ assertEquals(m1, m2);
+ }
+
+ public void testTypeToFromString() {
+ Markup m1 = new Markup("atype");
+ String str = m1.toString();
+ assertEquals("type: \"atype\"", str);
+ Markup m2 = Markup.markupFromString(str);
+ assertEquals(m1, m2);
+ }
+
+ public void testPlainTextToFromString() {
+ Markup m1 = new Markup();
+ m1.setPlainText("some_plainText");
+ String str = m1.toString();
+ assertEquals("plain_text: \"some_plainText\"", str);
+
+ Markup m2 = Markup.markupFromString(str);
+ assertEquals(m1, m2);
+ }
+
+ public void testParameterToFromString() {
+ Markup m1 = new Markup("cardinal");
+ m1.setParameter("integer", "-22");
+ String str = m1.toString();
+ assertEquals("type: \"cardinal\" integer: \"-22\"", str);
+ Markup m2 = Markup.markupFromString(str);
+ assertEquals(m1, m2);
+ }
+
+ // Parameters should be ordered alphabettically, so the output is stable.
+ public void testParameterOrderToFromString() {
+ Markup m1 = new Markup("cardinal");
+ m1.setParameter("ccc", "-");
+ m1.setParameter("aaa", "-");
+ m1.setParameter("aa", "-");
+ m1.setParameter("bbb", "-");
+ String str = m1.toString();
+ assertEquals(
+ "type: \"cardinal\" " +
+ "aa: \"-\" " +
+ "aaa: \"-\" " +
+ "bbb: \"-\" " +
+ "ccc: \"-\"",
+ str);
+ Markup m2 = Markup.markupFromString(str);
+ assertEquals(m1, m2);
+ }
+
+ public void testEmptyNestedToFromString() {
+ Markup m1 = new Markup("atype");
+ m1.addNestedMarkup(new Markup());
+ String str = m1.toString();
+ assertEquals("type: \"atype\" markup {}", str);
+ Markup m2 = Markup.markupFromString(str);
+ assertEquals(m1, m2);
+ }
+
+ public void testNestedWithTypeToFromString() {
+ Markup m1 = new Markup("atype");
+ m1.addNestedMarkup(new Markup("nested_type"));
+ String str = m1.toString();
+ assertEquals(
+ "type: \"atype\" " +
+ "markup { type: \"nested_type\" }",
+ str);
+ Markup m2 = Markup.markupFromString(str);
+ assertEquals(m1, m2);
+ }
+
+ public void testRemoveNestedMarkup() {
+ Markup m = new Markup("atype");
+ Markup m1 = new Markup("nested_type1");
+ Markup m2 = new Markup("nested_type2");
+ Markup m3 = new Markup("nested_type3");
+ m.addNestedMarkup(m1);
+ m.addNestedMarkup(m2);
+ m.addNestedMarkup(m3);
+ m.removeNestedMarkup(m1);
+ m.removeNestedMarkup(m3);
+ String str = m.toString();
+ assertEquals(
+ "type: \"atype\" " +
+ "markup { type: \"nested_type2\" }",
+ str);
+ Markup mFromString = Markup.markupFromString(str);
+ assertEquals(m, mFromString);
+ }
+
+ public void testLotsofNestingToFromString() {
+ Markup m1 = new Markup("top")
+ .addNestedMarkup(new Markup("top_child1")
+ .addNestedMarkup(new Markup("top_child1_child1"))
+ .addNestedMarkup(new Markup("top_child1_child2")))
+ .addNestedMarkup(new Markup("top_child2")
+ .addNestedMarkup(new Markup("top_child2_child2"))
+ .addNestedMarkup(new Markup("top_child2_child2")));
+
+ String str = m1.toString();
+ assertEquals(
+ "type: \"top\" " +
+ "markup { " +
+ "type: \"top_child1\" " +
+ "markup { type: \"top_child1_child1\" } " +
+ "markup { type: \"top_child1_child2\" } " +
+ "} " +
+ "markup { " +
+ "type: \"top_child2\" " +
+ "markup { type: \"top_child2_child2\" } " +
+ "markup { type: \"top_child2_child2\" } " +
+ "}",
+ str);
+ Markup m2 = Markup.markupFromString(str);
+ assertEquals(m1, m2);
+ }
+
+ public void testFilledToFromString() {
+ Markup m1 = new Markup("measure");
+ m1.setPlainText("fifty-five amps");
+ m1.setParameter("unit", "meter");
+ m1.addNestedMarkup(new Markup("cardinal").setParameter("integer", "55"));
+ String str = m1.toString();
+ assertEquals(
+ "type: \"measure\" " +
+ "plain_text: \"fifty-five amps\" " +
+ "unit: \"meter\" " +
+ "markup { type: \"cardinal\" integer: \"55\" }",
+ str);
+
+ Markup m2 = Markup.markupFromString(str);
+ assertEquals(m1, m2);
+ }
+
+ public void testErrorFromString() {
+ String str = "type: \"atype\" markup {mistake}";
+ try {
+ Markup.markupFromString(str);
+ Assert.fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testEscapeQuotes() {
+ Markup m1 = new Markup("text")
+ .setParameter("something_unknown", "\"this\" is \"a sentence \" with quotes\"");
+ String str = m1.toString();
+ assertEquals(
+ "type: \"text\" " +
+ "something_unknown: \"\\\"this\\\" is \\\"a sentence \\\" with quotes\\\"\"",
+ str);
+
+ Markup m2 = Markup.markupFromString(str);
+ assertEquals(m1.toString(), m2.toString());
+ assertEquals(m1, m2);
+ }
+
+ public void testEscapeSlashes1() {
+ Markup m1 = new Markup("text")
+ .setParameter("something_unknown", "\\ \\\\ \t \n \"");
+ String str = m1.toString();
+ assertEquals(
+ "type: \"text\" " +
+ "something_unknown: \"\\\\ \\\\\\\\ \t \n \\\"\"",
+ str);
+
+ Markup m2 = Markup.markupFromString(str);
+ assertEquals(m1.toString(), m2.toString());
+ assertEquals(m1, m2);
+ }
+
+ public void testEscapeSlashes2() {
+ Markup m1 = new Markup("text")
+ .setParameter("something_unknown", "\\\"\\\"\\\\\"\"\\\\\\\"\"\"");
+ String str = m1.toString();
+ assertEquals(
+ "type: \"text\" " +
+ "something_unknown: \"\\\\\\\"\\\\\\\"\\\\\\\\\\\"\\\"\\\\\\\\\\\\\\\"\\\"\\\"\"",
+ str);
+
+ Markup m2 = Markup.markupFromString(str);
+ assertEquals(m1.toString(), m2.toString());
+ assertEquals(m1, m2);
+ }
+
+ public void testBadInput1() {
+ String str = "type: \"text\" text: \"\\\"";
+ try {
+ Markup.markupFromString(str);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testBadInput2() {
+ String str = "type: \"text\" text: \"\\a\"";
+ try {
+ Markup.markupFromString(str);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testValidParameterKey() {
+ Markup m = new Markup();
+ m.setParameter("ke9__yk_88ey_za7_", "test");
+ }
+
+ public void testInValidParameterKeyEmpty() {
+ Markup m = new Markup();
+ try {
+ m.setParameter("", "test");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testInValidParameterKeyDollar() {
+ Markup m = new Markup();
+ try {
+ m.setParameter("ke9y$k88ey7", "test");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testInValidParameterKeySpace() {
+ Markup m = new Markup();
+ try {
+ m.setParameter("ke9yk88ey7 ", "test");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testValidType() {
+ new Markup("_this_is_1_valid_type_222");
+ }
+
+ public void testInValidTypeAmpersand() {
+ try {
+ new Markup("abcde1234&");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testInValidTypeSpace() {
+ try {
+ new Markup(" ");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testSimpleParcelable() {
+ Markup markup = new Markup();
+
+ Parcel parcel = Parcel.obtain();
+ markup.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel);
+
+ assertFalse(markup == fromParcel);
+ assertEquals(markup, fromParcel);
+ }
+
+ public void testTypeParcelable() {
+ Markup markup = new Markup("text");
+
+ Parcel parcel = Parcel.obtain();
+ markup.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel);
+
+ assertFalse(markup == fromParcel);
+ assertEquals(markup, fromParcel);
+ }
+
+ public void testPlainTextsParcelable() {
+ Markup markup = new Markup();
+ markup.setPlainText("plainText");
+
+ Parcel parcel = Parcel.obtain();
+ markup.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel);
+
+ assertFalse(markup == fromParcel);
+ assertEquals(markup, fromParcel);
+ }
+
+ public void testParametersParcelable() {
+ Markup markup = new Markup();
+ markup.setParameter("key1", "value1");
+ markup.setParameter("key2", "value2");
+ markup.setParameter("key3", "value3");
+
+ Parcel parcel = Parcel.obtain();
+ markup.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel);
+
+ assertFalse(markup == fromParcel);
+ assertEquals(markup, fromParcel);
+ }
+
+ public void testNestedParcelable() {
+ Markup markup = new Markup();
+ markup.addNestedMarkup(new Markup("first"));
+ markup.addNestedMarkup(new Markup("second"));
+ markup.addNestedMarkup(new Markup("third"));
+
+ Parcel parcel = Parcel.obtain();
+ markup.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel);
+
+ assertFalse(markup == fromParcel);
+ assertEquals(markup, fromParcel);
+ }
+
+ public void testAllFieldsParcelable() {
+ Markup markup = new Markup("text");
+ markup.setPlainText("plain text");
+ markup.setParameter("key1", "value1");
+ markup.setParameter("key2", "value2");
+ markup.setParameter("key3", "value3");
+ markup.addNestedMarkup(new Markup("first"));
+ markup.addNestedMarkup(new Markup("second"));
+ markup.addNestedMarkup(new Markup("third"));
+
+ Parcel parcel = Parcel.obtain();
+ markup.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel);
+
+ assertFalse(markup == fromParcel);
+ assertEquals(markup, fromParcel);
+ }
+
+ public void testKeyCannotBeType() {
+ try {
+ new Markup().setParameter("type", "vale");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testKeyCannotBePlainText() {
+ try {
+ new Markup().setParameter("plain_text", "value");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+}
diff --git a/tests/TtsTests/src/com/android/speech/tts/TtsCardinalTest.java b/tests/TtsTests/src/com/android/speech/tts/TtsCardinalTest.java
new file mode 100644
index 0000000..c34f4ac
--- /dev/null
+++ b/tests/TtsTests/src/com/android/speech/tts/TtsCardinalTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+package com.android.speech.tts;
+
+import junit.framework.Assert;
+import android.test.InstrumentationTestCase;
+import android.test.MoreAsserts;
+
+import android.speech.tts.Markup;
+import android.speech.tts.Utterance;
+import android.speech.tts.Utterance.TtsCardinal;
+import android.speech.tts.Utterance.TtsText;
+
+public class TtsCardinalTest extends InstrumentationTestCase {
+
+ public void testConstruct() {
+ assertNotNull(new TtsCardinal(0));
+ }
+
+ public void testFluentAPI() {
+ new TtsCardinal()
+ .setPlainText("a plaintext") // from AbstractTts
+ .setGender(Utterance.GENDER_MALE) // from AbstractTtsSemioticClass
+ .setInteger("-10001"); // from TtsText
+ }
+
+ public void testZero() {
+ assertEquals("0", new TtsCardinal(0).getInteger());
+ }
+
+ public void testThirtyOne() {
+ assertEquals("31", new TtsCardinal(31).getInteger());
+ }
+
+ public void testMarkupZero() {
+ TtsCardinal c = new TtsCardinal(0);
+ Markup m = c.getMarkup();
+ assertEquals("0", m.getParameter("integer"));
+ }
+
+ public void testMarkupThirtyOne() {
+ TtsCardinal c = new TtsCardinal(31);
+ Markup m = c.getMarkup();
+ assertEquals("31", m.getParameter("integer"));
+ }
+
+ public void testMarkupThirtyOneString() {
+ TtsCardinal c = new TtsCardinal("31");
+ Markup m = c.getMarkup();
+ assertEquals("31", m.getParameter("integer"));
+ }
+
+ public void testMarkupNegativeThirtyOne() {
+ TtsCardinal c = new TtsCardinal(-31);
+ Markup m = c.getMarkup();
+ assertEquals("-31", m.getParameter("integer"));
+ }
+
+ public void testMarkupMinusZero() {
+ TtsCardinal c = new TtsCardinal("-0");
+ Markup m = c.getMarkup();
+ assertEquals("-0", m.getParameter("integer"));
+ }
+
+ public void testMarkupNegativeThirtyOneString() {
+ TtsCardinal c = new TtsCardinal("-31");
+ Markup m = c.getMarkup();
+ assertEquals("-31", m.getParameter("integer"));
+ }
+
+ public void testOnlyLetters() {
+ try {
+ new TtsCardinal("abc");
+ Assert.fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testOnlyMinus() {
+ try {
+ new TtsCardinal("-");
+ Assert.fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testNegativeLetters() {
+ try {
+ new TtsCardinal("-abc");
+ Assert.fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testLetterNumberMix() {
+ try {
+ new TtsCardinal("-0a1b2c");
+ Assert.fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void letterNumberMix2() {
+ try {
+ new TtsCardinal("-a0b1c2");
+ Assert.fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+}
diff --git a/tests/TtsTests/src/com/android/speech/tts/TtsTextTest.java b/tests/TtsTests/src/com/android/speech/tts/TtsTextTest.java
new file mode 100644
index 0000000..35fd453
--- /dev/null
+++ b/tests/TtsTests/src/com/android/speech/tts/TtsTextTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package com.android.speech.tts;
+
+import android.test.InstrumentationTestCase;
+
+import android.speech.tts.Markup;
+import android.speech.tts.Utterance;
+import android.speech.tts.Utterance.TtsText;
+
+public class TtsTextTest extends InstrumentationTestCase {
+
+ public void testConstruct() {
+ assertNotNull(new TtsText());
+ }
+
+ public void testFluentAPI() {
+ new TtsText()
+ .setPlainText("a plaintext") // from AbstractTts
+ .setGender(Utterance.GENDER_MALE) // from AbstractTtsSemioticClass
+ .setText("text"); // from TtsText
+ }
+
+ public void testConstructEmptyString() {
+ assertTrue(new TtsText("").getText().isEmpty());
+ }
+
+ public void testConstructString() {
+ assertEquals("this is a test.", new TtsText("this is a test.").getText());
+ }
+
+ public void testSetText() {
+ assertEquals("This is a test.", new TtsText().setText("This is a test.").getText());
+ }
+
+ public void testEmptyMarkup() {
+ TtsText t = new TtsText();
+ Markup m = t.getMarkup();
+ assertEquals("text", m.getType());
+ assertNull(m.getPlainText());
+ assertEquals(0, m.nestedMarkupSize());
+ }
+
+ public void testConstructStringMarkup() {
+ TtsText t = new TtsText("test");
+ Markup m = t.getMarkup();
+ assertEquals("text", m.getType());
+ assertEquals("test", m.getParameter("text"));
+ assertEquals(0, m.nestedMarkupSize());
+ }
+
+ public void testSetStringMarkup() {
+ TtsText t = new TtsText();
+ t.setText("test");
+ Markup m = t.getMarkup();
+ assertEquals("text", m.getType());
+ assertEquals("test", m.getParameter("text"));
+ assertEquals(0, m.nestedMarkupSize());
+ }
+}
diff --git a/tests/TtsTests/src/com/android/speech/tts/UtteranceTest.java b/tests/TtsTests/src/com/android/speech/tts/UtteranceTest.java
new file mode 100644
index 0000000..8014dd1
--- /dev/null
+++ b/tests/TtsTests/src/com/android/speech/tts/UtteranceTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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.
+ */
+
+package com.android.speech.tts;
+
+import android.speech.tts.Markup;
+import android.speech.tts.Utterance;
+import android.speech.tts.Utterance.TtsCardinal;
+import android.speech.tts.Utterance.TtsText;
+
+import android.test.InstrumentationTestCase;
+
+public class UtteranceTest extends InstrumentationTestCase {
+
+ public void testEmptyUtterance() {
+ Utterance utt = new Utterance();
+ assertEquals(0, utt.size());
+ }
+
+ public void testSizeCardinal() {
+ Utterance utt = new Utterance()
+ .append(new TtsCardinal(42));
+ assertEquals(1, utt.size());
+ }
+
+ public void testSizeCardinalString() {
+ Utterance utt = new Utterance()
+ .append(new TtsCardinal(42))
+ .append(new TtsText("is the answer"));
+ assertEquals(2, utt.size());
+ }
+
+ public void testMarkupEmpty() {
+ Markup m = new Utterance().createMarkup();
+ assertEquals("utterance", m.getType());
+ assertEquals("", m.getPlainText());
+ }
+
+ public void testMarkupCardinal() {
+ Utterance utt = new Utterance()
+ .append(new TtsCardinal(42));
+ Markup markup = utt.createMarkup();
+ assertEquals("utterance", markup.getType());
+ assertEquals("42", markup.getPlainText());
+ assertEquals("42", markup.getNestedMarkup(0).getParameter("integer"));
+ assertEquals("42", markup.getNestedMarkup(0).getPlainText());
+ }
+
+ public void testMarkupCardinalString() {
+ Utterance utt = new Utterance()
+ .append(new TtsCardinal(42))
+ .append(new TtsText("is not just a number."));
+ Markup markup = utt.createMarkup();
+ assertEquals("utterance", markup.getType());
+ assertEquals("42 is not just a number.", markup.getPlainText());
+ assertEquals("cardinal", markup.getNestedMarkup(0).getType());
+ assertEquals("42", markup.getNestedMarkup(0).getParameter("integer"));
+ assertEquals("42", markup.getNestedMarkup(0).getPlainText());
+ assertEquals("text", markup.getNestedMarkup(1).getType());
+ assertEquals("is not just a number.", markup.getNestedMarkup(1).getParameter("text"));
+ assertEquals("is not just a number.", markup.getNestedMarkup(1).getPlainText());
+ }
+
+ public void testTextCardinalToFromString() {
+ Utterance utt = new Utterance()
+ .append(new TtsCardinal(55))
+ .append(new TtsText("this is a text."));
+ String str = utt.toString();
+ assertEquals(
+ "type: \"utterance\" " +
+ "markup { " +
+ "type: \"cardinal\" " +
+ "integer: \"55\" " +
+ "} " +
+ "markup { " +
+ "type: \"text\" " +
+ "text: \"this is a text.\" " +
+ "}"
+ , str);
+
+ Utterance utt_new = Utterance.utteranceFromString(str);
+ assertEquals(str, utt_new.toString());
+ }
+
+ public void testNotUtteranceFromString() {
+ String str =
+ "type: \"this_is_not_an_utterance\" " +
+ "markup { " +
+ "type: \"cardinal\" " +
+ "plain_text: \"55\" " +
+ "integer: \"55\" " +
+ "}";
+ try {
+ Utterance.utteranceFromString(str);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testFromMarkup() {
+ String markup_str =
+ "type: \"utterance\" " +
+ "markup { " +
+ "type: \"cardinal\" " +
+ "plain_text: \"55\" " +
+ "integer: \"55\" " +
+ "} " +
+ "markup { " +
+ "type: \"text\" " +
+ "plain_text: \"this is a text.\" " +
+ "text: \"this is a text.\" " +
+ "}";
+ Utterance utt = Utterance.utteranceFromString(markup_str);
+ assertEquals(markup_str, utt.toString());
+ }
+
+ public void testsetPlainText() {
+ Utterance utt = new Utterance()
+ .append(new TtsCardinal(-100).setPlainText("minus one hundred"));
+ assertEquals("minus one hundred", utt.get(0).getPlainText());
+ }
+
+ public void testRemoveTextThroughSet() {
+ Utterance utt = new Utterance()
+ .append(new TtsText().setText("test").setText(null));
+ assertNull(((TtsText) utt.get(0)).getText());
+ }
+
+ public void testUnknownNodeWithPlainText() {
+ String str =
+ "type: \"utterance\" " +
+ "markup { " +
+ "type: \"some_future_feature\" " +
+ "plain_text: \"biep bob bob\" " +
+ "bombom: \"lorum ipsum\" " +
+ "}";
+ Utterance utt = Utterance.utteranceFromString(str);
+ assertNotNull(utt);
+ assertEquals("text", utt.get(0).getType());
+ assertEquals("biep bob bob", ((TtsText) utt.get(0)).getText());
+ }
+
+ public void testUnknownNodeWithNoPlainTexts() {
+ String str =
+ "type: \"utterance\" " +
+ "markup { " +
+ "type: \"some_future_feature\" " +
+ "bombom: \"lorum ipsum\" " +
+ "markup { type: \"cardinal\" integer: \"10\" } " +
+ "markup { type: \"text\" text: \"pears\" } " +
+ "}";
+ Utterance utt = Utterance.utteranceFromString(str);
+ assertEquals(
+ "type: \"utterance\" " +
+ "markup { type: \"cardinal\" integer: \"10\" } " +
+ "markup { type: \"text\" text: \"pears\" }", utt.toString());
+ }
+
+ public void testCreateWarningOnFallbackTrue() {
+ Utterance utt = new Utterance()
+ .append(new TtsText("test"))
+ .setNoWarningOnFallback(true);
+ assertEquals(
+ "type: \"utterance\" " +
+ "no_warning_on_fallback: \"true\" " +
+ "markup { " +
+ "type: \"text\" " +
+ "text: \"test\" " +
+ "}", utt.toString());
+ }
+
+ public void testCreateWarningOnFallbackFalse() {
+ Utterance utt = new Utterance()
+ .append(new TtsText("test"))
+ .setNoWarningOnFallback(false);
+ assertEquals(
+ "type: \"utterance\" " +
+ "no_warning_on_fallback: \"false\" " +
+ "markup { " +
+ "type: \"text\" " +
+ "text: \"test\" " +
+ "}", utt.toString());
+ }
+
+ public void testCreatePlainTexts() {
+ Utterance utt = new Utterance()
+ .append(new TtsText("test"))
+ .append(new TtsCardinal(-55));
+ assertEquals(
+ "type: \"utterance\" " +
+ "plain_text: \"test -55\" " +
+ "markup { type: \"text\" plain_text: \"test\" text: \"test\" } " +
+ "markup { type: \"cardinal\" plain_text: \"-55\" integer: \"-55\" }",
+ utt.createMarkup().toString()
+ );
+ }
+
+ public void testDontOverwritePlainTexts() {
+ Utterance utt = new Utterance()
+ .append(new TtsText("test").setPlainText("else"))
+ .append(new TtsCardinal(-55).setPlainText("44"));
+ assertEquals(
+ "type: \"utterance\" " +
+ "plain_text: \"else 44\" " +
+ "markup { type: \"text\" plain_text: \"else\" text: \"test\" } " +
+ "markup { type: \"cardinal\" plain_text: \"44\" integer: \"-55\" }",
+ utt.createMarkup().toString()
+ );
+ }
+
+ public void test99BottlesOnWallMarkup() {
+ Utterance utt = new Utterance()
+ .append("there are")
+ .append(99)
+ .append("bottles on the wall.");
+ assertEquals(
+ "type: \"utterance\" " +
+ "plain_text: \"there are 99 bottles on the wall.\" " +
+ "markup { type: \"text\" plain_text: \"there are\" text: \"there are\" } " +
+ "markup { type: \"cardinal\" plain_text: \"99\" integer: \"99\" } " +
+ "markup { type: \"text\" plain_text: \"bottles on the wall.\" text: \"bottles on the wall.\" }",
+ utt.createMarkup().toString());
+ assertEquals("99", utt.createMarkup().getNestedMarkup(1).getPlainText());
+ Markup markup = new Markup(utt.createMarkup());
+ assertEquals("99", markup.getNestedMarkup(1).getPlainText());
+ }
+
+ public void testWhat() {
+ Utterance utt = new Utterance()
+ .append("there are")
+ .append(99)
+ .append("bottles on the wall.");
+ Markup m = utt.createMarkup();
+ m.getNestedMarkup(1).getPlainText().equals("99");
+ }
+}
diff --git a/tests/VoiceInteraction/AndroidManifest.xml b/tests/VoiceInteraction/AndroidManifest.xml
index 2d08163..e1a5854 100644
--- a/tests/VoiceInteraction/AndroidManifest.xml
+++ b/tests/VoiceInteraction/AndroidManifest.xml
@@ -23,7 +23,8 @@
android:permission="android.permission.BIND_VOICE_INTERACTION"
android:process=":session">
</service>
- <activity android:name="TestInteractionActivity" android:label="Voice Interaction Target">
+ <activity android:name="TestInteractionActivity" android:label="Voice Interaction Target"
+ android:theme="@android:style/Theme.Quantum.Light.Voice">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/VoiceInteraction/res/layout/test_interaction.xml b/tests/VoiceInteraction/res/layout/test_interaction.xml
index 2abf651..4c0c67a 100644
--- a/tests/VoiceInteraction/res/layout/test_interaction.xml
+++ b/tests/VoiceInteraction/res/layout/test_interaction.xml
@@ -18,6 +18,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
+ android:padding="8dp"
>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
@@ -29,9 +30,16 @@
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
- android:layout_marginTop="10dp"
- android:textSize="12sp"
+ android:layout_marginTop="16dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#ffffffff"
/>
+ <Button android:id="@+id/abort"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:text="@string/abortVoice"
+ />
+
</LinearLayout>
diff --git a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
index 563fa44..142d781 100644
--- a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
+++ b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
@@ -14,26 +14,49 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="#ffffffff"
- android:fitsSystemWindows="true"
- >
-
- <TextView android:id="@+id/text"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="32dp"
- />
-
- <Button android:id="@+id/start"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/start"
- />
-
-</LinearLayout>
-
-
+ android:fitsSystemWindows="true">
+
+ <FrameLayout android:layout_width="fill_parent"
+ android:layout_height="match_parent"
+ android:padding="8dp">
+
+ <LinearLayout android:id="@+id/content"
+ android:layout_width="fill_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="#ffffffff"
+ android:elevation="8dp"
+ >
+
+ <TextView android:id="@+id/text"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+
+ <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Button android:id="@+id/start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/start"
+ />
+ <Button android:id="@+id/confirm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/confirm"
+ />
+ <Button android:id="@+id/abort"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/abort"
+ />
+ </LinearLayout>
+
+ </LinearLayout>
+ </FrameLayout>
+</FrameLayout>
diff --git a/tests/VoiceInteraction/res/values/strings.xml b/tests/VoiceInteraction/res/values/strings.xml
index 12edb31..70baa52 100644
--- a/tests/VoiceInteraction/res/values/strings.xml
+++ b/tests/VoiceInteraction/res/values/strings.xml
@@ -16,7 +16,10 @@
<resources>
- <string name="start">Start!</string>
+ <string name="start">Start</string>
+ <string name="confirm">Confirm</string>
+ <string name="abort">Abort</string>
+ <string name="abortVoice">Abort Voice</string>
</resources>
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index a3af284..c24a088 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -33,9 +33,17 @@ public class MainInteractionSession extends VoiceInteractionSession
View mContentView;
TextView mText;
Button mStartButton;
+ Button mConfirmButton;
+ Button mAbortButton;
+ static final int STATE_IDLE = 0;
+ static final int STATE_LAUNCHING = 1;
+ static final int STATE_CONFIRM = 2;
+ static final int STATE_COMMAND = 3;
+ static final int STATE_ABORT_VOICE = 4;
+
+ int mState = STATE_IDLE;
Request mPendingRequest;
- boolean mPendingConfirm;
MainInteractionSession(Context context) {
super(context);
@@ -54,21 +62,39 @@ public class MainInteractionSession extends VoiceInteractionSession
mText = (TextView)mContentView.findViewById(R.id.text);
mStartButton = (Button)mContentView.findViewById(R.id.start);
mStartButton.setOnClickListener(this);
+ mConfirmButton = (Button)mContentView.findViewById(R.id.confirm);
+ mConfirmButton.setOnClickListener(this);
+ mAbortButton = (Button)mContentView.findViewById(R.id.abort);
+ mAbortButton.setOnClickListener(this);
+ updateState();
return mContentView;
}
+ void updateState() {
+ mStartButton.setEnabled(mState == STATE_IDLE);
+ mConfirmButton.setEnabled(mState == STATE_CONFIRM || mState == STATE_COMMAND);
+ mAbortButton.setEnabled(mState == STATE_ABORT_VOICE);
+ }
+
public void onClick(View v) {
- if (mPendingRequest == null) {
- mStartButton.setEnabled(false);
+ if (v == mStartButton) {
+ mState = STATE_LAUNCHING;
+ updateState();
startVoiceActivity(mStartIntent);
- } else {
- if (mPendingConfirm) {
+ } else if (v == mConfirmButton) {
+ if (mState == STATE_CONFIRM) {
mPendingRequest.sendConfirmResult(true, null);
} else {
mPendingRequest.sendCommandResult(true, null);
}
mPendingRequest = null;
- mStartButton.setText("Start");
+ mState = STATE_IDLE;
+ updateState();
+ } else if (v == mAbortButton) {
+ mPendingRequest.sendAbortVoiceResult(null);
+ mPendingRequest = null;
+ mState = STATE_IDLE;
+ updateState();
}
}
@@ -78,23 +104,32 @@ public class MainInteractionSession extends VoiceInteractionSession
}
@Override
- public void onConfirm(Caller caller, Request request, String prompt, Bundle extras) {
+ public void onConfirm(Caller caller, Request request, CharSequence prompt, Bundle extras) {
Log.i(TAG, "onConfirm: prompt=" + prompt + " extras=" + extras);
mText.setText(prompt);
- mStartButton.setEnabled(true);
mStartButton.setText("Confirm");
mPendingRequest = request;
- mPendingConfirm = true;
+ mState = STATE_CONFIRM;
+ updateState();
+ }
+
+ @Override
+ public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) {
+ Log.i(TAG, "onAbortVoice: message=" + message + " extras=" + extras);
+ mText.setText(message);
+ mPendingRequest = request;
+ mState = STATE_ABORT_VOICE;
+ updateState();
}
@Override
public void onCommand(Caller caller, Request request, String command, Bundle extras) {
Log.i(TAG, "onCommand: command=" + command + " extras=" + extras);
mText.setText("Command: " + command);
- mStartButton.setEnabled(true);
mStartButton.setText("Finish Command");
mPendingRequest = request;
- mPendingConfirm = false;
+ mState = STATE_COMMAND;
+ updateState();
}
@Override
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
index 9c772ff..3ae6a36 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -20,11 +20,16 @@ import android.app.Activity;
import android.app.VoiceInteractor;
import android.os.Bundle;
import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
-public class TestInteractionActivity extends Activity {
+public class TestInteractionActivity extends Activity implements View.OnClickListener {
static final String TAG = "TestInteractionActivity";
VoiceInteractor mInteractor;
+ Button mAbortButton;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -37,6 +42,13 @@ public class TestInteractionActivity extends Activity {
}
setContentView(R.layout.test_interaction);
+ mAbortButton = (Button)findViewById(R.id.abort);
+ mAbortButton.setOnClickListener(this);
+
+ // Framework should take care of these.
+ getWindow().setGravity(Gravity.TOP);
+ getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
mInteractor = getVoiceInteractor();
VoiceInteractor.ConfirmationRequest req = new VoiceInteractor.ConfirmationRequest(
@@ -62,6 +74,26 @@ public class TestInteractionActivity extends Activity {
}
@Override
+ public void onClick(View v) {
+ if (v == mAbortButton) {
+ VoiceInteractor.AbortVoiceRequest req = new VoiceInteractor.AbortVoiceRequest(
+ "Dammit, we suck :(", null) {
+ @Override
+ public void onCancel() {
+ Log.i(TAG, "Canceled!");
+ }
+
+ @Override
+ public void onAbortResult(Bundle result) {
+ Log.i(TAG, "Abort result: result=" + result);
+ getActivity().finish();
+ }
+ };
+ mInteractor.submitRequest(req);
+ }
+ }
+
+ @Override
public void onDestroy() {
super.onDestroy();
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index e9daffd..e35bc06 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -71,7 +71,7 @@ public final class Canvas_Delegate {
* Returns the native delegate associated to a given {@link Canvas} object.
*/
public static Canvas_Delegate getDelegate(Canvas canvas) {
- return sManager.getDelegate(canvas.getNativeCanvas());
+ return sManager.getDelegate(canvas.getNativeCanvasWrapper());
}
/**
@@ -102,7 +102,7 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static boolean isOpaque(Canvas thisCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
if (canvasDelegate == null) {
return false;
}
@@ -113,7 +113,7 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static int getWidth(Canvas thisCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
if (canvasDelegate == null) {
return 0;
}
@@ -124,7 +124,7 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static int getHeight(Canvas thisCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
if (canvasDelegate == null) {
return 0;
}
@@ -135,7 +135,7 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
if (canvasDelegate == null) {
return;
}
@@ -146,7 +146,7 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static void rotate(Canvas thisCanvas, float degrees) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
if (canvasDelegate == null) {
return;
}
@@ -157,7 +157,7 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
if (canvasDelegate == null) {
return;
}
@@ -168,7 +168,7 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
if (canvasDelegate == null) {
return;
}
@@ -204,7 +204,7 @@ public final class Canvas_Delegate {
/*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
float bottom) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
if (canvasDelegate == null) {
return false;
}
@@ -227,7 +227,7 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static int save(Canvas thisCanvas, int saveFlags) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
if (canvasDelegate == null) {
return 0;
}
@@ -238,7 +238,7 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static void restore(Canvas thisCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
if (canvasDelegate == null) {
return;
}
@@ -249,7 +249,7 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static int getSaveCount(Canvas thisCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
if (canvasDelegate == null) {
return 0;
}
@@ -260,7 +260,7 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
if (canvasDelegate == null) {
return;
}
@@ -287,7 +287,7 @@ public final class Canvas_Delegate {
/*package*/ static void drawLines(Canvas thisCanvas,
final float[] pts, final int offset, final int count,
Paint paint) {
- draw(thisCanvas.getNativeCanvas(), paint.mNativePaint, false /*compositeOnly*/,
+ draw(thisCanvas.getNativeCanvasWrapper(), paint.mNativePaint, false /*compositeOnly*/,
false /*forceSrcMode*/, new GcSnapshot.Drawable() {
@Override
public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index d31239b..5c51c63 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1466,4 +1466,10 @@ public final class BridgeContext extends Context {
// pass
return new File[0];
}
+
+ @Override
+ public File[] getExternalMediaDirs() {
+ // pass
+ return new File[0];
+ }
}
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 58b0d61..99151c3 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -90,6 +90,19 @@ public class ScanResult implements Parcelable {
*/
public final static int UNSPECIFIED = -1;
+ /** information element from beacon
+ * @hide
+ */
+ public static class InformationElement {
+ public int id;
+ public byte[] bytes;
+ }
+
+ /** information elements found in the beacon
+ * @hide
+ */
+ public InformationElement informationElements[];
+
/** {@hide} */
public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency,
long tsf) {
@@ -131,8 +144,7 @@ public class ScanResult implements Parcelable {
distanceCm = source.distanceCm;
distanceSdCm = source.distanceSdCm;
seen = source.seen;
- if (source.passpoint != null)
- passpoint = new WifiPasspointInfo(source.passpoint);
+ passpoint = source.passpoint;
}
}
@@ -166,8 +178,7 @@ public class ScanResult implements Parcelable {
sb.append(", distanceSd: ").append((distanceSdCm != UNSPECIFIED ? distanceSdCm : "?")).
append("(cm)");
- if (passpoint != null)
- sb.append(", passpoint: [").append(passpoint.toString()).append("]");
+ sb.append(", passpoint: ").append(passpoint != null ? "yes" : "no");
return sb.toString();
}
@@ -199,6 +210,16 @@ public class ScanResult implements Parcelable {
} else {
dest.writeInt(0);
}
+ if (informationElements != null) {
+ dest.writeInt(informationElements.length);
+ for (int i = 0; i < informationElements.length; i++) {
+ dest.writeInt(informationElements[i].id);
+ dest.writeInt(informationElements[i].bytes.length);
+ dest.writeByteArray(informationElements[i].bytes);
+ }
+ } else {
+ dest.writeInt(0);
+ }
}
/** Implement the Parcelable interface {@hide} */
@@ -223,6 +244,17 @@ public class ScanResult implements Parcelable {
if (in.readInt() == 1) {
sr.passpoint = WifiPasspointInfo.CREATOR.createFromParcel(in);
}
+ int n = in.readInt();
+ if (n != 0) {
+ sr.informationElements = new InformationElement[n];
+ for (int i = 0; i < n; i++) {
+ sr.informationElements[i] = new InformationElement();
+ sr.informationElements[i].id = in.readInt();
+ int len = in.readInt();
+ sr.informationElements[i].bytes = new byte[len];
+ in.readByteArray(sr.informationElements[i].bytes);
+ }
+ }
return sr;
}
@@ -230,5 +262,4 @@ public class ScanResult implements Parcelable {
return new ScanResult[size];
}
};
-
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 963117c..5dfc318 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -324,6 +324,24 @@ public class WifiConfiguration implements Parcelable {
/**
* @hide
+ * Uid of app creating the configuration
+ */
+ public int creatorUid;
+
+ /**
+ * @hide
+ * Uid of last app issuing a connection related command
+ */
+ public int lastConnectUid;
+
+ /**
+ * @hide
+ * Uid of last app modifying the configuration
+ */
+ public int lastUpdateUid;
+
+ /**
+ * @hide
* BSSID list on which this configuration was seen.
* TODO: prevent this list to grow infinitely, age-out the results
*/
@@ -441,10 +459,17 @@ public class WifiConfiguration implements Parcelable {
/** @hide
* if this is set, the WifiConfiguration cannot use linkages so as to bump
* it's relative priority.
+ * - status between and 128 indicate various level of blacklisting depending
+ * on the severity or frequency of the connection error
+ * - deleted status indicates that the user is deleting the configuration, and so
+ * although it may have been self added we will not re-self-add it, ignore it,
+ * not return it to applications, and not connect to it
* */
public static final int AUTO_JOIN_TEMPORARY_DISABLED = 1;
/** @hide */
- public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE = 2;
+ public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE = 128;
+ /** @hide */
+ public static final int AUTO_JOIN_DELETED = 200;
/**
* @hide
@@ -453,11 +478,29 @@ public class WifiConfiguration implements Parcelable {
/**
* Set if the configuration was self added by the framework
+ * This boolean is cleared if we get a connect/save/ update or
+ * any wifiManager command that indicate the user interacted with the configuration
+ * since we will now consider that the configuration belong to him.
* @hide
*/
public boolean selfAdded;
/**
+ * Set if the configuration was self added by the framework
+ * This boolean is set once and never cleared. It is used
+ * so as we never loose track of who created the
+ * configuration in the first place.
+ * @hide
+ */
+ public boolean didSelfAdd;
+
+ /**
+ * peer WifiConfiguration this WifiConfiguration was added for
+ * @hide
+ */
+ public String peerWifiConfiguration;
+
+ /**
* @hide
* Indicate that a WifiConfiguration is temporary and should not be saved
* nor considered by AutoJoin.
@@ -513,6 +556,7 @@ public class WifiConfiguration implements Parcelable {
enterpriseConfig = new WifiEnterpriseConfig();
autoJoinStatus = AUTO_JOIN_ENABLED;
selfAdded = false;
+ didSelfAdd = false;
ephemeral = false;
mIpConfiguration = new IpConfiguration();
}
@@ -650,6 +694,10 @@ public class WifiConfiguration implements Parcelable {
sbuf.append(mIpConfiguration.toString());
+ if (selfAdded) sbuf.append("selfAdded");
+ if (creatorUid != 0) sbuf.append("uid=" + Integer.toString(creatorUid));
+
+
return sbuf.toString();
}
@@ -883,6 +931,7 @@ public class WifiConfiguration implements Parcelable {
networkId = source.networkId;
status = source.status;
disableReason = source.disableReason;
+ disableReason = source.disableReason;
SSID = source.SSID;
BSSID = source.BSSID;
FQDN = source.FQDN;
@@ -933,6 +982,11 @@ public class WifiConfiguration implements Parcelable {
}
lastFailure = source.lastFailure;
+ didSelfAdd = source.didSelfAdd;
+ lastConnectUid = source.lastConnectUid;
+ lastUpdateUid = source.lastUpdateUid;
+ creatorUid = source.creatorUid;
+ peerWifiConfiguration = source.peerWifiConfiguration;
}
}
@@ -972,6 +1026,10 @@ public class WifiConfiguration implements Parcelable {
dest.writeString(defaultGwMacAddress);
dest.writeInt(autoJoinStatus);
dest.writeInt(selfAdded ? 1 : 0);
+ dest.writeInt(didSelfAdd ? 1 : 0);
+ dest.writeInt(creatorUid);
+ dest.writeInt(lastConnectUid);
+ dest.writeInt(lastUpdateUid);
/*
TODO: should we write the cache results to the parcel?
if (scanResultCache != null) {
@@ -1017,6 +1075,10 @@ public class WifiConfiguration implements Parcelable {
config.defaultGwMacAddress = in.readString();
config.autoJoinStatus = in.readInt();
config.selfAdded = in.readInt() != 0;
+ config.didSelfAdd = in.readInt() != 0;
+ config.creatorUid = in.readInt();
+ config.lastConnectUid = in.readInt();
+ config.lastUpdateUid = in.readInt();
/*
TODO: should we write the cache results to the parcel?
boolean done = false;
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 9ea7027..3b65ca8 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -40,6 +40,7 @@ import java.util.concurrent.CountDownLatch;
* Get an instance of this class by calling
* {@link android.content.Context#getSystemService(String) Context.getSystemService(Context
* .WIFI_SCANNING_SERVICE)}.
+ * @hide
*/
public class WifiScanner {
@@ -72,16 +73,14 @@ public class WifiScanner {
public static final int REASON_INVALID_LISTENER = -2;
/** Invalid request */
public static final int REASON_INVALID_REQUEST = -3;
- /** Request conflicts with other scans that may be going on */
- public static final int REASON_CONFLICTING_REQUEST = -4;
/**
* Generic action callback invocation interface
* @hide
*/
public static interface ActionListener {
- public void onSuccess(Object result);
- public void onFailure(int reason, Object exception);
+ public void onSuccess();
+ public void onFailure(int reason, String description);
}
/**
@@ -193,58 +192,6 @@ public class WifiScanner {
}
- /** information element from beacon */
- public static class InformationElement {
- public int id;
- public byte[] bytes;
- }
-
- /** scan result with information elements from beacons */
- public static class FullScanResult implements Parcelable {
- public ScanResult result;
- public InformationElement informationElements[];
-
- /** Implement the Parcelable interface {@hide} */
- public int describeContents() {
- return 0;
- }
-
- /** Implement the Parcelable interface {@hide} */
- public void writeToParcel(Parcel dest, int flags) {
- result.writeToParcel(dest, flags);
- dest.writeInt(informationElements.length);
- for (int i = 0; i < informationElements.length; i++) {
- dest.writeInt(informationElements[i].id);
- dest.writeInt(informationElements[i].bytes.length);
- dest.writeByteArray(informationElements[i].bytes);
- }
- }
-
- /** Implement the Parcelable interface {@hide} */
- public static final Creator<FullScanResult> CREATOR =
- new Creator<FullScanResult>() {
- public FullScanResult createFromParcel(Parcel in) {
- FullScanResult result = new FullScanResult();
- result.result = ScanResult.CREATOR.createFromParcel(in);
- int n = in.readInt();
- result.informationElements = new InformationElement[n];
- for (int i = 0; i < n; i++) {
- result.informationElements[i] = new InformationElement();
- result.informationElements[i].id = in.readInt();
- int len = in.readInt();
- result.informationElements[i].bytes = new byte[len];
- in.readByteArray(result.informationElements[i].bytes);
- }
-
- return result;
- }
-
- public FullScanResult[] newArray(int size) {
- return new FullScanResult[size];
- }
- };
- }
-
/** @hide */
public static class ParcelableScanResults implements Parcelable {
public ScanResult mResults[];
@@ -305,7 +252,7 @@ public class WifiScanner {
/**
* reports full scan result for each access point found in scan
*/
- public void onFullResult(FullScanResult fullScanResult);
+ public void onFullResult(ScanResult fullScanResult);
}
/** @hide */
@@ -336,13 +283,12 @@ public class WifiScanner {
}
/**
* retrieves currently available scan results
- * @param flush {@code true} means flush all results
- * @param listener specifies which scan to cancel; must be same object as passed in {@link
- * #startBackgroundScan}
*/
- public void retrieveScanResults(boolean flush, ScanListener listener) {
+ public ScanResult[] getScanResults() {
validateChannel();
- sAsyncChannel.sendMessage(CMD_GET_SCAN_RESULTS, 0, getListenerKey(listener));
+ Message reply = sAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0);
+ ScanResult[] results = (ScanResult[]) reply.obj;
+ return results;
}
/** specifies information about an access point of interest */
@@ -490,7 +436,7 @@ public class WifiScanner {
}
/** interface to receive hotlist events on; use this on {@link #setHotlist} */
- public static interface HotlistListener extends ActionListener {
+ public static interface HotspotListener extends ActionListener {
/** indicates that access points were found by on going scans
* @param results list of scan results, one for each access point visible currently
*/
@@ -550,10 +496,10 @@ public class WifiScanner {
* @param hotspots access points of interest
* @param apLostThreshold number of scans needed to indicate that AP is lost
* @param listener object provided to report events on; this object must be unique and must
- * also be provided on {@link #resetHotlist}
+ * also be provided on {@link #stopTrackingHotspots}
*/
- public void setHotlist(HotspotInfo[] hotspots,
- int apLostThreshold, HotlistListener listener) {
+ public void startTrackingHotspots(HotspotInfo[] hotspots,
+ int apLostThreshold, HotspotListener listener) {
validateChannel();
HotlistSettings settings = new HotlistSettings();
settings.hotspotInfos = hotspots;
@@ -562,9 +508,9 @@ public class WifiScanner {
/**
* remove tracking of interesting access points
- * @param listener same object provided in {@link #setHotlist}
+ * @param listener same object provided in {@link #startTrackingHotspots}
*/
- public void resetHotlist(HotlistListener listener) {
+ public void stopTrackingHotspots(HotspotListener listener) {
validateChannel();
sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, removeListener(listener));
}
@@ -769,10 +715,10 @@ public class WifiScanner {
switch (msg.what) {
/* ActionListeners grouped together */
case CMD_OP_SUCCEEDED :
- ((ActionListener) listener).onSuccess(msg.obj);
+ ((ActionListener) listener).onSuccess();
break;
case CMD_OP_FAILED :
- ((ActionListener) listener).onFailure(msg.arg1, msg.obj);
+ ((ActionListener) listener).onFailure(msg.arg1, (String)msg.obj);
removeListener(msg.arg2);
break;
case CMD_SCAN_RESULT :
@@ -780,14 +726,14 @@ public class WifiScanner {
((ParcelableScanResults) msg.obj).getResults());
return;
case CMD_FULL_SCAN_RESULT :
- FullScanResult result = (FullScanResult) msg.obj;
+ ScanResult result = (ScanResult) msg.obj;
((ScanListener) listener).onFullResult(result);
return;
case CMD_PERIOD_CHANGED:
((ScanListener) listener).onPeriodChanged(msg.arg1);
return;
case CMD_AP_FOUND:
- ((HotlistListener) listener).onFound(
+ ((HotspotListener) listener).onFound(
((ParcelableScanResults) msg.obj).getResults());
return;
case CMD_WIFI_CHANGE_DETECTED:
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
index f809abf..54ac71e 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
@@ -20,10 +20,13 @@ import android.net.wifi.WifiEnterpriseConfig;
import android.os.Parcelable;
import android.os.Parcel;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.Set;
+import java.util.List;
import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
+
/**
* A class representing a Wi-Fi Passpoint credential.
@@ -32,64 +35,76 @@ import java.util.Map;
public class WifiPasspointCredential implements Parcelable {
private final static String TAG = "PasspointCredential";
- private String mWifiTreePath;
- private String mWifiSPFQDN;
+ private final static boolean DBG = true;
+
+ /** Wi-Fi nodes**/
+ private String mWifiSpFqdn;
+
+ /** PerProviderSubscription nodes **/
private String mCredentialName;
- private String mUpdateIdentifier;
+
+ /** SubscriptionUpdate nodes **/
+ private String mSubscriptionUpdateInterval;
private String mSubscriptionUpdateMethod;
- private WifiEnterpriseConfig mEnterpriseConfig;
+ private String mSubscriptionUpdateRestriction;
+ private String mSubscriptionUpdateURI;
+ private String mSubscriptionUpdateUsername;
+ private String mSubscriptionUpdatePassword;
+
+ /** HomeSP nodes **/
+ private String mHomeSpFqdn;
+ private String mFriendlyName;
+ private Collection<WifiPasspointDmTree.HomeOIList> mHomeOIList;
+ private Collection<WifiPasspointDmTree.OtherHomePartners> mOtherHomePartnerList;
+
+ /** SubscriptionParameters nodes**/
+ private String mCreationDate;
+ private String mExpirationDate;
+
+ /** Credential nodes **/
private String mType;
private String mInnerMethod;
private String mCertType;
private String mCertSha256Fingerprint;
+ private String mUpdateIdentifier;
private String mUsername;
private String mPasswd;
+ private String mRealm;
private String mImsi;
private String mMcc;
private String mMnc;
private String mCaRootCert;
- private String mRealm;
- private int mPriority; //User preferred priority; The smaller, the higher
- private boolean mUserPreferred = false;
- private String mHomeSpFqdn;
- private String mFriendlyName;
- private String mOtherhomepartnerFqdn;
private String mClientCert;
- private String mCreationDate;
- private String mExpirationDate;
-
- private String mSubscriptionDMAccUsername;
- private String mSubscriptionDMAccPassword;
- private String mSubscriptionUpdateInterval;
+ private boolean mCheckAaaServerCertStatus;
- private String mPolicyUpdateURI;
+ /** Policy nodes **/
+ private String mPolicyUpdateUri;
private String mPolicyUpdateInterval;
- private String mPolicyDMAccUsername;
- private String mPolicyDMAccPassword;
+ private String mPolicyUpdateUsername;
+ private String mPolicyUpdatePassword;
private String mPolicyUpdateRestriction;
private String mPolicyUpdateMethod;
-
private Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> mPreferredRoamingPartnerList;
- private Collection<WifiPasspointDmTree.HomeOIList> mHomeOIList;
private Collection<WifiPasspointDmTree.MinBackhaulThresholdNetwork> mMinBackhaulThresholdNetwork;
- private Collection<WifiPasspointDmTree.RequiredProtoPortTuple> mRequiredProtoPortTuple;
private Collection<WifiPasspointDmTree.SPExclusionList> mSpExclusionList;
+ private Collection<WifiPasspointDmTree.RequiredProtoPortTuple> mRequiredProtoPortTuple;
private String mMaxBssLoad;
- private boolean mIsMachineRemediation;
-
- private String mAAACertURL;
- private String mAAASha256Fingerprint;
+ /** CrednetialPriority node **/
+ private int mCrednetialPriority;
- private String mSubscriptionUpdateRestriction;
- private String mSubscriptionUpdateURI;
+ /** AAAServerTrustRoot nodes **/
+ private String mAaaCertUrl;
+ private String mAaaSha256Fingerprint;
- private boolean mCheckAaaServerCertStatus;
+ /** Others **/
+ private boolean mIsMachineRemediation;
+ private boolean mUserPreferred = false;
+ private String mWifiTreePath;
+ private WifiEnterpriseConfig mEnterpriseConfig;
/** @hide */
- public WifiPasspointCredential() {
-
- }
+ public WifiPasspointCredential() {}
/**
* Constructor
@@ -114,84 +129,6 @@ public class WifiPasspointCredential implements Parcelable {
public WifiPasspointCredential(String type,
String caroot,
String clientcert,
- WifiPasspointDmTree.SpFqdn sp,
- WifiPasspointDmTree.CredentialInfo credinfo) {
-
- if (credinfo == null) {
- return;
- }
-
- mType = type;
- mCaRootCert = caroot;
- mClientCert = clientcert;
-
- mWifiSPFQDN = sp.nodeName;
- mUpdateIdentifier = sp.perProviderSubscription.UpdateIdentifier;
-
- mCredentialName = credinfo.nodeName;
- Set set = credinfo.homeSP.otherHomePartners.entrySet();
- Iterator i = set.iterator();
- if (i.hasNext()) {
- Map.Entry entry3 = (Map.Entry) i.next();
- WifiPasspointDmTree.OtherHomePartners ohp = (WifiPasspointDmTree.OtherHomePartners) entry3.getValue();
- mOtherhomepartnerFqdn = ohp.FQDN;
- }
-
- set = credinfo.aAAServerTrustRoot.entrySet();
- i = set.iterator();
- if (i.hasNext()) {
- Map.Entry entry3 = (Map.Entry) i.next();
- WifiPasspointDmTree.AAAServerTrustRoot aaa = (WifiPasspointDmTree.AAAServerTrustRoot) entry3.getValue();
- mAAACertURL = aaa.CertURL;
- mAAASha256Fingerprint = aaa.CertSHA256Fingerprint;
- }
-
- mCertType = credinfo.credential.digitalCertificate.CertificateType;
- mCertSha256Fingerprint = credinfo.credential.digitalCertificate.CertSHA256Fingerprint;
- mUsername = credinfo.credential.usernamePassword.Username;
- mPasswd = credinfo.credential.usernamePassword.Password;
- mIsMachineRemediation = credinfo.credential.usernamePassword.MachineManaged;
- mInnerMethod = credinfo.credential.usernamePassword.eAPMethod.InnerMethod;
- mImsi = credinfo.credential.sim.IMSI;
- mCreationDate = credinfo.credential.CreationDate;
- mExpirationDate = credinfo.credential.ExpirationDate;
- mRealm = credinfo.credential.Realm;
-
- if (credinfo.credentialPriority == null) {
- credinfo.credentialPriority = "128";
- }
- mPriority = Integer.parseInt(credinfo.credentialPriority);
-
- mHomeSpFqdn = credinfo.homeSP.FQDN;
-
- mSubscriptionUpdateInterval = credinfo.subscriptionUpdate.UpdateInterval;
- mSubscriptionUpdateMethod = credinfo.subscriptionUpdate.UpdateMethod;
- mSubscriptionUpdateRestriction = credinfo.subscriptionUpdate.Restriction;
- mSubscriptionUpdateURI = credinfo.subscriptionUpdate.URI;
- mSubscriptionDMAccUsername = credinfo.subscriptionUpdate.usernamePassword.Username;
- mSubscriptionDMAccPassword = credinfo.subscriptionUpdate.usernamePassword.Password;
-
- mPolicyUpdateURI = credinfo.policy.policyUpdate.URI;
- mPolicyUpdateInterval = credinfo.policy.policyUpdate.UpdateInterval;
- mPolicyDMAccUsername = credinfo.policy.policyUpdate.usernamePassword.Username;
- mPolicyDMAccPassword = credinfo.policy.policyUpdate.usernamePassword.Password;
- mPolicyUpdateRestriction = credinfo.policy.policyUpdate.Restriction;
- mPolicyUpdateMethod = credinfo.policy.policyUpdate.UpdateMethod;
- mPreferredRoamingPartnerList = credinfo.policy.preferredRoamingPartnerList.values();
- mMinBackhaulThresholdNetwork = credinfo.policy.minBackhaulThreshold.values();
- mRequiredProtoPortTuple = credinfo.policy.requiredProtoPortTuple.values();
- mMaxBssLoad = credinfo.policy.maximumBSSLoadValue;
- mSpExclusionList = credinfo.policy.sPExclusionList.values();
-
- mHomeOIList = credinfo.homeSP.homeOIList.values();
- mFriendlyName = credinfo.homeSP.FriendlyName;
- mCheckAaaServerCertStatus = credinfo.credential.CheckAAAServerCertStatus;
- }
-
- /** @hide */
- public WifiPasspointCredential(String type,
- String caroot,
- String clientcert,
String mcc,
String mnc,
WifiPasspointDmTree.SpFqdn sp,
@@ -205,25 +142,19 @@ public class WifiPasspointCredential implements Parcelable {
mCaRootCert = caroot;
mClientCert = clientcert;
- mWifiSPFQDN = sp.nodeName;
+ mWifiSpFqdn = sp.nodeName;
mUpdateIdentifier = sp.perProviderSubscription.UpdateIdentifier;
mCredentialName = credinfo.nodeName;
- Set set = credinfo.homeSP.otherHomePartners.entrySet();
- Iterator i = set.iterator();
- if (i.hasNext()) {
- Map.Entry entry3 = (Map.Entry) i.next();
- WifiPasspointDmTree.OtherHomePartners ohp = (WifiPasspointDmTree.OtherHomePartners) entry3.getValue();
- mOtherhomepartnerFqdn = ohp.FQDN;
- }
+ mOtherHomePartnerList = credinfo.homeSP.otherHomePartners.values();
- set = credinfo.aAAServerTrustRoot.entrySet();
- i = set.iterator();
+ Set set = credinfo.aAAServerTrustRoot.entrySet();
+ Iterator i = set.iterator();
if (i.hasNext()) {
Map.Entry entry3 = (Map.Entry) i.next();
WifiPasspointDmTree.AAAServerTrustRoot aaa = (WifiPasspointDmTree.AAAServerTrustRoot) entry3.getValue();
- mAAACertURL = aaa.CertURL;
- mAAASha256Fingerprint = aaa.CertSHA256Fingerprint;
+ mAaaCertUrl = aaa.CertURL;
+ mAaaSha256Fingerprint = aaa.CertSHA256Fingerprint;
}
mCertType = credinfo.credential.digitalCertificate.CertificateType;
@@ -240,22 +171,24 @@ public class WifiPasspointCredential implements Parcelable {
mRealm = credinfo.credential.Realm;
if (credinfo.credentialPriority == null) {
- credinfo.credentialPriority = "128";
+ mCrednetialPriority = 128;
+ } else {
+ mCrednetialPriority = Integer.parseInt(credinfo.credentialPriority);
}
- mPriority = Integer.parseInt(credinfo.credentialPriority);
mHomeSpFqdn = credinfo.homeSP.FQDN;
+ mSubscriptionUpdateInterval = credinfo.subscriptionUpdate.UpdateInterval;
mSubscriptionUpdateMethod = credinfo.subscriptionUpdate.UpdateMethod;
mSubscriptionUpdateRestriction = credinfo.subscriptionUpdate.Restriction;
mSubscriptionUpdateURI = credinfo.subscriptionUpdate.URI;
- mSubscriptionDMAccUsername = credinfo.subscriptionUpdate.usernamePassword.Username;
- mSubscriptionDMAccPassword = credinfo.subscriptionUpdate.usernamePassword.Password;
+ mSubscriptionUpdateUsername = credinfo.subscriptionUpdate.usernamePassword.Username;
+ mSubscriptionUpdatePassword = credinfo.subscriptionUpdate.usernamePassword.Password;
- mPolicyUpdateURI = credinfo.policy.policyUpdate.URI;
+ mPolicyUpdateUri = credinfo.policy.policyUpdate.URI;
mPolicyUpdateInterval = credinfo.policy.policyUpdate.UpdateInterval;
- mPolicyDMAccUsername = credinfo.policy.policyUpdate.usernamePassword.Username;
- mPolicyDMAccPassword = credinfo.policy.policyUpdate.usernamePassword.Password;
+ mPolicyUpdateUsername = credinfo.policy.policyUpdate.usernamePassword.Username;
+ mPolicyUpdatePassword = credinfo.policy.policyUpdate.usernamePassword.Password;
mPolicyUpdateRestriction = credinfo.policy.policyUpdate.Restriction;
mPolicyUpdateMethod = credinfo.policy.policyUpdate.UpdateMethod;
mPreferredRoamingPartnerList = credinfo.policy.preferredRoamingPartnerList.values();
@@ -266,6 +199,7 @@ public class WifiPasspointCredential implements Parcelable {
mHomeOIList = credinfo.homeSP.homeOIList.values();
mFriendlyName = credinfo.homeSP.FriendlyName;
+ mCheckAaaServerCertStatus = credinfo.credential.CheckAAAServerCertStatus;
}
/** @hide */
@@ -284,8 +218,8 @@ public class WifiPasspointCredential implements Parcelable {
}
/** @hide */
- public String getWifiSPFQDN() {
- return mWifiSPFQDN;
+ public String getWifiSpFqdn() {
+ return mWifiSpFqdn;
}
/** @hide */
@@ -294,7 +228,7 @@ public class WifiPasspointCredential implements Parcelable {
}
/** @hide */
- public String getEapMethodStr() {
+ public String getType() {
return mType;
}
@@ -384,14 +318,14 @@ public class WifiPasspointCredential implements Parcelable {
return 0;
}
- return mPriority;
+ return mCrednetialPriority;
}
/**
* Get the fully qualified domain name (FQDN) of this Passpoint credential.
* @return FQDN
*/
- public String getFqdn() {
+ public String getHomeSpFqdn() {
return mHomeSpFqdn;
}
@@ -405,23 +339,23 @@ public class WifiPasspointCredential implements Parcelable {
/** @hide */
- public String getOtherhomepartners() {
- return mOtherhomepartnerFqdn;
+ public Collection<WifiPasspointDmTree.OtherHomePartners> getOtherHomePartnerList() {
+ return mOtherHomePartnerList;
}
/** @hide */
- public String getSubscriptionDMAccUsername() {
- return mSubscriptionDMAccUsername;
+ public String getSubscriptionUpdateUsername() {
+ return mSubscriptionUpdateUsername;
}
/** @hide */
- public String getSubscriptionDMAccPassword() {
- return mSubscriptionDMAccPassword;
+ public String getSubscriptionUpdatePassword() {
+ return mSubscriptionUpdatePassword;
}
/** @hide */
- public String getPolicyUpdateURI() {
- return mPolicyUpdateURI;
+ public String getPolicyUpdateUri() {
+ return mPolicyUpdateUri;
}
/** @hide */
@@ -430,13 +364,13 @@ public class WifiPasspointCredential implements Parcelable {
}
/** @hide */
- public String getPolicyDMAccUsername() {
- return mPolicyDMAccUsername;
+ public String getPolicyUpdateUsername() {
+ return mPolicyUpdateUsername;
}
/** @hide */
- public String getPolicyDMAccPassword() {
- return mPolicyDMAccPassword;
+ public String getPolicyUpdatePassword() {
+ return mPolicyUpdatePassword;
}
/** @hide */
@@ -465,12 +399,12 @@ public class WifiPasspointCredential implements Parcelable {
}
/** @hide */
- public Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> getPrpList() {
+ public Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> getPreferredRoamingPartnerList() {
return mPreferredRoamingPartnerList;
}
/** @hide */
- public Collection<WifiPasspointDmTree.HomeOIList> getHomeOIList() {
+ public Collection<WifiPasspointDmTree.HomeOIList> getHomeOiList() {
return mHomeOIList;
}
@@ -495,13 +429,13 @@ public class WifiPasspointCredential implements Parcelable {
}
/** @hide */
- public String getAAACertURL() {
- return mAAACertURL;
+ public String getAaaCertUrl() {
+ return mAaaCertUrl;
}
/** @hide */
- public String getAAASha256Fingerprint() {
- return mAAASha256Fingerprint;
+ public String getAaaSha256Fingerprint() {
+ return mAaaSha256Fingerprint;
}
/** @hide */
@@ -578,72 +512,75 @@ public class WifiPasspointCredential implements Parcelable {
StringBuffer sb = new StringBuffer();
String none = "<none>";
- sb.append(", UpdateIdentifier: ")
- .append(mUpdateIdentifier == null ? none : mUpdateIdentifier).
- append(", SubscriptionUpdateMethod: ")
- .append(mSubscriptionUpdateMethod == null ? none : mSubscriptionUpdateMethod).
- append(", Type: ").append(mType == null ? none : mType).
- append(", Username: ").append(mUsername == null ? none : mUsername).
- append(", Passwd: ").append(mPasswd == null ? none : mPasswd).
- append(", SubDMAccUsername: ")
- .append(mSubscriptionDMAccUsername == null ? none : mSubscriptionDMAccUsername).
- append(", SubDMAccPassword: ")
- .append(mSubscriptionDMAccPassword == null ? none : mSubscriptionDMAccPassword).
- append(", PolDMAccUsername: ")
- .append(mPolicyDMAccUsername == null ? none : mPolicyDMAccUsername).
- append(", PolDMAccPassword: ")
- .append(mPolicyDMAccPassword == null ? none : mPolicyDMAccPassword).
- append(", Imsi: ").append(mImsi == null ? none : mImsi).
- append(", Mcc: ").append(mMcc == null ? none : mMcc).
- append(", Mnc: ").append(mMnc == null ? none : mMnc).
- append(", CaRootCert: ").append(mCaRootCert == null ? none : mCaRootCert).
- append(", Realm: ").append(mRealm == null ? none : mRealm).
- append(", Priority: ").append(mPriority).
- append(", Fqdn: ").append(mHomeSpFqdn == null ? none : mHomeSpFqdn).
- append(", Otherhomepartners: ")
- .append(mOtherhomepartnerFqdn == null ? none : mOtherhomepartnerFqdn).
- append(", ExpirationDate: ")
- .append(mExpirationDate == null ? none : mExpirationDate).
- append(", MaxBssLoad: ").append(mMaxBssLoad == null ? none : mMaxBssLoad).
- append(", SPExclusionList: ").append(mSpExclusionList);
-
- if (mPreferredRoamingPartnerList != null) {
- sb.append("PreferredRoamingPartnerList:");
- for (WifiPasspointDmTree.PreferredRoamingPartnerList prpListItem : mPreferredRoamingPartnerList) {
- sb.append("[fqdnmatch:").append(prpListItem.FQDN_Match).
- append(", priority:").append(prpListItem.Priority).
- append(", country:").append(prpListItem.Country).append("]");
+ if (!DBG) {
+ sb.append(none);
+ } else {
+ sb.append(", UpdateIdentifier: ")
+ .append(mUpdateIdentifier == null ? none : mUpdateIdentifier)
+ .append(", SubscriptionUpdateMethod: ")
+ .append(mSubscriptionUpdateMethod == null ? none : mSubscriptionUpdateMethod)
+ .append(", Type: ").append(mType == null ? none : mType)
+ .append(", Username: ").append(mUsername == null ? none : mUsername)
+ .append(", Passwd: ").append(mPasswd == null ? none : mPasswd)
+ .append(", SubDMAccUsername: ")
+ .append(mSubscriptionUpdateUsername == null ? none : mSubscriptionUpdateUsername)
+ .append(", SubDMAccPassword: ")
+ .append(mSubscriptionUpdatePassword == null ? none : mSubscriptionUpdatePassword)
+ .append(", PolDMAccUsername: ")
+ .append(mPolicyUpdateUsername == null ? none : mPolicyUpdateUsername)
+ .append(", PolDMAccPassword: ")
+ .append(mPolicyUpdatePassword == null ? none : mPolicyUpdatePassword)
+ .append(", Imsi: ").append(mImsi == null ? none : mImsi)
+ .append(", Mcc: ").append(mMcc == null ? none : mMcc)
+ .append(", Mnc: ").append(mMnc == null ? none : mMnc)
+ .append(", CaRootCert: ").append(mCaRootCert == null ? none : mCaRootCert)
+ .append(", Realm: ").append(mRealm == null ? none : mRealm)
+ .append(", Priority: ").append(mCrednetialPriority)
+ .append(", Fqdn: ").append(mHomeSpFqdn == null ? none : mHomeSpFqdn)
+ .append(", Otherhomepartners: ")
+ .append(mOtherHomePartnerList == null ? none : mOtherHomePartnerList)
+ .append(", ExpirationDate: ")
+ .append(mExpirationDate == null ? none : mExpirationDate)
+ .append(", MaxBssLoad: ").append(mMaxBssLoad == null ? none : mMaxBssLoad)
+ .append(", SPExclusionList: ").append(mSpExclusionList);
+
+ if (mPreferredRoamingPartnerList != null) {
+ sb.append("PreferredRoamingPartnerList:");
+ for (WifiPasspointDmTree.PreferredRoamingPartnerList prpListItem : mPreferredRoamingPartnerList) {
+ sb.append("[fqdnmatch:").append(prpListItem.FQDN_Match).
+ append(", priority:").append(prpListItem.Priority).
+ append(", country:").append(prpListItem.Country).append("]");
+ }
}
- }
- if (mHomeOIList != null) {
- sb.append("HomeOIList:");
- for (WifiPasspointDmTree.HomeOIList HomeOIListItem : mHomeOIList) {
- sb.append("[HomeOI:").append(HomeOIListItem.HomeOI).
- append(", HomeOIRequired:").append(HomeOIListItem.HomeOIRequired).
- append("]");
+ if (mHomeOIList != null) {
+ sb.append("HomeOIList:");
+ for (WifiPasspointDmTree.HomeOIList HomeOIListItem : mHomeOIList) {
+ sb.append("[HomeOI:").append(HomeOIListItem.HomeOI).
+ append(", HomeOIRequired:").append(HomeOIListItem.HomeOIRequired).
+ append("]");
+ }
}
- }
- if (mMinBackhaulThresholdNetwork != null) {
- sb.append("BackHaulThreshold:");
- for (WifiPasspointDmTree.MinBackhaulThresholdNetwork BhtListItem : mMinBackhaulThresholdNetwork) {
- sb.append("[networkType:").append(BhtListItem.NetworkType).
- append(", dlBandwidth:").append(BhtListItem.DLBandwidth).
- append(", ulBandwidth:").append(BhtListItem.ULBandwidth).
- append("]");
+ if (mMinBackhaulThresholdNetwork != null) {
+ sb.append("BackHaulThreshold:");
+ for (WifiPasspointDmTree.MinBackhaulThresholdNetwork BhtListItem : mMinBackhaulThresholdNetwork) {
+ sb.append("[networkType:").append(BhtListItem.NetworkType).
+ append(", dlBandwidth:").append(BhtListItem.DLBandwidth).
+ append(", ulBandwidth:").append(BhtListItem.ULBandwidth).
+ append("]");
+ }
}
- }
- if (mRequiredProtoPortTuple != null) {
- sb.append("WifiMORequiredProtoPortTupleList:");
- for (WifiPasspointDmTree.RequiredProtoPortTuple RpptListItem : mRequiredProtoPortTuple) {
- sb.append("[IPProtocol:").append(RpptListItem.IPProtocol).
- append(", PortNumber:").append(RpptListItem.PortNumber).
- append("]");
+ if (mRequiredProtoPortTuple != null) {
+ sb.append("WifiMORequiredProtoPortTupleList:");
+ for (WifiPasspointDmTree.RequiredProtoPortTuple RpptListItem : mRequiredProtoPortTuple) {
+ sb.append("[IPProtocol:").append(RpptListItem.IPProtocol).
+ append(", PortNumber:").append(RpptListItem.PortNumber).
+ append("]");
+ }
}
}
-
return sb.toString();
}
@@ -654,19 +591,22 @@ public class WifiPasspointCredential implements Parcelable {
/** Implement the Parcelable interface {@hide} */
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mWifiSpFqdn);
+ dest.writeString(mCredentialName);
dest.writeString(mType);
- dest.writeString(mUsername);
- dest.writeString(mPasswd);
- dest.writeString(mImsi);
- dest.writeString(mMcc);
- dest.writeString(mMnc);
- dest.writeString(mCaRootCert);
- dest.writeString(mRealm);
- dest.writeInt(mPriority);
+ dest.writeInt(mCrednetialPriority);
dest.writeString(mHomeSpFqdn);
- dest.writeString(mOtherhomepartnerFqdn);
- dest.writeString(mClientCert);
- dest.writeString(mExpirationDate);
+ dest.writeString(mRealm);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void readFromParcel(Parcel in) {
+ mWifiSpFqdn = in.readString();
+ mCredentialName = in.readString();
+ mType = in.readString();
+ mCrednetialPriority = in.readInt();
+ mHomeSpFqdn = in.readString();
+ mRealm = in.readString();
}
/** Implement the Parcelable interface {@hide} */
@@ -674,19 +614,12 @@ public class WifiPasspointCredential implements Parcelable {
new Creator<WifiPasspointCredential>() {
public WifiPasspointCredential createFromParcel(Parcel in) {
WifiPasspointCredential pc = new WifiPasspointCredential();
+ pc.mWifiSpFqdn = in.readString();
+ pc.mCredentialName = in.readString();
pc.mType = in.readString();
- pc.mUsername = in.readString();
- pc.mPasswd = in.readString();
- pc.mImsi = in.readString();
- pc.mMcc = in.readString();
- pc.mMnc = in.readString();
- pc.mCaRootCert = in.readString();
- pc.mRealm = in.readString();
- pc.mPriority = in.readInt();
+ pc.mCrednetialPriority = in.readInt();
pc.mHomeSpFqdn = in.readString();
- pc.mOtherhomepartnerFqdn = in.readString();
- pc.mClientCert = in.readString();
- pc.mExpirationDate = in.readString();
+ pc.mRealm = in.readString();
return pc;
}
@@ -699,9 +632,9 @@ public class WifiPasspointCredential implements Parcelable {
public int compareTo(WifiPasspointCredential another) {
//The smaller the higher
- if (mPriority < another.mPriority) {
+ if (mCrednetialPriority < another.mCrednetialPriority) {
return -1;
- } else if (mPriority == another.mPriority) {
+ } else if (mCrednetialPriority == another.mCrednetialPriority) {
return this.mType.compareTo(another.mType);
} else {
return 1;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java
index 9ff1973..bbf5fc6 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java
@@ -25,19 +25,17 @@ import java.util.HashMap;
/**
* Required Mobile Device Management Tree Structure
*
- * +----------+
- * | ./(Root) |
- * +----+-----+
- * |
- * +---------+ | +---------+ +---------+
- * | DevInfo |-----------+---------| Wi-Fi |---|SP FQDN* |
- * +---------+ | +---------+ +---------+
- * +---------+ |
- * |DevDetail|-----------+
- * +---------+
- *
- * For example,
- * ./Wi-Fi/wi-fi.org/PerproviderSubscription/Cred01/Policy/PreferredRoamingPartnerList/Roa01/FQDN_Math
+ * +----------+
+ * | ./(Root) |
+ * +----+-----+
+ * |
+ * +---------+ | +---------+ +---------+
+ * | DevInfo |-----------+---------| Wi-Fi |--|SP FQDN* |
+ * +---------+ | +---------+ +---------+
+ * +---------+ | |
+ * |DevDetail|-----------+ +-----------------------+
+ * +---------+ |PerproviderSubscription|--<X>+
+ * +-----------------------+
*
* This class contains all nodes start from Wi-Fi
* @hide
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
index 99bea2f..5ef1bf9 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
@@ -88,68 +88,174 @@ public class WifiPasspointInfo implements Parcelable {
CONNECTION_CAPABILITY |
OSU_PROVIDER;
- /** TODO doc */
- public String bssid;
- /** TODO doc */
- public String venueName;
+ public static class WanMetrics {
+ public static final int STATUS_RESERVED = 0;
+ public static final int STATUS_UP = 1;
+ public static final int STATUS_DOWN = 2;
+ public static final int STATUS_TEST = 3;
+
+ public int wanInfo;
+ public long downlinkSpeed;
+ public long uplinkSpeed;
+ public int downlinkLoad;
+ public int uplinkLoad;
+ public int lmd;
+
+ public int getLinkStatus() {
+ return wanInfo & 0x3;
+ }
- /** TODO doc */
- public String networkAuthType;
+ public boolean getSymmetricLink() {
+ return (wanInfo & (1 << 2)) != 0;
+ }
- /** TODO doc */
- public String roamingConsortium;
+ public boolean getAtCapacity() {
+ return (wanInfo & (1 << 3)) != 0;
+ }
- /** TODO doc */
- public String ipAddrTypeAvaibility;
+ @Override
+ public String toString() {
+ return wanInfo + "," + downlinkSpeed + "," + uplinkSpeed + "," +
+ downlinkLoad + "," + uplinkLoad + "," + lmd;
+ }
+ }
- /** TODO doc */
- public String naiRealm;
+ public static class IpProtoPort {
+ public static final int STATUS_CLOSED = 0;
+ public static final int STATUS_OPEN = 1;
+ public static final int STATUS_UNKNOWN = 2;
- /** TODO doc */
- public String cellularNetwork;
+ public int proto;
+ public int port;
+ public int status;
- /** TODO doc */
- public String domainName;
+ @Override
+ public String toString() {
+ return proto + "," + port + "," + status;
+ }
+ }
- /** TODO doc */
- public String operatorFriendlyName;
+ public static class NetworkAuthType {
+ public static final int TYPE_TERMS_AND_CONDITION = 0;
+ public static final int TYPE_ONLINE_ENROLLMENT = 1;
+ public static final int TYPE_HTTP_REDIRECTION = 2;
+ public static final int TYPE_DNS_REDIRECTION = 3;
- /** TODO doc */
- public String wanMetrics;
+ public int type;
+ public String redirectUrl;
- /** TODO doc */
- public String connectionCapability;
+ @Override
+ public String toString() {
+ return type + "," + redirectUrl;
+ }
+ }
- /** TODO doc */
- public List<WifiPasspointOsuProvider> osuProviderList;
+ public static class IpAddressType {
+ public static final int IPV6_NOT_AVAILABLE = 0;
+ public static final int IPV6_AVAILABLE = 1;
+ public static final int IPV6_UNKNOWN = 2;
+
+ public static final int IPV4_NOT_AVAILABLE = 0;
+ public static final int IPV4_PUBLIC = 1;
+ public static final int IPV4_PORT_RESTRICTED = 2;
+ public static final int IPV4_SINGLE_NAT = 3;
+ public static final int IPV4_DOUBLE_NAT = 4;
+ public static final int IPV4_PORT_RESTRICTED_SINGLE_NAT = 5;
+ public static final int IPV4_PORT_RESTRICTED_DOUBLE_NAT = 6;
+ public static final int IPV4_PORT_UNKNOWN = 7;
+
+ private static final int NULL_VALUE = -1;
+
+ public int availability;
+
+ public int getIpv6Availability() {
+ return availability & 0x3;
+ }
+
+ public int getIpv4Availability() {
+ return (availability & 0xFF) >> 2;
+ }
+
+ @Override
+ public String toString() {
+ return getIpv6Availability() + "," + getIpv4Availability();
+ }
+ }
+
+ public static class NaiRealm {
+ public static final int ENCODING_RFC4282 = 0;
+ public static final int ENCODING_UTF8 = 1;
+
+ public int encoding;
+ public String realm;
- /** default constructor @hide */
- public WifiPasspointInfo() {
- // osuProviderList = new ArrayList<OsuProvider>();
+ @Override
+ public String toString() {
+ return encoding + "," + realm;
+ }
}
- /** copy constructor @hide */
- public WifiPasspointInfo(WifiPasspointInfo source) {
- // TODO
- bssid = source.bssid;
- venueName = source.venueName;
- networkAuthType = source.networkAuthType;
- roamingConsortium = source.roamingConsortium;
- ipAddrTypeAvaibility = source.ipAddrTypeAvaibility;
- naiRealm = source.naiRealm;
- cellularNetwork = source.cellularNetwork;
- domainName = source.domainName;
- operatorFriendlyName = source.operatorFriendlyName;
- wanMetrics = source.wanMetrics;
- connectionCapability = source.connectionCapability;
- if (source.osuProviderList != null) {
- osuProviderList = new ArrayList<WifiPasspointOsuProvider>();
- for (WifiPasspointOsuProvider osu : source.osuProviderList)
- osuProviderList.add(new WifiPasspointOsuProvider(osu));
+ public static class CellularNetwork {
+ public byte[] rawData;
+
+ public int getMnc() {
+ // TODO
+ return 0;
+ }
+
+ public int getMcc() {
+ // TODO
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ if (rawData == null) return null;
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < rawData.length; i++)
+ sb.append(String.format("%02X", rawData[i]));
+ return sb.toString();
}
+
}
+ /** BSSID */
+ public String bssid;
+
+ /** venue name */
+ public String venueName;
+
+ /** list of network authentication types */
+ public List<NetworkAuthType> networkAuthType;
+
+ /** list of roaming consortium OIs */
+ public List<String> roamingConsortium;
+
+ /** IP address availability */
+ public IpAddressType ipAddrTypeAvailability;
+
+ /** NAI realm */
+ public List<NaiRealm> naiRealm;
+
+ /** 3GPP cellular network */
+ public CellularNetwork cellularNetwork;
+
+ /** fully qualified domain name (FQDN) */
+ public List<String> domainName;
+
+ /** HS 2.0 operator friendly name */
+ public String operatorFriendlyName;
+
+ /** HS 2.0 wan metrics */
+ public WanMetrics wanMetrics;
+
+ /** HS 2.0 list of IP proto port */
+ public List<IpProtoPort> connectionCapability;
+
+ /** HS 2.0 list of OSU providers */
+ public List<WifiPasspointOsuProvider> osuProviderList;
+
/**
* Convert mask to ANQP subtypes, for supplicant command use.
*
@@ -193,46 +299,149 @@ public class WifiPasspointInfo implements Parcelable {
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
+
sb.append("BSSID: ").append(bssid);
+
if (venueName != null)
- sb.append(" venueName: ").append(venueName);
- if (networkAuthType != null)
- sb.append(" networkAuthType: ").append(networkAuthType);
- if (roamingConsortium != null)
- sb.append(" roamingConsortium: ").append(roamingConsortium);
- if (ipAddrTypeAvaibility != null)
- sb.append(" ipAddrTypeAvaibility: ").append(ipAddrTypeAvaibility);
- if (naiRealm != null)
- sb.append(" naiRealm: ").append(naiRealm);
+ sb.append(" venueName: ").append(venueName.replace("\n", "\\n"));
+
+ if (networkAuthType != null) {
+ sb.append(" networkAuthType: ");
+ for (NetworkAuthType auth : networkAuthType)
+ sb.append("(").append(auth.toString()).append(")");
+ }
+
+ if (roamingConsortium != null) {
+ sb.append(" roamingConsortium: ");
+ for (String oi : roamingConsortium)
+ sb.append("(").append(oi).append(")");
+ }
+
+ if (ipAddrTypeAvailability != null) {
+ sb.append(" ipAddrTypeAvaibility: ").append("(")
+ .append(ipAddrTypeAvailability.toString()).append(")");
+ }
+
+ if (naiRealm != null) {
+ sb.append(" naiRealm: ");
+ for (NaiRealm realm : naiRealm)
+ sb.append("(").append(realm.toString()).append(")");
+ }
+
if (cellularNetwork != null)
- sb.append(" cellularNetwork: ").append(cellularNetwork);
- if (domainName != null)
- sb.append(" domainName: ").append(domainName);
+ sb.append(" cellularNetwork: ").append("(")
+ .append(cellularNetwork.toString()).append(")");
+
+ if (domainName != null) {
+ sb.append(" domainName: ");
+ for (String fqdn : domainName)
+ sb.append("(").append(fqdn).append(")");
+ }
+
if (operatorFriendlyName != null)
- sb.append(" operatorFriendlyName: ").append(operatorFriendlyName);
+ sb.append(" operatorFriendlyName: ").append("(")
+ .append(operatorFriendlyName).append(")");
+
if (wanMetrics != null)
- sb.append(" wanMetrics: ").append(wanMetrics);
- if (connectionCapability != null)
- sb.append(" connectionCapability: ").append(connectionCapability);
- if (osuProviderList != null)
- sb.append(" osuProviderList: (size=" + osuProviderList.size() + ")");
+ sb.append(" wanMetrics: ").append("(")
+ .append(wanMetrics.toString()).append(")");
+
+ if (connectionCapability != null) {
+ sb.append(" connectionCapability: ");
+ for (IpProtoPort ip : connectionCapability)
+ sb.append("(").append(ip.toString()).append(")");
+ }
+
+ if (osuProviderList != null) {
+ sb.append(" osuProviderList: ");
+ for (WifiPasspointOsuProvider osu : osuProviderList)
+ sb.append("(").append(osu.toString()).append(")");
+ }
+
return sb.toString();
}
/** Implement the Parcelable interface {@hide} */
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeValue(bssid);
- out.writeValue(venueName);
- out.writeValue(networkAuthType);
- out.writeValue(roamingConsortium);
- out.writeValue(ipAddrTypeAvaibility);
- out.writeValue(naiRealm);
- out.writeValue(cellularNetwork);
- out.writeValue(domainName);
- out.writeValue(operatorFriendlyName);
- out.writeValue(wanMetrics);
- out.writeValue(connectionCapability);
+ out.writeString(bssid);
+ out.writeString(venueName);
+
+ if (networkAuthType == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(networkAuthType.size());
+ for (NetworkAuthType auth : networkAuthType) {
+ out.writeInt(auth.type);
+ out.writeString(auth.redirectUrl);
+ }
+ }
+
+ if (roamingConsortium == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(roamingConsortium.size());
+ for (String oi : roamingConsortium)
+ out.writeString(oi);
+ }
+
+ if (ipAddrTypeAvailability == null) {
+ out.writeInt(IpAddressType.NULL_VALUE);
+ } else {
+ out.writeInt(ipAddrTypeAvailability.availability);
+ }
+
+ if (naiRealm == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(naiRealm.size());
+ for (NaiRealm realm : naiRealm) {
+ out.writeInt(realm.encoding);
+ out.writeString(realm.realm);
+ }
+ }
+
+ if (cellularNetwork == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(cellularNetwork.rawData.length);
+ out.writeByteArray(cellularNetwork.rawData);
+ }
+
+
+ if (domainName == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(domainName.size());
+ for (String fqdn : domainName)
+ out.writeString(fqdn);
+ }
+
+ out.writeString(operatorFriendlyName);
+
+ if (wanMetrics == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(1);
+ out.writeInt(wanMetrics.wanInfo);
+ out.writeLong(wanMetrics.downlinkSpeed);
+ out.writeLong(wanMetrics.uplinkSpeed);
+ out.writeInt(wanMetrics.downlinkLoad);
+ out.writeInt(wanMetrics.uplinkLoad);
+ out.writeInt(wanMetrics.lmd);
+ }
+
+ if (connectionCapability == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(connectionCapability.size());
+ for (IpProtoPort ip : connectionCapability) {
+ out.writeInt(ip.proto);
+ out.writeInt(ip.port);
+ out.writeInt(ip.status);
+ }
+ }
+
if (osuProviderList == null) {
out.writeInt(0);
} else {
@@ -254,18 +463,86 @@ public class WifiPasspointInfo implements Parcelable {
@Override
public WifiPasspointInfo createFromParcel(Parcel in) {
WifiPasspointInfo p = new WifiPasspointInfo();
- p.bssid = (String) in.readValue(String.class.getClassLoader());
- p.venueName = (String) in.readValue(String.class.getClassLoader());
- p.networkAuthType = (String) in.readValue(String.class.getClassLoader());
- p.roamingConsortium = (String) in.readValue(String.class.getClassLoader());
- p.ipAddrTypeAvaibility = (String) in.readValue(String.class.getClassLoader());
- p.naiRealm = (String) in.readValue(String.class.getClassLoader());
- p.cellularNetwork = (String) in.readValue(String.class.getClassLoader());
- p.domainName = (String) in.readValue(String.class.getClassLoader());
- p.operatorFriendlyName = (String) in.readValue(String.class.getClassLoader());
- p.wanMetrics = (String) in.readValue(String.class.getClassLoader());
- p.connectionCapability = (String) in.readValue(String.class.getClassLoader());
- int n = in.readInt();
+ int n;
+
+ p.bssid = in.readString();
+ p.venueName = in.readString();
+
+ n = in.readInt();
+ if (n > 0) {
+ p.networkAuthType = new ArrayList<NetworkAuthType>();
+ for (int i = 0; i < n; i++) {
+ NetworkAuthType auth = new NetworkAuthType();
+ auth.type = in.readInt();
+ auth.redirectUrl = in.readString();
+ p.networkAuthType.add(auth);
+ }
+ }
+
+ n = in.readInt();
+ if (n > 0) {
+ p.roamingConsortium = new ArrayList<String>();
+ for (int i = 0; i < n; i++)
+ p.roamingConsortium.add(in.readString());
+ }
+
+ n = in.readInt();
+ if (n != IpAddressType.NULL_VALUE) {
+ p.ipAddrTypeAvailability = new IpAddressType();
+ p.ipAddrTypeAvailability.availability = n;
+ }
+
+ n = in.readInt();
+ if (n > 0) {
+ p.naiRealm = new ArrayList<NaiRealm>();
+ for (int i = 0; i < n; i++) {
+ NaiRealm realm = new NaiRealm();
+ realm.encoding = in.readInt();
+ realm.realm = in.readString();
+ p.naiRealm.add(realm);
+ }
+ }
+
+ n = in.readInt();
+ if (n > 0) {
+ p.cellularNetwork = new CellularNetwork();
+ p.cellularNetwork.rawData = new byte[n];
+ in.readByteArray(p.cellularNetwork.rawData);
+ }
+
+ n = in.readInt();
+ if (n > 0) {
+ p.domainName = new ArrayList<String>();
+ for (int i = 0; i < n; i++)
+ p.domainName.add(in.readString());
+ }
+
+ p.operatorFriendlyName = in.readString();
+
+ n = in.readInt();
+ if (n > 0) {
+ p.wanMetrics = new WanMetrics();
+ p.wanMetrics.wanInfo = in.readInt();
+ p.wanMetrics.downlinkSpeed = in.readLong();
+ p.wanMetrics.uplinkSpeed = in.readLong();
+ p.wanMetrics.downlinkLoad = in.readInt();
+ p.wanMetrics.uplinkLoad = in.readInt();
+ p.wanMetrics.lmd = in.readInt();
+ }
+
+ n = in.readInt();
+ if (n > 0) {
+ p.connectionCapability = new ArrayList<IpProtoPort>();
+ for (int i = 0; i < n; i++) {
+ IpProtoPort ip = new IpProtoPort();
+ ip.proto = in.readInt();
+ ip.port = in.readInt();
+ ip.status = in.readInt();
+ p.connectionCapability.add(ip);
+ }
+ }
+
+ n = in.readInt();
if (n > 0) {
p.osuProviderList = new ArrayList<WifiPasspointOsuProvider>();
for (int i = 0; i < n; i++) {
@@ -274,6 +551,7 @@ public class WifiPasspointInfo implements Parcelable {
p.osuProviderList.add(osu);
}
}
+
return p;
}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
index 18a8f1e..f40dc4f 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
@@ -94,10 +94,10 @@ public class WifiPasspointOsuProvider implements Parcelable {
sb.append(" serverUri: ").append(serverUri);
sb.append(" osuMethod: ").append(osuMethod);
if (iconFileName != null) {
- sb.append(" icon: [").append(iconWidth).append("x")
+ sb.append(" icon: <").append(iconWidth).append("x")
.append(iconHeight).append(" ")
.append(iconType).append(" ")
- .append(iconFileName);
+ .append(iconFileName).append(">");
}
if (osuNai != null)
sb.append(" osuNai: ").append(osuNai);
@@ -113,16 +113,16 @@ public class WifiPasspointOsuProvider implements Parcelable {
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeValue(ssid);
- out.writeValue(friendlyName);
- out.writeValue(serverUri);
+ out.writeString(ssid);
+ out.writeString(friendlyName);
+ out.writeString(serverUri);
out.writeInt(osuMethod);
out.writeInt(iconWidth);
out.writeInt(iconHeight);
- out.writeValue(iconType);
- out.writeValue(iconFileName);
- out.writeValue(osuNai);
- out.writeValue(osuService);
+ out.writeString(iconType);
+ out.writeString(iconFileName);
+ out.writeString(osuNai);
+ out.writeString(osuService);
// TODO: icon image?
}
@@ -131,16 +131,16 @@ public class WifiPasspointOsuProvider implements Parcelable {
@Override
public WifiPasspointOsuProvider createFromParcel(Parcel in) {
WifiPasspointOsuProvider osu = new WifiPasspointOsuProvider();
- osu.ssid = (String) in.readValue(String.class.getClassLoader());
- osu.friendlyName = (String) in.readValue(String.class.getClassLoader());
- osu.serverUri = (String) in.readValue(String.class.getClassLoader());
+ osu.ssid = in.readString();
+ osu.friendlyName = in.readString();
+ osu.serverUri = in.readString();
osu.osuMethod = in.readInt();
osu.iconWidth = in.readInt();
osu.iconHeight = in.readInt();
- osu.iconType = (String) in.readValue(String.class.getClassLoader());
- osu.iconFileName = (String) in.readValue(String.class.getClassLoader());
- osu.osuNai = (String) in.readValue(String.class.getClassLoader());
- osu.osuService = (String) in.readValue(String.class.getClassLoader());
+ osu.iconType = in.readString();
+ osu.iconFileName = in.readString();
+ osu.osuNai = in.readString();
+ osu.osuService = in.readString();
return osu;
}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
index 5f76562..9fccf0a 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
@@ -35,7 +35,7 @@ public class WifiPasspointPolicy implements Parcelable {
public static final int UNRESTRICTED = 2;
private String mName;
- private int mSubscriptionPriority;
+ private int mCredentialPriority;
private int mRoamingPriority;
private String mBssid;
private String mSsid;
@@ -44,11 +44,13 @@ public class WifiPasspointPolicy implements Parcelable {
private boolean mIsHomeSp;
/** @hide */
- public WifiPasspointPolicy(String name, int priority, String ssid,
+ public WifiPasspointPolicy(String name, String ssid,
String bssid, WifiPasspointCredential pc,
int restriction, boolean ishomesp) {
mName = name;
- mSubscriptionPriority = priority;
+ if (pc != null) {
+ mCredentialPriority = pc.getPriority();
+ }
//PerProviderSubscription/<X+>/Policy/PreferredRoamingPartnerList/<X+>/Priority
mRoamingPriority = 128; //default priority value of 128
mSsid = ssid;
@@ -102,8 +104,8 @@ public class WifiPasspointPolicy implements Parcelable {
}
/** @hide */
- public void setSubscriptionPriority(int priority) {
- mSubscriptionPriority = priority;
+ public void setCredentialPriority(int priority) {
+ mCredentialPriority = priority;
}
/** @hide */
@@ -111,8 +113,8 @@ public class WifiPasspointPolicy implements Parcelable {
mRoamingPriority = priority;
}
- public int getSubscriptionPriority() {
- return mSubscriptionPriority;
+ public int getCredentialPriority() {
+ return mCredentialPriority;
}
public int getRoamingPriority() {
@@ -132,11 +134,11 @@ public class WifiPasspointPolicy implements Parcelable {
return -1;
} else if ((this.mIsHomeSp == true && another.getHomeSp() == true)) {
Log.d(TAG, "both HomeSP");
- //if both home sp, compare subscription priority
- if (this.mSubscriptionPriority < another.getSubscriptionPriority()) {
+ //if both home sp, compare credential priority
+ if (this.mCredentialPriority < another.getCredentialPriority()) {
Log.d(TAG, "this priority is higher");
return -1;
- } else if (this.mSubscriptionPriority == another.getSubscriptionPriority()) {
+ } else if (this.mCredentialPriority == another.getCredentialPriority()) {
Log.d(TAG, "both priorities equal");
//if priority still the same, compare name(ssid)
if (this.mName.compareTo(another.mName) != 0) {
@@ -192,7 +194,7 @@ public class WifiPasspointPolicy implements Parcelable {
@Override
/** @hide */
public String toString() {
- return "PasspointPolicy: name=" + mName + " SubscriptionPriority=" + mSubscriptionPriority +
+ return "PasspointPolicy: name=" + mName + " CredentialPriority=" + mCredentialPriority +
" mRoamingPriority" + mRoamingPriority +
" ssid=" + mSsid + " restriction=" + mRestriction +
" ishomesp=" + mIsHomeSp + " Credential=" + mCredential;