summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk2
-rw-r--r--CleanSpec.mk2
-rw-r--r--api/current.txt39
-rw-r--r--cmds/app_process/Android.mk22
-rw-r--r--cmds/bootanimation/Android.mk3
-rw-r--r--cmds/bu/src/com/android/commands/bu/Backup.java9
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java160
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java35
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl5
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl5
-rw-r--r--core/java/android/app/AppOpsManager.java48
-rw-r--r--core/java/android/app/IBackupAgent.aidl12
-rw-r--r--core/java/android/app/UiAutomation.java39
-rw-r--r--core/java/android/app/backup/BackupDataOutput.java9
-rw-r--r--core/java/android/app/backup/IBackupManager.aidl2
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java86
-rw-r--r--core/java/android/appwidget/AppWidgetProvider.java51
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java205
-rw-r--r--core/java/android/bluetooth/BluetoothAdvScanData.java7
-rw-r--r--core/java/android/bluetooth/BluetoothGatt.java11
-rw-r--r--core/java/android/bluetooth/IBluetoothGattCallback.aidl1
-rw-r--r--core/java/android/hardware/Camera.java30
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java1
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java1
-rw-r--r--core/java/android/hardware/input/InputManager.java28
-rw-r--r--core/java/android/net/EthernetDataTracker.java2
-rw-r--r--core/java/android/net/MobileDataStateTracker.java11
-rw-r--r--core/java/android/os/BatteryProperty.java1
-rw-r--r--core/java/android/os/BatteryStats.java9
-rw-r--r--core/java/android/os/Environment.java15
-rw-r--r--core/java/android/os/IVibratorService.aidl4
-rw-r--r--core/java/android/os/NullVibrator.java20
-rw-r--r--core/java/android/os/SystemVibrator.java25
-rw-r--r--core/java/android/os/Vibrator.java74
-rw-r--r--core/java/android/provider/SearchIndexableData.java117
-rw-r--r--core/java/android/provider/SearchIndexableResource.java65
-rw-r--r--core/java/android/provider/SearchIndexablesContract.java179
-rw-r--r--core/java/android/provider/SearchIndexablesProvider.java174
-rw-r--r--core/java/android/util/LongArray.java4
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java153
-rw-r--r--core/java/android/view/IMagnificationCallbacks.aidl29
-rw-r--r--core/java/android/view/IWindowManager.aidl51
-rw-r--r--core/java/android/view/View.java8
-rw-r--r--core/java/android/view/ViewGroup.java4
-rw-r--r--core/java/android/view/ViewRootImpl.java6
-rw-r--r--core/java/android/view/WindowInfo.aidl19
-rw-r--r--core/java/android/view/WindowInfo.java164
-rw-r--r--core/java/android/view/WindowManagerInternal.java133
-rw-r--r--core/java/android/view/WindowManagerPolicy.java7
-rw-r--r--core/java/android/view/accessibility/AccessibilityCache.java467
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java30
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java142
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java296
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java96
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfoCache.java336
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeProvider.java13
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java4
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.aidl19
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java574
-rw-r--r--core/java/android/view/animation/ClipRectAnimation.java59
-rw-r--r--core/java/android/view/animation/Transformation.java53
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl2
-rw-r--r--core/java/com/android/internal/appwidget/IAppWidgetService.aidl1
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java106
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java238
-rw-r--r--core/java/com/android/internal/widget/ActionBarOverlayLayout.java2
-rw-r--r--core/java/com/android/server/AppWidgetBackupBridge.java63
-rw-r--r--core/java/com/android/server/WidgetBackupProvider.java34
-rw-r--r--core/jni/android_content_res_Configuration.cpp12
-rw-r--r--core/jni/android_view_GLES20Canvas.cpp2
-rw-r--r--core/jni/android_view_GLRenderer.cpp2
-rw-r--r--core/jni/android_view_HardwareLayer.cpp2
-rw-r--r--core/jni/android_view_RenderNode.cpp2
-rw-r--r--core/res/AndroidManifest.xml20
-rw-r--r--core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.pngbin549 -> 543 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_route_disabled_holo_light.pngbin537 -> 561 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.pngbin588 -> 497 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_route_off_holo_light.pngbin573 -> 637 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.pngbin616 -> 543 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.pngbin606 -> 581 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.pngbin599 -> 565 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.pngbin590 -> 599 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.pngbin618 -> 568 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.pngbin593 -> 622 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.pngbin583 -> 551 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_route_on_holo_light.pngbin583 -> 699 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_notification_cast_0.pngbin448 -> 471 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_notification_cast_1.pngbin457 -> 473 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_notification_cast_2.pngbin465 -> 472 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_notification_cast_on.pngbin414 -> 470 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_route_disabled_holo_dark.pngbin388 -> 353 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_route_disabled_holo_light.pngbin388 -> 380 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.pngbin400 -> 379 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_route_off_holo_light.pngbin394 -> 401 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.pngbin429 -> 373 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.pngbin440 -> 398 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.pngbin417 -> 388 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.pngbin410 -> 401 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.pngbin430 -> 383 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.pngbin424 -> 399 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.pngbin399 -> 402 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_route_on_holo_light.pngbin399 -> 438 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_notification_cast_0.pngbin332 -> 329 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_notification_cast_1.pngbin329 -> 330 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_notification_cast_2.pngbin326 -> 351 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_notification_cast_on.pngbin305 -> 342 bytes
-rw-r--r--core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.pngbin174 -> 1125 bytes
-rw-r--r--core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.pngbin161 -> 1121 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.pngbin709 -> 677 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.pngbin702 -> 689 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.pngbin759 -> 684 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.pngbin745 -> 787 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.pngbin807 -> 675 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.pngbin806 -> 721 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.pngbin772 -> 698 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.pngbin766 -> 719 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.pngbin804 -> 712 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.pngbin778 -> 790 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.pngbin746 -> 686 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.pngbin749 -> 857 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_notification_cast_0.pngbin557 -> 582 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_notification_cast_1.pngbin585 -> 607 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_notification_cast_2.pngbin589 -> 610 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_notification_cast_on.pngbin555 -> 596 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.pngbin1155 -> 1162 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.pngbin1152 -> 1158 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.pngbin1071 -> 1000 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.pngbin1053 -> 1033 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.pngbin1130 -> 1067 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.pngbin1108 -> 1130 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.pngbin1183 -> 1014 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.pngbin1180 -> 1062 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.pngbin1145 -> 1043 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.pngbin1150 -> 1093 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.pngbin1187 -> 1026 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.pngbin1168 -> 1110 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.pngbin1102 -> 1148 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.pngbin1108 -> 1271 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_notification_cast_0.pngbin812 -> 853 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_notification_cast_1.pngbin847 -> 868 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_notification_cast_2.pngbin827 -> 867 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_notification_cast_on.pngbin768 -> 934 bytes
-rw-r--r--core/res/res/values-ca/strings.xml2
-rw-r--r--core/res/res/values-el/strings.xml6
-rw-r--r--core/res/res/values-es-rUS/strings.xml2
-rw-r--r--core/res/res/values-fa/strings.xml2
-rw-r--r--core/res/res/values-hu/strings.xml2
-rw-r--r--core/res/res/values-iw/strings.xml2
-rw-r--r--core/res/res/values-lt/strings.xml2
-rw-r--r--core/res/res/values-sk/strings.xml18
-rw-r--r--core/res/res/values-th/strings.xml2
-rw-r--r--core/res/res/values/attrs.xml2
-rw-r--r--core/res/res/values/strings.xml14
-rw-r--r--docs/downloads/training/BitmapFun.zipbin104008 -> 0 bytes
-rw-r--r--docs/html/_redirects.yaml3
-rw-r--r--docs/html/guide/topics/graphics/hardware-accel.jd294
-rw-r--r--docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html64
-rw-r--r--docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html67
-rw-r--r--docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html56
-rw-r--r--docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html59
-rw-r--r--docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html17
-rw-r--r--docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html204
-rw-r--r--docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html358
-rw-r--r--docs/html/training/displaying-bitmaps/cache-bitmap.jd4
-rw-r--r--docs/html/training/displaying-bitmaps/display-bitmap.jd4
-rw-r--r--docs/html/training/displaying-bitmaps/index.jd4
-rw-r--r--docs/html/training/displaying-bitmaps/load-bitmap.jd4
-rw-r--r--docs/html/training/displaying-bitmaps/manage-memory.jd4
-rw-r--r--docs/html/training/displaying-bitmaps/process-bitmap.jd4
-rw-r--r--docs/html/wear/css/wear.css19
-rw-r--r--docs/html/wear/design/index.html13
-rw-r--r--docs/html/wear/design/user-interface.html6
-rw-r--r--docs/html/wear/images/close.pngbin1261 -> 603 bytes
-rw-r--r--docs/html/wear/index.html54
-rw-r--r--docs/html/wear/notifications/pages.html2
-rw-r--r--docs/html/wear/notifications/remote-input.html30
-rw-r--r--docs/html/wear/notifications/stacks.html35
-rw-r--r--docs/html/wear/preview/start.html27
-rw-r--r--libs/androidfw/Asset.cpp18
-rw-r--r--libs/androidfw/BackupData.cpp2
-rw-r--r--libs/androidfw/BackupHelpers.cpp6
-rw-r--r--libs/androidfw/CursorWindow.cpp16
-rw-r--r--libs/androidfw/ResourceTypes.cpp150
-rw-r--r--libs/androidfw/ZipUtils.cpp4
-rw-r--r--libs/hwui/Android.mk1
-rw-r--r--libs/hwui/DeferredLayerUpdater.h2
-rw-r--r--libs/hwui/DisplayList.cpp537
-rw-r--r--libs/hwui/DisplayList.h130
-rw-r--r--libs/hwui/DisplayListRenderer.cpp4
-rw-r--r--libs/hwui/DisplayListRenderer.h2
-rw-r--r--libs/hwui/Layer.cpp4
-rw-r--r--libs/hwui/OpenGLRenderer.cpp16
-rw-r--r--libs/hwui/Rect.h8
-rw-r--r--libs/hwui/RenderNode.cpp571
-rw-r--r--libs/hwui/RenderNode.h200
-rw-r--r--libs/hwui/ShadowTessellator.cpp28
-rw-r--r--libs/hwui/ShadowTessellator.h6
-rw-r--r--libs/hwui/SpotShadow.cpp7
-rw-r--r--media/java/android/media/AudioService.java8
-rw-r--r--media/java/android/media/AudioTrack.java37
-rw-r--r--media/java/android/media/MediaHTTPConnection.java2
-rw-r--r--media/java/android/media/MediaPlayer.java59
-rw-r--r--media/java/android/media/SoundPool.java43
-rw-r--r--media/jni/android_media_MediaHTTPConnection.cpp8
-rw-r--r--media/jni/android_media_MediaPlayer.cpp21
-rw-r--r--media/jni/mediaeditor/Android.mk1
-rw-r--r--media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp4
-rw-r--r--packages/DocumentsUI/res/values-de/strings.xml2
-rw-r--r--packages/DocumentsUI/res/values-el/strings.xml4
-rw-r--r--packages/DocumentsUI/res/values-in/strings.xml2
-rw-r--r--packages/DocumentsUI/res/values-sk/strings.xml2
-rw-r--r--packages/ExternalStorageProvider/res/values-sk/strings.xml2
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java1
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java1
-rw-r--r--packages/PrintSpooler/res/values-fr/strings.xml2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java30
-rw-r--r--packages/Shell/res/values-el/strings.xml2
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.pngbin1325 -> 1324 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.pngbin1277 -> 1395 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.pngbin1275 -> 1256 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.pngbin1288 -> 1280 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.pngbin1301 -> 1290 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.pngbin933 -> 959 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.pngbin942 -> 1012 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.pngbin921 -> 917 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.pngbin949 -> 937 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.pngbin958 -> 951 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.pngbin796 -> 855 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.pngbin742 -> 802 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.pngbin743 -> 738 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.pngbin765 -> 782 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.pngbin812 -> 782 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.pngbin1202 -> 1326 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.pngbin1169 -> 1355 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.pngbin1139 -> 1135 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.pngbin1159 -> 1182 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.pngbin1197 -> 1170 bytes
-rw-r--r--packages/SystemUI/res/values-it/strings.xml2
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml2
-rw-r--r--packages/SystemUI/res/values-th/strings.xml4
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml2
-rw-r--r--packages/services/PacProcessor/jni/Android.mk1
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindowManager.java21
-rw-r--r--rs/java/android/renderscript/RenderScript.java20
-rw-r--r--rs/jni/android_renderscript_RenderScript.cpp24
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java670
-rw-r--r--services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java42
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetService.java63
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java741
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java481
-rw-r--r--services/core/java/com/android/server/AppOpsService.java87
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java46
-rw-r--r--services/core/java/com/android/server/VibratorService.java28
-rwxr-xr-xservices/core/java/com/android/server/am/ActiveServices.java10
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java2
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java5
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java113
-rw-r--r--services/core/java/com/android/server/pm/Installer.java4
-rwxr-xr-xservices/core/java/com/android/server/pm/PackageManagerService.java44
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java103
-rw-r--r--services/core/java/com/android/server/power/AutomaticBrightnessController.java3
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java1173
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java272
-rw-r--r--services/core/java/com/android/server/wm/DisplayMagnifier.java756
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java275
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java54
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java15
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java7
-rw-r--r--tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java36
270 files changed, 9353 insertions, 3749 deletions
diff --git a/Android.mk b/Android.mk
index a691987..d279704 100644
--- a/Android.mk
+++ b/Android.mk
@@ -196,7 +196,6 @@ LOCAL_SRC_FILES += \
core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
core/java/android/view/IApplicationToken.aidl \
core/java/android/view/IAssetAtlas.aidl \
- core/java/android/view/IMagnificationCallbacks.aidl \
core/java/android/view/IInputFilter.aidl \
core/java/android/view/IInputFilterHost.aidl \
core/java/android/view/IOnKeyguardExitResult.aidl \
@@ -399,6 +398,7 @@ aidl_files := \
frameworks/base/core/java/android/view/accessibility/AccessibilityEvent.aidl \
frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl \
frameworks/base/core/java/android/view/accessibility/AccessibilityRecord.aidl \
+ frameworks/base/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl \
frameworks/base/core/java/android/view/KeyEvent.aidl \
frameworks/base/core/java/android/view/MotionEvent.aidl \
frameworks/base/core/java/android/view/Surface.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 448b03d..4f0e603 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -187,6 +187,8 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/framework-res_in
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/print/IPrintClient.*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/media/java/android/media/IMedia*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/view/IMagnificationCallbacks*)
+
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/api/current.txt b/api/current.txt
index e72354b..09102fb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2328,6 +2328,7 @@ package android.accessibilityservice {
ctor public AccessibilityService();
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
+ method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public final android.os.IBinder onBind(android.content.Intent);
method protected boolean onGesture(int);
@@ -2393,6 +2394,7 @@ package android.accessibilityservice {
field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+ field public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 64; // 0x40
field public int eventTypes;
field public int feedbackType;
field public int flags;
@@ -4600,6 +4602,7 @@ package android.app {
method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
+ method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
method public boolean injectInputEvent(android.view.InputEvent, boolean);
method public final boolean performGlobalAction(int);
method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
@@ -4973,15 +4976,19 @@ package android.appwidget {
field public static final java.lang.String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
field public static final java.lang.String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
field public static final java.lang.String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
+ field public static final java.lang.String ACTION_APPWIDGET_HOST_RESTORED = "android.appwidget.action.APPWIDGET_HOST_RESTORED";
field public static final java.lang.String ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS";
field public static final java.lang.String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK";
+ field public static final java.lang.String ACTION_APPWIDGET_RESTORED = "android.appwidget.action.APPWIDGET_RESTORED";
field public static final java.lang.String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
field public static final java.lang.String EXTRA_APPWIDGET_ID = "appWidgetId";
field public static final java.lang.String EXTRA_APPWIDGET_IDS = "appWidgetIds";
+ field public static final java.lang.String EXTRA_APPWIDGET_OLD_IDS = "appWidgetOldIds";
field public static final java.lang.String EXTRA_APPWIDGET_OPTIONS = "appWidgetOptions";
field public static final java.lang.String EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider";
field public static final java.lang.String EXTRA_CUSTOM_EXTRAS = "customExtras";
field public static final java.lang.String EXTRA_CUSTOM_INFO = "customInfo";
+ field public static final java.lang.String EXTRA_HOST_ID = "hostId";
field public static final int INVALID_APPWIDGET_ID = 0; // 0x0
field public static final java.lang.String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
field public static final java.lang.String OPTION_APPWIDGET_HOST_CATEGORY = "appWidgetCategory";
@@ -4998,6 +5005,7 @@ package android.appwidget {
method public void onDisabled(android.content.Context);
method public void onEnabled(android.content.Context);
method public void onReceive(android.content.Context, android.content.Intent);
+ method public void onRestored(android.content.Context, int[], int[]);
method public void onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[]);
}
@@ -19546,8 +19554,10 @@ package android.os {
public abstract class Vibrator {
method public abstract void cancel();
method public abstract boolean hasVibrator();
- method public abstract void vibrate(long);
- method public abstract void vibrate(long[], int);
+ method public void vibrate(long);
+ method public void vibrate(long, int);
+ method public void vibrate(long[], int);
+ method public void vibrate(long[], int, int);
}
public class WorkSource implements android.os.Parcelable {
@@ -30398,6 +30408,7 @@ package android.view.accessibility {
field public static final int TYPE_VIEW_TEXT_CHANGED = 16; // 0x10
field public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+ field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20
}
@@ -30461,6 +30472,7 @@ package android.view.accessibility {
method public int getTextSelectionEnd();
method public int getTextSelectionStart();
method public java.lang.String getViewIdResourceName();
+ method public android.view.accessibility.AccessibilityWindowInfo getWindow();
method public int getWindowId();
method public boolean isAccessibilityFocused();
method public boolean isCheckable();
@@ -30607,6 +30619,7 @@ package android.view.accessibility {
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
method public boolean performAction(int, int, android.os.Bundle);
+ field public static final int HOST_VIEW_ID = -1; // 0xffffffff
}
public class AccessibilityRecord {
@@ -30658,6 +30671,28 @@ package android.view.accessibility {
method public void setToIndex(int);
}
+ public final class AccessibilityWindowInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public void getBoundsInScreen(android.graphics.Rect);
+ method public android.view.accessibility.AccessibilityWindowInfo getChild(int);
+ method public int getChildCount();
+ method public int getId();
+ method public int getLayer();
+ method public android.view.accessibility.AccessibilityWindowInfo getParent();
+ method public android.view.accessibility.AccessibilityNodeInfo getRoot();
+ method public int getType();
+ method public boolean isActive();
+ method public boolean isFocused();
+ method public static android.view.accessibility.AccessibilityWindowInfo obtain();
+ method public static android.view.accessibility.AccessibilityWindowInfo obtain(android.view.accessibility.AccessibilityWindowInfo);
+ method public void recycle();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int TYPE_APPLICATION = 1; // 0x1
+ field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+ field public static final int TYPE_SYSTEM = 3; // 0x3
+ }
+
public class CaptioningManager {
method public void addCaptioningChangeListener(android.view.accessibility.CaptioningManager.CaptioningChangeListener);
method public final float getFontScale();
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index b9afe40..cb7e1a0 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -1,4 +1,6 @@
LOCAL_PATH:= $(call my-dir)
+
+# 32-bit app_process
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
@@ -12,9 +14,29 @@ LOCAL_SHARED_LIBRARIES := \
libandroid_runtime
LOCAL_MODULE:= app_process
+LOCAL_32_BIT_ONLY := true
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(TARGET_IS_64_BIT),true)
+
+# 64-bit app_process64
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ app_main.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ liblog \
+ libbinder \
+ libandroid_runtime
+LOCAL_MODULE:= app_process64
+LOCAL_NO_2ND_ARCH := true
include $(BUILD_EXECUTABLE)
+endif # TARGET_IS_64_BIT
# Build a variant of app_process binary linked with ASan runtime.
# ARM-only at the moment.
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index a4e2718..c4fe6cf 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -21,5 +21,8 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_MODULE:= bootanimation
+ifdef TARGET_32_BIT_SURFACEFLINGER
+LOCAL_32_BIT_ONLY := true
+endif
include $(BUILD_EXECUTABLE)
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index 73fd660..2673031 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -68,6 +68,7 @@ public final class Backup {
boolean saveObbs = false;
boolean saveShared = false;
boolean doEverything = false;
+ boolean doWidgets = false;
boolean allIncludesSystem = true;
String arg;
@@ -89,6 +90,10 @@ public final class Backup {
allIncludesSystem = true;
} else if ("-nosystem".equals(arg)) {
allIncludesSystem = false;
+ } else if ("-widgets".equals(arg)) {
+ doWidgets = true;
+ } else if ("-nowidgets".equals(arg)) {
+ doWidgets = false;
} else if ("-all".equals(arg)) {
doEverything = true;
} else {
@@ -114,8 +119,8 @@ public final class Backup {
try {
fd = ParcelFileDescriptor.adoptFd(socketFd);
String[] packArray = new String[packages.size()];
- mBackupManager.fullBackup(fd, saveApks, saveObbs, saveShared, doEverything,
- allIncludesSystem, packages.toArray(packArray));
+ mBackupManager.fullBackup(fd, saveApks, saveObbs, saveShared, doWidgets,
+ doEverything, allIncludesSystem, packages.toArray(packArray));
} catch (RemoteException e) {
Log.e(TAG, "Unable to invoke backup manager for backup");
} finally {
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 1e3d5be..b01d92c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -23,14 +23,18 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
import com.android.internal.os.HandlerCaller;
+import java.util.List;
+
/**
* An accessibility service runs in the background and receives callbacks by the system
* when {@link AccessibilityEvent}s are fired. Such events denote some state transition
@@ -180,28 +184,37 @@ import com.android.internal.os.HandlerCaller;
* event generation has settled down.</p>
* <h3>Event types</h3>
* <ul>
- * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}
- * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}
- * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}
- * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}
- * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}
- * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li>
+ * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li>
+ * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li>
+ * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li>
+ * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li>
+ * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li>
* </ul>
* <h3>Feedback types</h3>
* <ul>
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li>
* </ul>
* @see AccessibilityEvent
* @see AccessibilityServiceInfo
@@ -443,8 +456,41 @@ public abstract class AccessibilityService extends Service {
}
/**
+ * Gets the windows on the screen. This method returns only the windows
+ * that a sighted user can interact with, as opposed to all windows.
+ * For example, if there is a modal dialog shown and the user cannot touch
+ * anything behind it, then only the modal window will be reported
+ * (assuming it is the top one). For convenience the returned windows
+ * are ordered in a descending layer order, which is the windows that
+ * are higher in the Z-order are reported first.
+ * <p>
+ * <strong>Note:</strong> In order to access the windows your service has
+ * to declare the capability to retrieve window content by setting the
+ * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
+ * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
+ * Also the service has to opt-in to retrieve the interactive windows by
+ * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
+ * flag.
+ * </p>
+ *
+ * @return The windows if there are windows and the service is can retrieve
+ * them, otherwise an empty list.
+ */
+ public List<AccessibilityWindowInfo> getWindows() {
+ return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId);
+ }
+
+ /**
* Gets the root node in the currently active window if this service
- * can retrieve window content.
+ * can retrieve window content. The active window is the one that the user
+ * is currently touching or the window with input focus, if the user is not
+ * touching any window.
+ * <p>
+ * <strong>Note:</strong> In order to access the root node your service has
+ * to declare the capability to retrieve window content by setting the
+ * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
+ * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
+ * </p>
*
* @return The root node if this service can retrieve window content.
*/
@@ -584,12 +630,13 @@ public abstract class AccessibilityService extends Service {
static final int NO_ID = -1;
- private static final int DO_SET_SET_CONNECTION = 10;
- private static final int DO_ON_INTERRUPT = 20;
- private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
- private static final int DO_ON_GESTURE = 40;
- private static final int DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 50;
- private static final int DO_ON_KEY_EVENT = 60;
+ private static final int DO_SET_SET_CONNECTION = 1;
+ private static final int DO_ON_INTERRUPT = 2;
+ private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
+ private static final int DO_ON_GESTURE = 4;
+ private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
+ private static final int DO_ON_KEY_EVENT = 6;
+ private static final int DO_ON_WINDOWS_CHANGED = 7;
private final HandlerCaller mCaller;
@@ -624,8 +671,8 @@ public abstract class AccessibilityService extends Service {
mCaller.sendMessage(message);
}
- public void clearAccessibilityNodeInfoCache() {
- Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE);
+ public void clearAccessibilityCache() {
+ Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE);
mCaller.sendMessage(message);
}
@@ -635,6 +682,13 @@ public abstract class AccessibilityService extends Service {
mCaller.sendMessage(message);
}
+ @Override
+ public void onWindowsChanged(int[] windowIds) {
+ Message message = mCaller.obtainMessageO(DO_ON_WINDOWS_CHANGED, windowIds);
+ mCaller.sendMessage(message);
+ }
+
+ @Override
public void executeMessage(Message message) {
switch (message.what) {
case DO_ON_ACCESSIBILITY_EVENT: {
@@ -642,12 +696,19 @@ public abstract class AccessibilityService extends Service {
if (event != null) {
AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
mCallback.onAccessibilityEvent(event);
- event.recycle();
+ // Make sure the event is recycled.
+ try {
+ event.recycle();
+ } catch (IllegalStateException ise) {
+ /* ignore - best effort */
+ }
}
} return;
+
case DO_ON_INTERRUPT: {
mCallback.onInterrupt();
} return;
+
case DO_SET_SET_CONNECTION: {
mConnectionId = message.arg1;
IAccessibilityServiceConnection connection =
@@ -658,18 +719,22 @@ public abstract class AccessibilityService extends Service {
mCallback.onSetConnectionId(mConnectionId);
mCallback.onServiceConnected();
} else {
- AccessibilityInteractionClient.getInstance().removeConnection(mConnectionId);
+ AccessibilityInteractionClient.getInstance().removeConnection(
+ mConnectionId);
AccessibilityInteractionClient.getInstance().clearCache();
mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID);
}
} return;
+
case DO_ON_GESTURE: {
final int gestureId = message.arg1;
mCallback.onGesture(gestureId);
} return;
- case DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: {
+
+ case DO_CLEAR_ACCESSIBILITY_CACHE: {
AccessibilityInteractionClient.getInstance().clearCache();
} return;
+
case DO_ON_KEY_EVENT: {
KeyEvent event = (KeyEvent) message.obj;
try {
@@ -685,9 +750,40 @@ public abstract class AccessibilityService extends Service {
}
}
} finally {
- event.recycle();
+ // Make sure the event is recycled.
+ try {
+ event.recycle();
+ } catch (IllegalStateException ise) {
+ /* ignore - best effort */
+ }
}
} return;
+
+ case DO_ON_WINDOWS_CHANGED: {
+ final int[] windowIds = (int[]) message.obj;
+
+ // Update the cached windows first.
+ // NOTE: The cache will hold on to the windows so do not recycle.
+ if (windowIds != null) {
+ AccessibilityInteractionClient.getInstance().removeWindows(windowIds);
+ }
+
+ // Let the client know the windows changed.
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_WINDOWS_CHANGED);
+ event.setEventTime(SystemClock.uptimeMillis());
+ event.setSealed(true);
+
+ mCallback.onAccessibilityEvent(event);
+
+ // Make sure the event is recycled.
+ try {
+ event.recycle();
+ } catch (IllegalStateException ise) {
+ /* ignore - best effort */
+ }
+ } break;
+
default :
Log.w(LOG_TAG, "Unknown message type " + message.what);
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index bdc4fdd..4f9ba59 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -284,6 +284,27 @@ public class AccessibilityServiceInfo implements Parcelable {
public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020;
/**
+ * This flag indicates to the system that the accessibility service wants
+ * to access content of all interactive windows. An interactive window is a
+ * window that can be touched by a sighted user when explore by touch is not
+ * enabled. If this flag is not set your service will not receive
+ * {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED}
+ * events, calling AccessibilityService{@link AccessibilityService#getWindows()
+ * AccessibilityService.getWindows()} will return an empty list, and {@link
+ * AccessibilityNodeInfo#getWindow() AccessibilityNodeInfo.getWindow()} will
+ * return null.
+ * <p>
+ * Services that want to set this flag have to declare the capability
+ * to retrieve window content in their meta-data by setting the attribute
+ * {@link android.R.attr#canRetrieveWindowContent canRetrieveWindowContent} to
+ * true, otherwise this flag will be ignored. For how to declare the meta-data
+ * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
+ * </p>
+ * @see android.R.styleable#AccessibilityService_canRetrieveWindowContent
+ */
+ public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000040;
+
+ /**
* The event types an {@link AccessibilityService} is interested in.
* <p>
* <strong>Can be dynamically set at runtime.</strong>
@@ -302,6 +323,15 @@ public class AccessibilityServiceInfo implements Parcelable {
* @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED
* @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
* @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_START
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_END
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_ANNOUNCEMENT
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_START
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_END
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED
*/
public int eventTypes;
@@ -354,6 +384,7 @@ public class AccessibilityServiceInfo implements Parcelable {
* @see #FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY
* @see #FLAG_REQUEST_FILTER_KEY_EVENTS
* @see #FLAG_REPORT_VIEW_IDS
+ * @see #FLAG_RETRIEVE_INTERACTIVE_WINDOWS
*/
public int flags;
@@ -449,7 +480,7 @@ public class AccessibilityServiceInfo implements Parcelable {
com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType,
0);
notificationTimeout = asAttributes.getInt(
- com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
+ com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
0);
flags = asAttributes.getInt(
com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
@@ -861,6 +892,8 @@ public class AccessibilityServiceInfo implements Parcelable {
return "FLAG_REPORT_VIEW_IDS";
case FLAG_REQUEST_FILTER_KEY_EVENTS:
return "FLAG_REQUEST_FILTER_KEY_EVENTS";
+ case FLAG_RETRIEVE_INTERACTIVE_WINDOWS:
+ return "FLAG_RETRIEVE_INTERACTIVE_WINDOWS";
default:
return null;
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index c5e3d43a..edd8727 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -18,6 +18,7 @@ package android.accessibilityservice;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityWindowInfo;
import android.view.KeyEvent;
/**
@@ -35,7 +36,9 @@ import android.view.KeyEvent;
void onGesture(int gesture);
- void clearAccessibilityNodeInfoCache();
+ void clearAccessibilityCache();
void onKeyEvent(in KeyEvent event, int sequence);
+
+ void onWindowsChanged(in int[] changedWindowIds);
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 3df06b5..5f7a17d 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -21,6 +21,7 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.view.MagnificationSpec;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import android.view.accessibility.AccessibilityWindowInfo;
/**
* Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService.
@@ -53,6 +54,10 @@ interface IAccessibilityServiceConnection {
int action, in Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long threadId);
+ AccessibilityWindowInfo getWindow(int windowId);
+
+ List<AccessibilityWindowInfo> getWindows();
+
AccessibilityServiceInfo getServiceInfo();
boolean performGlobalAction(int action);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 079cf7a..b616c1e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -780,6 +780,25 @@ public class AppOpsManager {
}
}
+ /**
+ * Set a non-persisted restriction on an audio operation at a stream-level.
+ * Restrictions are temporary additional constraints imposed on top of the persisted rules
+ * defined by {@link #setMode}.
+ *
+ * @param code The operation to restrict.
+ * @param stream The {@link android.media.AudioManager} stream type.
+ * @param mode The restriction mode (MODE_IGNORED,MODE_ERRORED) or MODE_ALLOWED to unrestrict.
+ * @param exceptionPackages Optional list of packages to exclude from the restriction.
+ * @hide
+ */
+ public void setRestriction(int code, int stream, int mode, String[] exceptionPackages) {
+ try {
+ final int uid = Binder.getCallingUid();
+ mService.setAudioRestriction(code, stream, uid, mode, exceptionPackages);
+ } catch (RemoteException e) {
+ }
+ }
+
/** @hide */
public void resetAllModes() {
try {
@@ -1009,6 +1028,35 @@ public class AppOpsManager {
}
/**
+ * Like {@link #checkOp} but at a stream-level for audio operations.
+ * @hide
+ */
+ public int checkAudioOp(int op, int stream, int uid, String packageName) {
+ try {
+ final int mode = mService.checkAudioOperation(op, stream, uid, packageName);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
+ }
+ return mode;
+ } catch (RemoteException e) {
+ }
+ return MODE_IGNORED;
+ }
+
+ /**
+ * Like {@link #checkAudioOp} but instead of throwing a {@link SecurityException} it
+ * returns {@link #MODE_ERRORED}.
+ * @hide
+ */
+ public int checkAudioOpNoThrow(int op, int stream, int uid, String packageName) {
+ try {
+ return mService.checkAudioOperation(op, stream, uid, packageName);
+ } catch (RemoteException e) {
+ }
+ return MODE_IGNORED;
+ }
+
+ /**
* Make note of an application performing an operation. Note that you must pass
* in both the uid and name of the application to be checked; this function will verify
* that these two match, and if not, return {@link #MODE_IGNORED}. If this call
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 087f83c..4ca06ed 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -76,8 +76,9 @@ oneway interface IBackupAgent {
* @param callbackBinder Binder on which to indicate operation completion,
* passed here as a convenience to the agent.
*/
- void doRestore(in ParcelFileDescriptor data, int appVersionCode,
- in ParcelFileDescriptor newState, int token, IBackupManager callbackBinder);
+ void doRestore(in ParcelFileDescriptor data,
+ int appVersionCode, in ParcelFileDescriptor newState,
+ int token, IBackupManager callbackBinder);
/**
* Perform a "full" backup to the given file descriptor. The output file is presumed
@@ -112,8 +113,15 @@ oneway interface IBackupAgent {
* @param path Relative path of the file within its semantic domain.
* @param mode Access mode of the file system entity, e.g. 0660.
* @param mtime Last modification time of the file system entity.
+ * @param token Opaque token identifying this transaction. This must
+ * be echoed back to the backup service binder once the agent is
+ * finished restoring the application based on the restore data
+ * contents.
+ * @param callbackBinder Binder on which to indicate operation completion,
+ * passed here as a convenience to the agent.
*/
void doRestoreFile(in ParcelFileDescriptor data, long size,
int type, String domain, String path, long mode, long mtime,
int token, IBackupManager callbackBinder);
+
}
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 498fa42..354a19f 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -36,9 +36,11 @@ import android.view.Surface;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.TimeoutException;
/**
@@ -269,10 +271,10 @@ public final class UiAutomation {
* @param action The action to perform.
* @return Whether the action was successfully performed.
*
- * @see AccessibilityService#GLOBAL_ACTION_BACK
- * @see AccessibilityService#GLOBAL_ACTION_HOME
- * @see AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS
- * @see AccessibilityService#GLOBAL_ACTION_RECENTS
+ * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK
+ * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_HOME
+ * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS
+ * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_RECENTS
*/
public final boolean performGlobalAction(int action) {
final IAccessibilityServiceConnection connection;
@@ -346,6 +348,33 @@ public final class UiAutomation {
}
/**
+ * Gets the windows on the screen. This method returns only the windows
+ * that a sighted user can interact with, as opposed to all windows.
+ * For example, if there is a modal dialog shown and the user cannot touch
+ * anything behind it, then only the modal window will be reported
+ * (assuming it is the top one). For convenience the returned windows
+ * are ordered in a descending layer order, which is the windows that
+ * are higher in the Z-order are reported first.
+ * <p>
+ * <strong>Note:</strong> In order to access the windows you have to opt-in
+ * to retrieve the interactive windows by setting the
+ * {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
+ * </p>
+ *
+ * @return The windows if there are windows such, otherwise an empty list.
+ */
+ public List<AccessibilityWindowInfo> getWindows() {
+ final int connectionId;
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ connectionId = mConnectionId;
+ }
+ // Calling out without a lock held.
+ return AccessibilityInteractionClient.getInstance()
+ .getWindows(connectionId);
+ }
+
+ /**
* Gets the root {@link AccessibilityNodeInfo} in the active window.
*
* @return The root info.
@@ -632,7 +661,7 @@ public final class UiAutomation {
* potentially undesirable actions such as calling 911 or posting on public forums etc.
*
* @param enable whether to run in a "monkey" mode or not. Default is not.
- * @see {@link ActivityManager#isUserAMonkey()}
+ * @see {@link android.app.ActivityManager#isUserAMonkey()}
*/
public void setRunAsMonkey(boolean enable) {
synchronized (mLock) {
diff --git a/core/java/android/app/backup/BackupDataOutput.java b/core/java/android/app/backup/BackupDataOutput.java
index 3a070b6..845784f 100644
--- a/core/java/android/app/backup/BackupDataOutput.java
+++ b/core/java/android/app/backup/BackupDataOutput.java
@@ -17,6 +17,7 @@
package android.app.backup;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -76,13 +77,19 @@ public class BackupDataOutput {
/**
* Mark the beginning of one record in the backup data stream. This must be called before
* {@link #writeEntityData}.
- * @param key A string key that uniquely identifies the data record within the application
+ * @param key A string key that uniquely identifies the data record within the application.
+ * Keys whose first character is \uFF00 or higher are not valid.
* @param dataSize The size in bytes of this record's data. Passing a dataSize
* of -1 indicates that the record under this key should be deleted.
* @return The number of bytes written to the backup stream
* @throws IOException if the write failed
*/
public int writeEntityHeader(String key, int dataSize) throws IOException {
+ if (key != null && key.charAt(0) >= 0xff00) {
+ if (Process.myUid() != Process.SYSTEM_UID) {
+ throw new IllegalArgumentException("Invalid key " + key);
+ }
+ }
int result = writeEntityHeader_native(mBackupWriter, key, dataSize);
if (result >= 0) {
return result;
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 12ee3b6..c629a2e 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -167,7 +167,7 @@ interface IBackupManager {
* are to be backed up. The <code>allApps</code> parameter supersedes this.
*/
void fullBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
- boolean includeShared, boolean allApps, boolean allIncludesSystem,
+ boolean includeShared, boolean doWidgets, boolean allApps, boolean allIncludesSystem,
in String[] packageNames);
/**
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 8a89cbc..dd3a871 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -214,6 +214,12 @@ public class AppWidgetManager {
public static final String EXTRA_CUSTOM_INFO = "customInfo";
/**
+ * An intent extra attached to the {@link #ACTION_APPWIDGET_HOST_RESTORED} broadcast,
+ * indicating the integer ID of the host whose widgets have just been restored.
+ */
+ public static final String EXTRA_HOST_ID = "hostId";
+
+ /**
* An intent extra to pass to the AppWidget picker containing a {@link java.util.List} of
* {@link android.os.Bundle} objects to mix in to the list of AppWidgets that are
* installed. It will be added to the extras object on the {@link android.content.Intent}
@@ -310,6 +316,86 @@ public class AppWidgetManager {
public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
/**
+ * Sent to providers after AppWidget state related to the provider has been restored from
+ * backup. The intent contains information about how to translate AppWidget ids from the
+ * restored data to their new equivalents.
+ *
+ * <p>The intent will contain the following extras:
+ *
+ * <table>
+ * <tr>
+ * <td>{@link #EXTRA_APPWIDGET_OLD_IDS}</td>
+ * <td>The set of appWidgetIds represented in a restored backup that have been successfully
+ * incorporated into the current environment. This may be all of the AppWidgets known
+ * to this application, or just a subset. Each entry in this array of appWidgetIds has
+ * a corresponding entry in the {@link #EXTRA_APPWIDGET_IDS} extra.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXTRA_APPWIDGET_IDS}</td>
+ * <td>The set of appWidgetIds now valid for this application. The app should look at
+ * its restored widget configuration and translate each appWidgetId in the
+ * {@link #EXTRA_APPWIDGET_OLD_IDS} array to its new value found at the corresponding
+ * index within this array.</td>
+ * </tr>
+ * </table>
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ *
+ * @see {@link #ACTION_APPWIDGET_HOST_RESTORED} for the corresponding host broadcast
+ */
+ public static final String ACTION_APPWIDGET_RESTORED
+ = "android.appwidget.action.APPWIDGET_RESTORED";
+
+ /**
+ * Sent to widget hosts after AppWidget state related to the host has been restored from
+ * backup. The intent contains information about how to translate AppWidget ids from the
+ * restored data to their new equivalents. If an application maintains multiple separate
+ * widget hosts instances, it will receive this broadcast separately for each one.
+ *
+ * <p>The intent will contain the following extras:
+ *
+ * <table>
+ * <tr>
+ * <td>{@link #EXTRA_APPWIDGET_OLD_IDS}</td>
+ * <td>The set of appWidgetIds represented in a restored backup that have been successfully
+ * incorporated into the current environment. This may be all of the AppWidgets known
+ * to this application, or just a subset. Each entry in this array of appWidgetIds has
+ * a corresponding entry in the {@link #EXTRA_APPWIDGET_IDS} extra.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXTRA_APPWIDGET_IDS}</td>
+ * <td>The set of appWidgetIds now valid for this application. The app should look at
+ * its restored widget configuration and translate each appWidgetId in the
+ * {@link #EXTRA_APPWIDGET_OLD_IDS} array to its new value found at the corresponding
+ * index within this array.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXTRA_HOST_ID}</td>
+ * <td>The integer ID of the widget host instance whose state has just been restored.</td>
+ * </tr>
+ * </table>
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ *
+ * @see {@link #ACTION_APPWIDGET_RESTORED} for the corresponding provider broadcast
+ */
+ public static final String ACTION_APPWIDGET_HOST_RESTORED
+ = "android.appwidget.action.APPWIDGET_HOST_RESTORED";
+
+ /**
+ * An intent extra that contains multiple appWidgetIds. These are id values as
+ * they were provided to the application during a recent restore from backup. It is
+ * attached to the {@link #ACTION_APPWIDGET_RESTORED} broadcast intent.
+ *
+ * <p>
+ * The value will be an int array that can be retrieved like this:
+ * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/TestAppWidgetProvider.java getExtra_EXTRA_APPWIDGET_IDS}
+ */
+ public static final String EXTRA_APPWIDGET_OLD_IDS = "appWidgetOldIds";
+
+ /**
* Field for the manifest meta-data tag.
*
* @see AppWidgetProviderInfo
diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java
index edf142b..ab91edf 100644
--- a/core/java/android/appwidget/AppWidgetProvider.java
+++ b/core/java/android/appwidget/AppWidgetProvider.java
@@ -66,15 +66,13 @@ public class AppWidgetProvider extends BroadcastReceiver {
this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
}
}
- }
- else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
+ } else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
this.onDeleted(context, new int[] { appWidgetId });
}
- }
- else if (AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
+ } else if (AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)
&& extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS)) {
@@ -83,19 +81,28 @@ public class AppWidgetProvider extends BroadcastReceiver {
this.onAppWidgetOptionsChanged(context, AppWidgetManager.getInstance(context),
appWidgetId, widgetExtras);
}
- }
- else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
+ } else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
this.onEnabled(context);
- }
- else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
+ } else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
this.onDisabled(context);
+ } else if (AppWidgetManager.ACTION_APPWIDGET_RESTORED.equals(action)) {
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ int[] oldIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
+ int[] newIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
+ if (oldIds != null && oldIds.length > 0) {
+ this.onRestored(context, oldIds, newIds);
+ this.onUpdate(context, AppWidgetManager.getInstance(context), newIds);
+ }
+ }
}
}
// END_INCLUDE(onReceive)
/**
- * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_UPDATE} broadcast when
- * this AppWidget provider is being asked to provide {@link android.widget.RemoteViews RemoteViews}
+ * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_UPDATE} and
+ * {@link AppWidgetManager#ACTION_APPWIDGET_RESTORED} broadcasts when this AppWidget
+ * provider is being asked to provide {@link android.widget.RemoteViews RemoteViews}
* for a set of AppWidgets. Override this method to implement your own AppWidget functionality.
*
* {@more}
@@ -123,8 +130,8 @@ public class AppWidgetProvider extends BroadcastReceiver {
* running.
* @param appWidgetManager A {@link AppWidgetManager} object you can call {@link
* AppWidgetManager#updateAppWidget} on.
- * @param appWidgetId The appWidgetId of the widget who's size changed.
- * @param newOptions The appWidgetId of the widget who's size changed.
+ * @param appWidgetId The appWidgetId of the widget whose size changed.
+ * @param newOptions The appWidgetId of the widget whose size changed.
*
* @see AppWidgetManager#ACTION_APPWIDGET_OPTIONS_CHANGED
*/
@@ -181,4 +188,24 @@ public class AppWidgetProvider extends BroadcastReceiver {
*/
public void onDisabled(Context context) {
}
+
+ /**
+ * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_RESTORED} broadcast
+ * when instances of this AppWidget provider have been restored from backup. If your
+ * provider maintains any persistent data about its widget instances, override this method
+ * to remap the old AppWidgetIds to the new values and update any other app state that may
+ * be relevant.
+ *
+ * <p>This callback will be followed immediately by a call to {@link #onUpdate} so your
+ * provider can immediately generate new RemoteViews suitable for its newly-restored set
+ * of instances.
+ *
+ * {@more}
+ *
+ * @param context
+ * @param oldWidgetIds
+ * @param newWidgetIds
+ */
+ public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 38a71aa..a4374b8 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -19,7 +19,9 @@ package android.bluetooth;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -180,43 +182,6 @@ public final class BluetoothAdapter {
"android.bluetooth.adapter.extra.DISCOVERABLE_DURATION";
/**
- * Activity Action: Show a system activity to request BLE advertising.<br>
- * If the device is not doing BLE advertising, this activity will start BLE advertising for the
- * device, otherwise it will continue BLE advertising using the current
- * {@link BluetoothAdvScanData}. <br>
- * Note this activity will also request the user to turn on Bluetooth if it's not currently
- * enabled.
- * @hide
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_START_ADVERTISING =
- "android.bluetooth.adapter.action.START_ADVERTISING";
-
- /**
- * Activity Action: Stop the current BLE advertising.
- * @hide
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_STOP_ADVERTISING =
- "android.bluetooth.adapter.action.STOP_ADVERTISING";
-
- /**
- * Broadcast Action: Indicate BLE Advertising is started.
- * @hide
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED =
- "android.bluetooth.adapter.action.ADVERTISING_STARTED";
-
- /**
- * Broadcast Action: Indicated BLE Advertising is stopped.
- * @hide
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED =
- "android.bluetooth.adapter.action.ADVERTISING_STOPPED";
-
- /**
* Activity Action: Show a system activity that allows the user to turn on
* Bluetooth.
* <p>This system activity will return once Bluetooth has completed turning
@@ -248,6 +213,22 @@ public final class BluetoothAdapter {
"android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
/**
+ * Broadcast Action: Indicate BLE Advertising is started.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED =
+ "android.bluetooth.adapter.action.ADVERTISING_STARTED";
+
+ /**
+ * Broadcast Action: Indicated BLE Advertising is stopped.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED =
+ "android.bluetooth.adapter.action.ADVERTISING_STOPPED";
+
+ /**
* Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
* intents to request the current scan mode. Possible values are:
* {@link #SCAN_MODE_NONE},
@@ -383,9 +364,27 @@ public final class BluetoothAdapter {
/** The profile is in disconnecting state */
public static final int STATE_DISCONNECTING = 3;
+ /** States for Bluetooth LE advertising */
+ /** @hide */
+ public static final int STATE_ADVERTISE_STARTING = 0;
+ /** @hide */
+ public static final int STATE_ADVERTISE_STARTED = 1;
+ /** @hide */
+ public static final int STATE_ADVERTISE_STOPPING = 2;
+ /** @hide */
+ public static final int STATE_ADVERTISE_STOPPED = 3;
+ /**
+ * Force stopping advertising without callback in case the advertising app dies.
+ * @hide
+ */
+ public static final int STATE_ADVERTISE_FORCE_STOPPING = 4;
+
/** @hide */
public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
+ /** @hide */
+ public static final int ADVERTISE_CALLBACK_SUCCESS = 0;
+
private static final int ADDRESS_LENGTH = 17;
/**
@@ -399,7 +398,9 @@ public final class BluetoothAdapter {
private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients;
private BluetoothAdvScanData mBluetoothAdvScanData = null;
- private GattCallbackWrapper mAdvertisingCallback;
+ private GattCallbackWrapper mAdvertisingGattCallback;
+ private final Handler mHandler; // Handler to post the advertise callback to run on main thread.
+ private final Object mLock = new Object();
/**
* Get a handle to the default local Bluetooth adapter.
@@ -435,6 +436,7 @@ public final class BluetoothAdapter {
} catch (RemoteException e) {Log.e(TAG, "", e);}
mManagerService = managerService;
mLeScanClients = new HashMap<LeScanCallback, GattCallbackWrapper>();
+ mHandler = new Handler(Looper.getMainLooper());
}
/**
@@ -474,6 +476,7 @@ public final class BluetoothAdapter {
/**
* Returns a {@link BluetoothAdvScanData} object representing advertising data.
+ * Data will be reset when bluetooth service is turned off.
* @hide
*/
public BluetoothAdvScanData getAdvScanData() {
@@ -494,19 +497,34 @@ public final class BluetoothAdapter {
}
}
+ /**
+ * Interface for BLE advertising callback.
+ *
+ * @hide
+ */
+ public interface AdvertiseCallback {
+ /**
+ * Callback when advertise starts.
+ * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure.
+ */
+ void onAdvertiseStart(int status);
+ /**
+ * Callback when advertise stops.
+ * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure.
+ */
+ void onAdvertiseStop(int status);
+ }
/**
* Start BLE advertising using current {@link BluetoothAdvScanData}.
- * An app should start advertising by requesting
- * {@link BluetoothAdapter#ACTION_START_ADVERTISING} instead of calling this method directly.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
*
- * @return true if BLE avertising succeeds, false otherwise.
+ * @param callback - {@link AdvertiseCallback}
+ * @return true if BLE advertising succeeds, false otherwise.
* @hide
*/
- public boolean startAdvertising() {
+ public boolean startAdvertising(final AdvertiseCallback callback) {
if (getState() != STATE_ON) return false;
-
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
@@ -516,18 +534,31 @@ public final class BluetoothAdapter {
// Restart/reset advertising packets if advertising is in progress.
if (isAdvertising()) {
// Invalid advertising callback.
- if (mAdvertisingCallback == null || mAdvertisingCallback.mLeHandle == -1) {
+ if (mAdvertisingGattCallback == null || mAdvertisingGattCallback.mLeHandle == -1) {
Log.e(TAG, "failed to restart advertising, invalid callback");
return false;
}
- iGatt.startAdvertising(mAdvertisingCallback.mLeHandle);
+ iGatt.startAdvertising(mAdvertisingGattCallback.mLeHandle);
+ // Run the callback from main thread.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // callback with status success.
+ callback.onAdvertiseStart(ADVERTISE_CALLBACK_SUCCESS);
+ }
+ });
return true;
}
UUID uuid = UUID.randomUUID();
GattCallbackWrapper wrapper =
- new GattCallbackWrapper(this, null, null, GattCallbackWrapper.CALLBACK_TYPE_ADV);
+ new GattCallbackWrapper(this, null, null, callback);
iGatt.registerClient(new ParcelUuid(uuid), wrapper);
- mAdvertisingCallback = wrapper;
+ if (!wrapper.advertiseStarted()) {
+ return false;
+ }
+ synchronized (mLock) {
+ mAdvertisingGattCallback = wrapper;
+ }
return true;
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -537,25 +568,28 @@ public final class BluetoothAdapter {
/**
* Stop BLE advertising.
- * An app should stop advertising by requesting
- * {@link BluetoothAdapter#ACTION_STOP_ADVERTISING} instead of calling this method directly.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
+ *
+ * @param callback - {@link AdvertiseCallback}
* @return true if BLE advertising stops, false otherwise.
* @hide
*/
- public boolean stopAdvertisting() {
+ public boolean stopAdvertising(AdvertiseCallback callback) {
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
// BLE is not supported
return false;
}
- if (mAdvertisingCallback == null) {
+ if (mAdvertisingGattCallback == null) {
// no callback.
return false;
}
- mAdvertisingCallback.stopAdvertising();
- mAdvertisingCallback = null;
+ // Make sure same callback is used for start and stop advertising.
+ if (callback != mAdvertisingGattCallback.mAdvertiseCallback) {
+ Log.e(TAG, "must use the same callback for star/stop advertising");
+ return false;
+ }
+ mAdvertisingGattCallback.stopAdvertising();
return true;
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1415,6 +1449,8 @@ public final class BluetoothAdapter {
if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
synchronized (mManagerCallback) {
mService = null;
+ // Reset bluetooth adv scan data when Gatt service is down.
+ mBluetoothAdvScanData = null;
for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
try {
if (cb != null) {
@@ -1689,11 +1725,9 @@ public final class BluetoothAdapter {
private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub {
private static final int LE_CALLBACK_REG_TIMEOUT = 2000;
private static final int LE_CALLBACK_REG_WAIT_COUNT = 5;
- private static final int CALLBACK_TYPE_SCAN = 0;
- private static final int CALLBACK_TYPE_ADV = 1;
+ private final AdvertiseCallback mAdvertiseCallback;
private final LeScanCallback mLeScanCb;
- private int mCallbackType;
// mLeHandle 0: not registered
// -1: scan stopped
@@ -1708,26 +1742,34 @@ public final class BluetoothAdapter {
mLeScanCb = leScanCb;
mScanFilter = uuid;
mLeHandle = 0;
- mCallbackType = CALLBACK_TYPE_SCAN;
+ mAdvertiseCallback = null;
}
public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb,
- UUID[] uuid, int type) {
+ UUID[] uuid, AdvertiseCallback callback) {
mBluetoothAdapter = new WeakReference<BluetoothAdapter>(bluetoothAdapter);
mLeScanCb = leScanCb;
mScanFilter = uuid;
mLeHandle = 0;
- mCallbackType = type;
+ mAdvertiseCallback = callback;
}
public boolean scanStarted() {
+ return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT);
+ }
+
+ public boolean advertiseStarted() {
+ // Wait for registeration callback.
+ return waitForRegisteration(1);
+ }
+
+ private boolean waitForRegisteration(int maxWaitCount) {
boolean started = false;
synchronized(this) {
if (mLeHandle == -1) return false;
-
int count = 0;
// wait for callback registration and LE scan to start
- while (mLeHandle == 0 && count < LE_CALLBACK_REG_WAIT_COUNT) {
+ while (mLeHandle == 0 && count < maxWaitCount) {
try {
wait(LE_CALLBACK_REG_TIMEOUT);
} catch (InterruptedException e) {
@@ -1751,15 +1793,12 @@ public final class BluetoothAdapter {
try {
IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt();
iGatt.stopAdvertising();
- Log.d(TAG, "unregeistering client " + mLeHandle);
- iGatt.unregisterClient(mLeHandle);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to stop advertising and unregister" + e);
+ Log.e(TAG, "Failed to stop advertising" + e);
}
} else {
Log.e(TAG, "stopAdvertising, BluetoothAdapter is null");
}
- mLeHandle = -1;
notifyAll();
}
}
@@ -1805,7 +1844,7 @@ public final class BluetoothAdapter {
BluetoothAdapter adapter = mBluetoothAdapter.get();
if (adapter != null) {
iGatt = adapter.getBluetoothManager().getBluetoothGatt();
- if (mCallbackType == CALLBACK_TYPE_ADV) {
+ if (mAdvertiseCallback != null) {
iGatt.startAdvertising(mLeHandle);
} else {
if (mScanFilter == null) {
@@ -1855,7 +1894,7 @@ public final class BluetoothAdapter {
* @hide
*/
public void onScanResult(String address, int rssi, byte[] advData) {
- if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
+ if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
// Check null in case the scan has been stopped
synchronized(this) {
@@ -1944,9 +1983,33 @@ public final class BluetoothAdapter {
// no op
}
- public void onListen(int status) {
- // no op
+ public void onAdvertiseStateChange(int advertiseState, int status) {
+ Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status);
+ if (advertiseState == STATE_ADVERTISE_STARTED) {
+ mAdvertiseCallback.onAdvertiseStart(status);
+ } else {
+ synchronized (this) {
+ if (status == ADVERTISE_CALLBACK_SUCCESS) {
+ BluetoothAdapter adapter = mBluetoothAdapter.get();
+ if (adapter != null) {
+ try {
+ IBluetoothGatt iGatt =
+ adapter.getBluetoothManager().getBluetoothGatt();
+ Log.d(TAG, "unregistering client " + mLeHandle);
+ iGatt.unregisterClient(mLeHandle);
+ // Reset advertise app handle.
+ mLeHandle = -1;
+ adapter.mAdvertisingGattCallback = null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to unregister client" + e);
+ }
+ } else {
+ Log.e(TAG, "cannot unregister client, BluetoothAdapter is null");
+ }
+ }
+ }
+ mAdvertiseCallback.onAdvertiseStop(status);
+ }
}
}
-
}
diff --git a/core/java/android/bluetooth/BluetoothAdvScanData.java b/core/java/android/bluetooth/BluetoothAdvScanData.java
index a97b0a8..df2c256 100644
--- a/core/java/android/bluetooth/BluetoothAdvScanData.java
+++ b/core/java/android/bluetooth/BluetoothAdvScanData.java
@@ -77,6 +77,7 @@ public final class BluetoothAdvScanData {
try {
return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData);
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to set manufacturer id and data.", e);
return false;
}
}
@@ -92,6 +93,7 @@ public final class BluetoothAdvScanData {
try {
return mBluetoothGatt.setAdvServiceData(serviceData);
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to set service data.", e);
return false;
}
}
@@ -103,6 +105,7 @@ public final class BluetoothAdvScanData {
try {
return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids());
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to get service uuids.", e);
return null;
}
}
@@ -115,6 +118,7 @@ public final class BluetoothAdvScanData {
try {
return mBluetoothGatt.getAdvManufacturerData();
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to get manufacturer data.", e);
return null;
}
}
@@ -127,6 +131,7 @@ public final class BluetoothAdvScanData {
try {
return mBluetoothGatt.getAdvServiceData();
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to get service data.", e);
return null;
}
}
@@ -140,7 +145,7 @@ public final class BluetoothAdvScanData {
try {
mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode);
} catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ Log.e(TAG, "Unable to remove manufacturer : " + manufacturerCode, e);
}
}
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index e3820a2..39305b0 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,8 +16,6 @@
package android.bluetooth;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -544,6 +542,15 @@ public final class BluetoothGatt implements BluetoothProfile {
Log.w(TAG, "Unhandled exception in callback", ex);
}
}
+
+ /**
+ * Advertise state change callback
+ * @hide
+ */
+ public void onAdvertiseStateChange(int state, int status) {
+ if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = "
+ + state + " status=" + status);
+ }
};
/*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) {
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index e3563fc..7c69a06 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -63,4 +63,5 @@ interface IBluetoothGattCallback {
in int charInstId, in ParcelUuid charUuid,
in byte[] value);
void onReadRemoteRssi(in String address, in int rssi, in int status);
+ oneway void onAdvertiseStateChange(in int advertiseState, in int status);
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 6e2a099..35c86e7 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -45,6 +45,7 @@ import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
/**
@@ -2149,10 +2150,20 @@ public class Camera {
private static final String PIXEL_FORMAT_JPEG = "jpeg";
private static final String PIXEL_FORMAT_BAYER_RGGB = "bayer-rggb";
- private HashMap<String, String> mMap;
+ /**
+ * Order matters: Keys that are {@link #set(String, String) set} later
+ * will take precedence over keys that are set earlier (if the two keys
+ * conflict with each other).
+ *
+ * <p>One example is {@link #setPreviewFpsRange(int, int)} , since it
+ * conflicts with {@link #setPreviewFrameRate(int)} whichever key is set later
+ * is the one that will take precedence.
+ * </p>
+ */
+ private final LinkedHashMap<String, String> mMap;
private Parameters() {
- mMap = new HashMap<String, String>(64);
+ mMap = new LinkedHashMap<String, String>(/*initialCapacity*/64);
}
/**
@@ -2232,7 +2243,7 @@ public class Camera {
return;
}
- mMap.put(key, value);
+ put(key, value);
}
/**
@@ -2242,7 +2253,18 @@ public class Camera {
* @param value the int value of the parameter
*/
public void set(String key, int value) {
- mMap.put(key, Integer.toString(value));
+ put(key, Integer.toString(value));
+ }
+
+ private void put(String key, String value) {
+ /*
+ * Remove the key if it already exists.
+ *
+ * This way setting a new value for an already existing key will always move
+ * that key to be ordered the latest in the map.
+ */
+ mMap.remove(key);
+ mMap.put(key, value);
}
private void set(String key, List<Area> areas) {
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index f6a6f96..7656505 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1120,7 +1120,6 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable {
* light.</p>
* <p>If the sensor can't expose this exact duration, it should shorten the
* duration exposed to the nearest possible value (rather than expose longer).</p>
- * <p>1/10000 - 30 sec range. No bulb mode</p>
*/
public static final Key<Long> SENSOR_EXPOSURE_TIME =
new Key<Long>("android.sensor.exposureTime", long.class);
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 7eb63d6..03661f0 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -1490,7 +1490,6 @@ public final class CaptureResult extends CameraMetadata {
* light.</p>
* <p>If the sensor can't expose this exact duration, it should shorten the
* duration exposed to the nearest possible value (rather than expose longer).</p>
- * <p>1/10000 - 30 sec range. No bulb mode</p>
*/
public static final Key<Long> SENSOR_EXPOSURE_TIME =
new Key<Long>("android.sensor.exposureTime", long.class);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index e3a3830..0c0dfe9 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -853,13 +853,21 @@ public final class InputManager {
return true;
}
+ /**
+ * @hide
+ */
@Override
- public void vibrate(long milliseconds) {
+ public void vibrate(int owningUid, String owningPackage, long milliseconds,
+ int streamHint) {
vibrate(new long[] { 0, milliseconds}, -1);
}
+ /**
+ * @hide
+ */
@Override
- public void vibrate(long[] pattern, int repeat) {
+ public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat,
+ int streamHint) {
if (repeat >= pattern.length) {
throw new ArrayIndexOutOfBoundsException();
}
@@ -870,22 +878,6 @@ public final class InputManager {
}
}
- /**
- * @hide
- */
- @Override
- public void vibrate(int owningUid, String owningPackage, long milliseconds) {
- vibrate(milliseconds);
- }
-
- /**
- * @hide
- */
- @Override
- public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) {
- vibrate(pattern, repeat);
- }
-
@Override
public void cancel() {
try {
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index 6c61046..10b5d0b 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -183,8 +183,10 @@ public class EthernetDataTracker extends BaseNetworkStateTracker {
Thread dhcpThread = new Thread(new Runnable() {
public void run() {
DhcpResults dhcpResults = new DhcpResults();
+ mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);
if (!NetworkUtils.runDhcp(mIface, dhcpResults)) {
Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
+ mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
return;
}
mLinkProperties = dhcpResults.linkProperties;
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 3c3d8ec..a470e88 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -561,6 +561,17 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker {
return false;
}
+
+ public void setInternalDataEnable(boolean enabled) {
+ if (DBG) log("setInternalDataEnable: E enabled=" + enabled);
+ final AsyncChannel channel = mDataConnectionTrackerAc;
+ if (channel != null) {
+ channel.sendMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE,
+ enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
+ }
+ if (VDBG) log("setInternalDataEnable: X enabled=" + enabled);
+ }
+
@Override
public void setUserDataEnable(boolean enabled) {
if (DBG) log("setUserDataEnable: E enabled=" + enabled);
diff --git a/core/java/android/os/BatteryProperty.java b/core/java/android/os/BatteryProperty.java
index 346f5de..76b0dc4 100644
--- a/core/java/android/os/BatteryProperty.java
+++ b/core/java/android/os/BatteryProperty.java
@@ -29,6 +29,7 @@ public class BatteryProperty implements Parcelable {
public static final int BATTERY_PROP_CHARGE_COUNTER = 1;
public static final int BATTERY_PROP_CURRENT_NOW = 2;
public static final int BATTERY_PROP_CURRENT_AVG = 3;
+ public static final int BATTERY_PROP_CAPACITY = 4;
public int valueInt;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 7db4ac2..b0d94d5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -570,13 +570,15 @@ public abstract class BatteryStats implements Parcelable {
public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18;
public static final int STATE_WIFI_ON_FLAG = 1<<17;
public static final int STATE_BLUETOOTH_ON_FLAG = 1<<16;
-
+
public static final int MOST_INTERESTING_STATES =
STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG
| STATE_GPS_ON_FLAG | STATE_PHONE_IN_CALL_FLAG;
public int states;
+ public int states2;
+
// The wake lock that was acquired at this point.
public HistoryTag wakelockTag;
@@ -1216,6 +1218,11 @@ public abstract class BatteryStats implements Parcelable {
public abstract int getHighDischargeAmountSinceCharge();
/**
+ * Retrieve the discharge amount over the selected discharge period <var>which</var>.
+ */
+ public abstract int getDischargeAmount(int which);
+
+ /**
* Get the amount the battery has discharged while the screen was on,
* since the last time power was unplugged.
*/
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 54e2c0b..e96398a 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -41,6 +41,7 @@ public class Environment {
private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE";
private static final String ENV_SECONDARY_STORAGE = "SECONDARY_STORAGE";
private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
+ private static final String ENV_OEM_ROOT = "OEM_ROOT";
/** {@hide} */
public static final String DIR_ANDROID = "Android";
@@ -55,6 +56,7 @@ public class Environment {
public static final String DIRECTORY_ANDROID = DIR_ANDROID;
private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
+ private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media");
private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull(
@@ -205,13 +207,24 @@ public class Environment {
}
/**
- * Gets the Android root directory.
+ * Return root of the "system" partition holding the core Android OS.
+ * Always present and mounted read-only.
*/
public static File getRootDirectory() {
return DIR_ANDROID_ROOT;
}
/**
+ * Return root directory of the "oem" partition holding OEM customizations,
+ * if any. If present, the partition is mounted read-only.
+ *
+ * @hide
+ */
+ public static File getOemDirectory() {
+ return DIR_OEM_ROOT;
+ }
+
+ /**
* Gets the system directory available for secure storage.
* If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system).
* Otherwise, it returns the unencrypted /data/system directory.
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index 456ffb1..4854bc0 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -20,8 +20,8 @@ package android.os;
interface IVibratorService
{
boolean hasVibrator();
- void vibrate(int uid, String packageName, long milliseconds, IBinder token);
- void vibratePattern(int uid, String packageName, in long[] pattern, int repeat, IBinder token);
+ void vibrate(int uid, String packageName, long milliseconds, int streamHint, IBinder token);
+ void vibratePattern(int uid, String packageName, in long[] pattern, int repeat, int streamHint, IBinder token);
void cancelVibrate(IBinder token);
}
diff --git a/core/java/android/os/NullVibrator.java b/core/java/android/os/NullVibrator.java
index af90bdb..536da32 100644
--- a/core/java/android/os/NullVibrator.java
+++ b/core/java/android/os/NullVibrator.java
@@ -36,22 +36,11 @@ public class NullVibrator extends Vibrator {
return false;
}
- @Override
- public void vibrate(long milliseconds) {
- }
-
- @Override
- public void vibrate(long[] pattern, int repeat) {
- if (repeat >= pattern.length) {
- throw new ArrayIndexOutOfBoundsException();
- }
- }
-
/**
* @hide
*/
@Override
- public void vibrate(int owningUid, String owningPackage, long milliseconds) {
+ public void vibrate(int owningUid, String owningPackage, long milliseconds, int streamHint) {
vibrate(milliseconds);
}
@@ -59,8 +48,11 @@ public class NullVibrator extends Vibrator {
* @hide
*/
@Override
- public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) {
- vibrate(pattern, repeat);
+ public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat,
+ int streamHint) {
+ if (repeat >= pattern.length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
}
@Override
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 700f80d..13bc4f6 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -16,7 +16,6 @@
package android.os;
-import android.app.ActivityThread;
import android.content.Context;
import android.util.Log;
@@ -28,18 +27,16 @@ import android.util.Log;
public class SystemVibrator extends Vibrator {
private static final String TAG = "Vibrator";
- private final String mPackageName;
private final IVibratorService mService;
private final Binder mToken = new Binder();
public SystemVibrator() {
- mPackageName = ActivityThread.currentPackageName();
mService = IVibratorService.Stub.asInterface(
ServiceManager.getService("vibrator"));
}
public SystemVibrator(Context context) {
- mPackageName = context.getOpPackageName();
+ super(context);
mService = IVibratorService.Stub.asInterface(
ServiceManager.getService("vibrator"));
}
@@ -57,27 +54,17 @@ public class SystemVibrator extends Vibrator {
return false;
}
- @Override
- public void vibrate(long milliseconds) {
- vibrate(Process.myUid(), mPackageName, milliseconds);
- }
-
- @Override
- public void vibrate(long[] pattern, int repeat) {
- vibrate(Process.myUid(), mPackageName, pattern, repeat);
- }
-
/**
* @hide
*/
@Override
- public void vibrate(int owningUid, String owningPackage, long milliseconds) {
+ public void vibrate(int owningUid, String owningPackage, long milliseconds, int streamHint) {
if (mService == null) {
Log.w(TAG, "Failed to vibrate; no vibrator service.");
return;
}
try {
- mService.vibrate(owningUid, owningPackage, milliseconds, mToken);
+ mService.vibrate(owningUid, owningPackage, milliseconds, streamHint, mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
@@ -87,7 +74,8 @@ public class SystemVibrator extends Vibrator {
* @hide
*/
@Override
- public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) {
+ public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat,
+ int streamHint) {
if (mService == null) {
Log.w(TAG, "Failed to vibrate; no vibrator service.");
return;
@@ -97,7 +85,8 @@ public class SystemVibrator extends Vibrator {
// anyway
if (repeat < pattern.length) {
try {
- mService.vibratePattern(owningUid, owningPackage, pattern, repeat, mToken);
+ mService.vibratePattern(owningUid, owningPackage, pattern, repeat, streamHint,
+ mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 5d55143..8845ba3 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -16,7 +16,9 @@
package android.os;
+import android.app.ActivityThread;
import android.content.Context;
+import android.media.AudioManager;
/**
* Class that operates the vibrator on the device.
@@ -28,10 +30,21 @@ import android.content.Context;
* {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as the argument.
*/
public abstract class Vibrator {
+
+ private final String mPackageName;
+
/**
* @hide to prevent subclassing from outside of the framework
*/
public Vibrator() {
+ mPackageName = ActivityThread.currentPackageName();
+ }
+
+ /**
+ * @hide to prevent subclassing from outside of the framework
+ */
+ protected Vibrator(Context context) {
+ mPackageName = context.getOpPackageName();
}
/**
@@ -40,7 +53,7 @@ public abstract class Vibrator {
* @return True if the hardware has a vibrator, else false.
*/
public abstract boolean hasVibrator();
-
+
/**
* Vibrate constantly for the specified period of time.
* <p>This method requires the caller to hold the permission
@@ -48,7 +61,23 @@ public abstract class Vibrator {
*
* @param milliseconds The number of milliseconds to vibrate.
*/
- public abstract void vibrate(long milliseconds);
+ public void vibrate(long milliseconds) {
+ vibrate(milliseconds, AudioManager.USE_DEFAULT_STREAM_TYPE);
+ }
+
+ /**
+ * Vibrate constantly for the specified period of time.
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#VIBRATE}.
+ *
+ * @param milliseconds The number of milliseconds to vibrate.
+ * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type.
+ * For example, specify {@link AudioManager#STREAM_ALARM} for alarm vibrations or
+ * {@link AudioManager#STREAM_RING} for vibrations associated with incoming calls.
+ */
+ public void vibrate(long milliseconds, int streamHint) {
+ vibrate(Process.myUid(), mPackageName, milliseconds, streamHint);
+ }
/**
* Vibrate with a given pattern.
@@ -70,21 +99,52 @@ public abstract class Vibrator {
* @param repeat the index into pattern at which to repeat, or -1 if
* you don't want to repeat.
*/
- public abstract void vibrate(long[] pattern, int repeat);
+ public void vibrate(long[] pattern, int repeat) {
+ vibrate(pattern, repeat, AudioManager.USE_DEFAULT_STREAM_TYPE);
+ }
+
+ /**
+ * Vibrate with a given pattern.
+ *
+ * <p>
+ * Pass in an array of ints that are the durations for which to turn on or off
+ * the vibrator in milliseconds. The first value indicates the number of milliseconds
+ * to wait before turning the vibrator on. The next value indicates the number of milliseconds
+ * for which to keep the vibrator on before turning it off. Subsequent values alternate
+ * between durations in milliseconds to turn the vibrator off or to turn the vibrator on.
+ * </p><p>
+ * To cause the pattern to repeat, pass the index into the pattern array at which
+ * to start the repeat, or -1 to disable repeating.
+ * </p>
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#VIBRATE}.
+ *
+ * @param pattern an array of longs of times for which to turn the vibrator on or off.
+ * @param repeat the index into pattern at which to repeat, or -1 if
+ * you don't want to repeat.
+ * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type.
+ * For example, specify {@link AudioManager#STREAM_ALARM} for alarm vibrations or
+ * {@link AudioManager#STREAM_RING} for vibrations associated with incoming calls.
+ */
+ public void vibrate(long[] pattern, int repeat, int streamHint) {
+ vibrate(Process.myUid(), mPackageName, pattern, repeat, streamHint);
+ }
/**
* @hide
- * Like {@link #vibrate(long)}, but allowing the caller to specify that
+ * Like {@link #vibrate(long, int)}, but allowing the caller to specify that
* the vibration is owned by someone else.
*/
- public abstract void vibrate(int owningUid, String owningPackage, long milliseconds);
+ public abstract void vibrate(int owningUid, String owningPackage,
+ long milliseconds, int streamHint);
/**
* @hide
- * Like {@link #vibrate(long[], int)}, but allowing the caller to specify that
+ * Like {@link #vibrate(long[], int, int)}, but allowing the caller to specify that
* the vibration is owned by someone else.
*/
- public abstract void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat);
+ public abstract void vibrate(int owningUid, String owningPackage,
+ long[] pattern, int repeat, int streamHint);
/**
* Turn the vibrator off.
diff --git a/core/java/android/provider/SearchIndexableData.java b/core/java/android/provider/SearchIndexableData.java
new file mode 100644
index 0000000..6381884
--- /dev/null
+++ b/core/java/android/provider/SearchIndexableData.java
@@ -0,0 +1,117 @@
+/*
+ * 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.provider;
+
+import android.content.Context;
+
+import java.util.Locale;
+
+/**
+ * The Indexable data for Search. This abstract class defines the common parts for all search
+ * indexable data.
+ *
+ * @hide
+ */
+public abstract class SearchIndexableData {
+
+ /**
+ * The context for the data. Will usually allow to retrieve some resources.
+ *
+ * @see Context
+ */
+ public Context context;
+
+ /**
+ * The locale for the data
+ */
+ public Locale locale;
+
+ /**
+ * The rank for the data. This is application specific.
+ */
+ public int rank;
+
+ /**
+ * The class name associated with the data. Generally this is a Fragment class name for
+ * referring where the data is coming from and for launching the associated Fragment for
+ * displaying the data. This is used only when the data is provided "locally".
+ *
+ * If the data is provided "externally", the relevant information come from the
+ * {@link SearchIndexableData#intentAction} and {@link SearchIndexableData#intentTargetPackage}
+ * and {@link SearchIndexableData#intentTargetClass}.
+ *
+ * @see SearchIndexableData#intentAction
+ * @see SearchIndexableData#intentTargetPackage
+ * @see SearchIndexableData#intentTargetClass
+ */
+ public String className;
+
+ /**
+ * The package name for retrieving the icon associated with the data.
+ *
+ * @see SearchIndexableData#iconResId
+ */
+ public String packageName;
+
+ /**
+ * The icon resource ID associated with the data.
+ *
+ * @see SearchIndexableData#packageName
+ */
+ public int iconResId;
+
+ /**
+ * The Intent action associated with the data. This is used when the
+ * {@link SearchIndexableData#className} is not relevant.
+ *
+ * @see SearchIndexableData#intentTargetPackage
+ * @see SearchIndexableData#intentTargetClass
+ */
+ public String intentAction;
+
+ /**
+ * The Intent target package associated with the data.
+ *
+ * @see SearchIndexableData#intentAction
+ * @see SearchIndexableData#intentTargetClass
+ */
+ public String intentTargetPackage;
+
+ /**
+ * The Intent target class associated with the data.
+ *
+ * @see SearchIndexableData#intentAction
+ * @see SearchIndexableData#intentTargetPackage
+ */
+ public String intentTargetClass;
+
+ /**
+ * Default constructor.
+ */
+ public SearchIndexableData() {
+ }
+
+ /**
+ * Constructor with a {@link Context}.
+ *
+ * @param ctx the Context
+ */
+ public SearchIndexableData(Context ctx) {
+ context = ctx;
+ locale = Locale.getDefault();
+ }
+}
diff --git a/core/java/android/provider/SearchIndexableResource.java b/core/java/android/provider/SearchIndexableResource.java
new file mode 100644
index 0000000..ba3bd4f
--- /dev/null
+++ b/core/java/android/provider/SearchIndexableResource.java
@@ -0,0 +1,65 @@
+/*
+ * 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.provider;
+
+import android.content.Context;
+
+/**
+ * Search Indexable Resource.
+ *
+ * This class wraps a set of reference information representing data that can be indexed from a
+ * resource which would typically be a {@link android.preference.PreferenceScreen}.
+ *
+ * xmlResId: the resource ID of a {@link android.preference.PreferenceScreen} XML file.
+ *
+ * @see SearchIndexableData
+ * @see android.preference.PreferenceScreen
+ *
+ * @hide
+ */
+public class SearchIndexableResource extends SearchIndexableData {
+
+ /**
+ * Resource ID of the associated {@link android.preference.PreferenceScreen} XML file.
+ */
+ public int xmlResId;
+
+ /**
+ * Constructor.
+ *
+ * @param rank the rank of the data.
+ * @param xmlResId the resource ID of a {@link android.preference.PreferenceScreen} XML file.
+ * @param className the class name associated with the data (generally a
+ * {@link android.app.Fragment}).
+ * @param iconResId the resource ID associated with the data.
+ */
+ public SearchIndexableResource(int rank, int xmlResId, String className, int iconResId) {
+ this.rank = rank;
+ this.xmlResId = xmlResId;
+ this.className = className;
+ this.iconResId = iconResId;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param context the Context associated with the data.
+ */
+ public SearchIndexableResource(Context context) {
+ super(context);
+ }
+} \ No newline at end of file
diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
new file mode 100644
index 0000000..b8635b8
--- /dev/null
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -0,0 +1,179 @@
+/*
+ * 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.provider;
+
+import android.content.ContentResolver;
+
+/**
+ * Describe the contract for an Indexable data.
+ *
+ * @hide
+ */
+public class SearchIndexablesContract {
+
+ /**
+ * Intent action used to identify {@link SearchIndexablesProvider}
+ * instances. This is used in the {@code <intent-filter>} of a {@code <provider>}.
+ */
+ public static final String PROVIDER_INTERFACE =
+ "android.content.action.SEARCH_INDEXABLES_PROVIDER";
+
+ private static final String SETTINGS = "settings";
+
+ /**
+ * Indexable references name.
+ */
+ public static final String INDEXABLES_XML_RES = "indexables_xml_res";
+
+ /**
+ * ContentProvider path for indexable xml resources.
+ */
+ public static final String INDEXABLES_XML_RES_PATH = SETTINGS + "/" + INDEXABLES_XML_RES;
+
+ /**
+ * Indexable raw data name.
+ */
+ public static final String INDEXABLES_RAW = "indexables_raw";
+
+ /**
+ * ContentProvider path for indexable raw data.
+ */
+ public static final String INDEXABLES_RAW_PATH = SETTINGS + "/" + INDEXABLES_RAW;
+
+ /**
+ * Indexable xml resources colums.
+ */
+ public static final String[] INDEXABLES_XML_RES_COLUMNS = new String[] {
+ XmlResource.COLUMN_RANK,
+ XmlResource.COLUMN_XML_RESID,
+ XmlResource.COLUMN_CLASS_NAME,
+ XmlResource.COLUMN_ICON_RESID,
+ XmlResource.COLUMN_INTENT_ACTION,
+ XmlResource.COLUMN_INTENT_TARGET_PACKAGE,
+ XmlResource.COLUMN_INTENT_TARGET_CLASS
+ };
+
+ /**
+ * Indexable raw data colums.
+ */
+ public static final String[] INDEXABLES_RAW_COLUMNS = new String[] {
+ RawData.COLUMN_RANK,
+ RawData.COLUMN_TITLE,
+ RawData.COLUMN_SUMMARY,
+ RawData.COLUMN_KEYWORDS,
+ RawData.COLUMN_SCREEN_TITLE,
+ RawData.COLUMN_CLASS_NAME,
+ RawData.COLUMN_ICON_RESID,
+ RawData.COLUMN_INTENT_ACTION,
+ RawData.COLUMN_INTENT_TARGET_PACKAGE,
+ RawData.COLUMN_INTENT_TARGET_CLASS,
+ };
+
+ /**
+ * Constants related to a {@link SearchIndexableResource}.
+ *
+ * This is a description of
+ */
+ public static final class XmlResource extends BaseColumns {
+ private XmlResource() {
+ }
+
+ public static final String MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +
+ "/" + INDEXABLES_XML_RES;
+
+ /**
+ * XML resource ID for the {@link android.preference.PreferenceScreen} to load and index.
+ */
+ public static final String COLUMN_XML_RESID = "xmlResId";
+ }
+
+ /**
+ * Constants related to a {@link SearchIndexableData}.
+ *
+ * This is the raw data that is stored into an Index. This is related to
+ * {@link android.preference.Preference} and its attributes like
+ * {@link android.preference.Preference#getTitle()},
+ * {@link android.preference.Preference#getSummary()}, etc.
+ *
+ */
+ public static final class RawData extends BaseColumns {
+ private RawData() {
+ }
+
+ public static final String MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +
+ "/" + INDEXABLES_RAW;
+
+ /**
+ * Title's raw data.
+ */
+ public static final String COLUMN_TITLE = "title";
+
+ /**
+ * Summary's raw data.
+ */
+ public static final String COLUMN_SUMMARY = "summary";
+
+ /**
+ * Keywords' raw data.
+ */
+ public static final String COLUMN_KEYWORDS = "keywords";
+
+ /**
+ * Fragment's title associated with the raw data.
+ */
+ public static final String COLUMN_SCREEN_TITLE = "screenTitle";
+ }
+
+ /**
+ * The base columns.
+ */
+ private static class BaseColumns {
+ private BaseColumns() {
+ }
+
+ /**
+ * Rank of the data. This is an integer used for ranking the search results. This is
+ * application specific.
+ */
+ public static final String COLUMN_RANK = "rank";
+
+ /**
+ * Class name associated with the data (usually a Fragment class name).
+ */
+ public static final String COLUMN_CLASS_NAME = "className";
+
+ /**
+ * Icon resource ID for the data.
+ */
+ public static final String COLUMN_ICON_RESID = "iconResId";
+
+ /**
+ * Intent action associated with the data.
+ */
+ public static final String COLUMN_INTENT_ACTION = "intentAction";
+
+ /**
+ * Intent target package associated with the data.
+ */
+ public static final String COLUMN_INTENT_TARGET_PACKAGE = "intentTargetPackage";
+
+ /**
+ * Intent target class associated with the data.
+ */
+ public static final String COLUMN_INTENT_TARGET_CLASS = "intentTargetClass";
+ }
+}
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
new file mode 100644
index 0000000..2e358e4
--- /dev/null
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -0,0 +1,174 @@
+/*
+ * 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.provider;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Base class for a search indexable provider. Such provider offers data to be indexed either
+ * as a reference to an XML file (like a {@link android.preference.PreferenceScreen}) or either
+ * as some raw data.
+ *
+ * @see SearchIndexableResource
+ * @see SearchIndexableData
+ * @see SearchIndexablesContract
+ *
+ * To create a search indexables provider, extend this class, then implement the abstract methods,
+ * and add it to your manifest like this:
+ *
+ * <pre class="prettyprint">&lt;manifest&gt;
+ * ...
+ * &lt;application&gt;
+ * ...
+ * &lt;provider
+ * android:name="com.example.MyIndexablesProvider"
+ * android:authorities="com.example.myindexablesprovider"
+ * android:exported="true"
+ * android:grantUriPermissions="true"
+ * android:permission="android.permission.READ_SEARCH_INDEXABLES"
+ * &lt;intent-filter&gt;
+ * &lt;action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" /&gt;
+ * &lt;/intent-filter&gt;
+ * &lt;/provider&gt;
+ * ...
+ * &lt;/application&gt;
+ *&lt;/manifest&gt;</pre>
+ * <p>
+ * When defining your provider, you must protect it with
+ * {@link android.Manifest.permission#READ_SEARCH_INDEXABLES}, which is a permission only the system
+ * can obtain.
+ * </p>
+ *
+ * @hide
+ */
+public abstract class SearchIndexablesProvider extends ContentProvider {
+ private static final String TAG = "IndexablesProvider";
+
+ private String mAuthority;
+ private UriMatcher mMatcher;
+
+ private static final int MATCH_RES_CODE = 1;
+ private static final int MATCH_RAW_CODE = 2;
+
+ /**
+ * Implementation is provided by the parent class.
+ */
+ @Override
+ public void attachInfo(Context context, ProviderInfo info) {
+ mAuthority = info.authority;
+
+ mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_XML_RES_PATH,
+ MATCH_RES_CODE);
+ mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_RAW_PATH,
+ MATCH_RAW_CODE);
+
+ // Sanity check our setup
+ if (!info.exported) {
+ throw new SecurityException("Provider must be exported");
+ }
+ if (!info.grantUriPermissions) {
+ throw new SecurityException("Provider must grantUriPermissions");
+ }
+ if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(info.readPermission)) {
+ throw new SecurityException("Provider must be protected by READ_SEARCH_INDEXABLES");
+ }
+
+ super.attachInfo(context, info);
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ switch (mMatcher.match(uri)) {
+ case MATCH_RES_CODE:
+ return queryXmlResources(null);
+ case MATCH_RAW_CODE:
+ return queryRawData(null);
+ default:
+ throw new UnsupportedOperationException("Unknown Uri " + uri);
+ }
+ }
+
+ /**
+ * Returns all {@link android.provider.SearchIndexablesContract.XmlResource}.
+ *
+ * Those are usually xml resource ID to some {@link android.preference.PreferenceScreen}.
+ *
+ * @param projection list of {@link android.provider.SearchIndexablesContract.XmlResource}
+ * columns to put into the cursor. If {@code null} all supported columns
+ * should be included.
+ */
+ public abstract Cursor queryXmlResources(String[] projection);
+
+ /**
+ * Returns all {@link android.provider.SearchIndexablesContract.RawData}.
+ *
+ * Those are raw indexable data.
+ *
+ * @param projection list of {@link android.provider.SearchIndexablesContract.RawData} columns
+ * to put into the cursor. If {@code null} all supported columns should be
+ * included.
+ */
+ public abstract Cursor queryRawData(String[] projection);
+
+ @Override
+ public String getType(Uri uri) {
+ switch (mMatcher.match(uri)) {
+ case MATCH_RES_CODE:
+ return SearchIndexablesContract.XmlResource.MIME_TYPE;
+ case MATCH_RAW_CODE:
+ return SearchIndexablesContract.RawData.MIME_TYPE;
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overriden.
+ */
+ @Override
+ public final Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException("Insert not supported");
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overriden.
+ */
+ @Override
+ public final int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("Delete not supported");
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overriden.
+ */
+ @Override
+ public final int update(
+ Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("Update not supported");
+ }
+}
diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java
index 290a89b..d5f15f0 100644
--- a/core/java/android/util/LongArray.java
+++ b/core/java/android/util/LongArray.java
@@ -111,7 +111,6 @@ public class LongArray implements Cloneable {
}
@Override
- @SuppressWarnings("unchecked")
public LongArray clone() {
LongArray clone = null;
try {
@@ -154,7 +153,8 @@ public class LongArray implements Cloneable {
if (index >= mSize) {
throw new ArrayIndexOutOfBoundsException(mSize, index);
}
- System.arraycopy(mValues, index, mValues, index + 1, mSize - index);
+ System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
+ mSize--;
}
/**
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 3859ad4..abae068 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -18,12 +18,14 @@ package android.view;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.util.LongSparseArray;
import android.view.View.AttachInfo;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -35,8 +37,11 @@ import com.android.internal.util.Predicate;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Queue;
/**
* Class for managing accessibility interactions initiated from the system
@@ -47,6 +52,8 @@ import java.util.Map;
*/
final class AccessibilityInteractionController {
+ private static final boolean ENFORCE_NODE_TREE_CONSISTENT = Build.IS_DEBUGGABLE;
+
private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
new ArrayList<AccessibilityNodeInfo>();
@@ -137,7 +144,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) {
+ if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
root = mViewRootImpl.mView;
} else {
root = findViewByAccessibilityId(accessibilityViewId);
@@ -209,7 +216,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
@@ -289,7 +296,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
@@ -297,9 +304,14 @@ final class AccessibilityInteractionController {
if (root != null && isShown(root)) {
AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
if (provider != null) {
- infos = provider.findAccessibilityNodeInfosByText(text,
- virtualDescendantId);
- } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
+ if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ infos = provider.findAccessibilityNodeInfosByText(text,
+ virtualDescendantId);
+ } else {
+ infos = provider.findAccessibilityNodeInfosByText(text,
+ AccessibilityNodeProvider.HOST_VIEW_ID);
+ }
+ } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
ArrayList<View> foundViews = mTempArrayList;
foundViews.clear();
root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
@@ -316,7 +328,7 @@ final class AccessibilityInteractionController {
if (provider != null) {
List<AccessibilityNodeInfo> infosFromProvider =
provider.findAccessibilityNodeInfosByText(text,
- AccessibilityNodeInfo.UNDEFINED);
+ AccessibilityNodeProvider.HOST_VIEW_ID);
if (infosFromProvider != null) {
infos.addAll(infosFromProvider);
}
@@ -391,7 +403,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
@@ -417,7 +429,7 @@ final class AccessibilityInteractionController {
focused = AccessibilityNodeInfo.obtain(
mViewRootImpl.mAccessibilityFocusedVirtualView);
}
- } else if (virtualDescendantId == View.NO_ID) {
+ } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
focused = host.createAccessibilityNodeInfo();
}
} break;
@@ -500,7 +512,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
@@ -576,7 +588,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View target = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
target = findViewByAccessibilityId(accessibilityViewId);
} else {
target = mViewRootImpl.mView;
@@ -584,9 +596,14 @@ final class AccessibilityInteractionController {
if (target != null && isShown(target)) {
AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
if (provider != null) {
- succeeded = provider.performAction(virtualDescendantId, action,
- arguments);
- } else if (virtualDescendantId == View.NO_ID) {
+ if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ succeeded = provider.performAction(virtualDescendantId, action,
+ arguments);
+ } else {
+ succeeded = provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
+ action, arguments);
+ }
+ } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
succeeded = target.performAccessibilityAction(action, arguments);
}
}
@@ -734,6 +751,85 @@ final class AccessibilityInteractionController {
}
}
}
+ if (ENFORCE_NODE_TREE_CONSISTENT) {
+ enforceNodeTreeConsistent(outInfos);
+ }
+ }
+
+ private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
+ LongSparseArray<AccessibilityNodeInfo> nodeMap =
+ new LongSparseArray<AccessibilityNodeInfo>();
+ final int nodeCount = nodes.size();
+ for (int i = 0; i < nodeCount; i++) {
+ AccessibilityNodeInfo node = nodes.get(i);
+ nodeMap.put(node.getSourceNodeId(), node);
+ }
+
+ // If the nodes are a tree it does not matter from
+ // which node we start to search for the root.
+ AccessibilityNodeInfo root = nodeMap.valueAt(0);
+ AccessibilityNodeInfo parent = root;
+ while (parent != null) {
+ root = parent;
+ parent = nodeMap.get(parent.getParentNodeId());
+ }
+
+ // Traverse the tree and do some checks.
+ AccessibilityNodeInfo accessFocus = null;
+ AccessibilityNodeInfo inputFocus = null;
+ HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
+ Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
+ fringe.add(root);
+
+ while (!fringe.isEmpty()) {
+ AccessibilityNodeInfo current = fringe.poll();
+
+ // Check for duplicates
+ if (!seen.add(current)) {
+ throw new IllegalStateException("Duplicate node: "
+ + current + " in window:"
+ + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
+ }
+
+ // Check for one accessibility focus.
+ if (current.isAccessibilityFocused()) {
+ if (accessFocus != null) {
+ throw new IllegalStateException("Duplicate accessibility focus:"
+ + current
+ + " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
+ } else {
+ accessFocus = current;
+ }
+ }
+
+ // Check for one input focus.
+ if (current.isFocused()) {
+ if (inputFocus != null) {
+ throw new IllegalStateException("Duplicate input focus: "
+ + current + " in window:"
+ + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
+ } else {
+ inputFocus = current;
+ }
+ }
+
+ final int childCount = current.getChildCount();
+ for (int j = 0; j < childCount; j++) {
+ final long childId = current.getChildId(j);
+ final AccessibilityNodeInfo child = nodeMap.get(childId);
+ if (child != null) {
+ fringe.add(child);
+ }
+ }
+ }
+
+ // Check for disconnected nodes.
+ for (int j = nodeMap.size() - 1; j >= 0; j--) {
+ AccessibilityNodeInfo info = nodeMap.valueAt(j);
+ if (!seen.contains(info)) {
+ throw new IllegalStateException("Disconnected node: " + info);
+ }
+ }
}
private void prefetchPredecessorsOfRealNode(View view,
@@ -774,7 +870,7 @@ final class AccessibilityInteractionController {
info = child.createAccessibilityNodeInfo();
} else {
info = provider.createAccessibilityNodeInfo(
- AccessibilityNodeInfo.UNDEFINED);
+ AccessibilityNodeProvider.HOST_VIEW_ID);
}
if (info != null) {
outInfos.add(info);
@@ -814,7 +910,7 @@ final class AccessibilityInteractionController {
}
} else {
AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
- AccessibilityNodeInfo.UNDEFINED);
+ AccessibilityNodeProvider.HOST_VIEW_ID);
if (info != null) {
outInfos.add(info);
addedChildren.put(child, info);
@@ -845,16 +941,22 @@ final class AccessibilityInteractionController {
List<AccessibilityNodeInfo> outInfos) {
long parentNodeId = root.getParentNodeId();
int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
- while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
return;
}
final int virtualDescendantId =
AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
- if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED
+ if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
|| accessibilityViewId == providerHost.getAccessibilityViewId()) {
- AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo(
- virtualDescendantId);
+ final AccessibilityNodeInfo parent;
+ if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ parent = provider.createAccessibilityNodeInfo(
+ virtualDescendantId);
+ } else {
+ parent= provider.createAccessibilityNodeInfo(
+ AccessibilityNodeProvider.HOST_VIEW_ID);
+ }
if (parent != null) {
outInfos.add(parent);
}
@@ -875,10 +977,15 @@ final class AccessibilityInteractionController {
AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
final int parentVirtualDescendantId =
AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
- if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED
+ if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
|| parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
- AccessibilityNodeInfo parent =
- provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
+ final AccessibilityNodeInfo parent;
+ if (parentAccessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ parent = provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
+ } else {
+ parent = provider.createAccessibilityNodeInfo(
+ AccessibilityNodeProvider.HOST_VIEW_ID);
+ }
if (parent != null) {
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
diff --git a/core/java/android/view/IMagnificationCallbacks.aidl b/core/java/android/view/IMagnificationCallbacks.aidl
deleted file mode 100644
index 032d073..0000000
--- a/core/java/android/view/IMagnificationCallbacks.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-** Copyright 2012, 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 android.graphics.Region;
-
-/**
- * {@hide}
- */
-oneway interface IMagnificationCallbacks {
- void onMagnifedBoundsChanged(in Region bounds);
- void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
- void onRotationChanged(int rotation);
- void onUserContextChanged();
-}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c92a104..8f542bb 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -27,7 +27,6 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.view.IApplicationToken;
-import android.view.IMagnificationCallbacks;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindowSession;
@@ -197,7 +196,7 @@ interface IWindowManager
void thawRotation();
/**
- * Gets whether the rotation is frozen.
+ * Gets whether the rotation is frozen.
*
* @return Whether the rotation is frozen.
*/
@@ -231,55 +230,7 @@ interface IWindowManager
void lockNow(in Bundle options);
/**
- * Gets the token for the focused window.
- */
- IBinder getFocusedWindowToken();
-
- /**
- * Sets an input filter for manipulating the input event stream.
- */
- void setInputFilter(in IInputFilter filter);
-
- /**
- * Gets the frame of a window given its token.
- */
- void getWindowFrame(IBinder token, out Rect outFrame);
-
- /**
* Device is in safe mode.
*/
boolean isSafeModeEnabled();
-
- /**
- * Sets the display magnification callbacks. These callbacks notify
- * the client for contextual changes related to display magnification.
- *
- * @param callbacks The magnification callbacks.
- */
- void setMagnificationCallbacks(IMagnificationCallbacks callbacks);
-
- /**
- * Sets the magnification spec to be applied to all windows that can be
- * magnified.
- *
- * @param spec The current magnification spec.
- */
- void setMagnificationSpec(in MagnificationSpec spec);
-
- /**
- * Gets the magnification spec for a window given its token. If the
- * window has a compatibility scale it is also folded in the returned
- * magnification spec.
- *
- * @param windowToken The unique window token.
- * @return The magnification spec if such or null.
- */
- MagnificationSpec getCompatibleMagnificationSpecForWindow(in IBinder windowToken);
-
- /**
- * Sets the current touch exploration state.
- *
- * @param enabled Whether touch exploration is enabled.
- */
- void setTouchExplorationEnabled(boolean enabled);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ecd73af..a8ccd49 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5101,10 +5101,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see AccessibilityDelegate
*/
public void sendAccessibilityEvent(int eventType) {
- // Excluded views do not send accessibility events.
- if (!includeForAccessibility()) {
- return;
- }
if (mAccessibilityDelegate != null) {
mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
} else {
@@ -6901,7 +6897,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @attr ref android.R.styleable#View_filterTouchesWhenObscured
*/
public void setFilterTouchesWhenObscured(boolean enabled) {
- setFlags(enabled ? 0 : FILTER_TOUCHES_WHEN_OBSCURED,
+ setFlags(enabled ? FILTER_TOUCHES_WHEN_OBSCURED : 0,
FILTER_TOUCHES_WHEN_OBSCURED);
}
@@ -9386,6 +9382,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mParent.invalidateChild(this, null);
}
dispatchVisibilityChanged(this, newVisibility);
+
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
if ((changed & WILL_NOT_CACHE_DRAWING) != 0) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f9b9401..22fbbd6 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3711,7 +3711,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
childHasTransientStateChanged(child, true);
}
- if (child.isImportantForAccessibility() && child.getVisibility() != View.GONE) {
+ if (child.getVisibility() != View.GONE) {
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -3954,7 +3954,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
onViewRemoved(view);
- if (view.isImportantForAccessibility() && view.getVisibility() != View.GONE) {
+ if (view.getVisibility() != View.GONE) {
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 185cb65..1d6e998 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6355,7 +6355,7 @@ public final class ViewRootImpl implements ViewParent,
public void ensureConnection() {
if (mAttachInfo != null) {
final boolean registered =
- mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED;
+ mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
if (!registered) {
mAttachInfo.mAccessibilityWindowId =
mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
@@ -6366,9 +6366,9 @@ public final class ViewRootImpl implements ViewParent,
public void ensureNoConnection() {
final boolean registered =
- mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED;
+ mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
if (registered) {
- mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED;
+ mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow);
}
}
diff --git a/core/java/android/view/WindowInfo.aidl b/core/java/android/view/WindowInfo.aidl
new file mode 100644
index 0000000..75b8fd2
--- /dev/null
+++ b/core/java/android/view/WindowInfo.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.view;
+
+parcelable WindowInfo;
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
new file mode 100644
index 0000000..7f89044
--- /dev/null
+++ b/core/java/android/view/WindowInfo.java
@@ -0,0 +1,164 @@
+/*
+ * 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.view;
+
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pools;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents information about a window from the
+ * window manager to another part of the system.
+ *
+ * @hide
+ */
+public class WindowInfo implements Parcelable {
+ private static final int MAX_POOL_SIZE = 10;
+
+ private static final Pools.SynchronizedPool<WindowInfo> sPool =
+ new Pools.SynchronizedPool<WindowInfo>(MAX_POOL_SIZE);
+
+ public int type;
+ public int layer;
+ public IBinder token;
+ public IBinder parentToken;
+ public boolean focused;
+ public final Rect boundsInScreen = new Rect();
+ public List<IBinder> childTokens;
+
+ private WindowInfo() {
+ /* do nothing - hide constructor */
+ }
+
+ public static WindowInfo obtain() {
+ WindowInfo window = sPool.acquire();
+ if (window == null) {
+ window = new WindowInfo();
+ }
+ return window;
+ }
+
+ public static WindowInfo obtain(WindowInfo other) {
+ WindowInfo window = obtain();
+ window.type = other.type;
+ window.layer = other.layer;
+ window.token = other.token;
+ window.parentToken = other.parentToken;
+ window.focused = other.focused;
+ window.boundsInScreen.set(other.boundsInScreen);
+
+ if (other.childTokens != null && !other.childTokens.isEmpty()) {
+ if (window.childTokens == null) {
+ window.childTokens = new ArrayList<IBinder>(other.childTokens);
+ } else {
+ window.childTokens.addAll(other.childTokens);
+ }
+ }
+
+ return window;
+ }
+
+ public void recycle() {
+ clear();
+ sPool.release(this);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(type);
+ parcel.writeInt(layer);
+ parcel.writeStrongBinder(token);
+ parcel.writeStrongBinder(parentToken);
+ parcel.writeInt(focused ? 1 : 0);
+ boundsInScreen.writeToParcel(parcel, flags);
+
+ if (childTokens != null && !childTokens.isEmpty()) {
+ parcel.writeInt(1);
+ parcel.writeBinderList(childTokens);
+ } else {
+ parcel.writeInt(0);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("WindowInfo[");
+ builder.append("type=").append(type);
+ builder.append(", layer=").append(layer);
+ builder.append(", token=").append(token);
+ builder.append(", parent=").append(parentToken);
+ builder.append(", focused=").append(focused);
+ builder.append(", children=").append(childTokens);
+ builder.append(']');
+ return builder.toString();
+ }
+
+ private void initFromParcel(Parcel parcel) {
+ type = parcel.readInt();
+ layer = parcel.readInt();
+ token = parcel.readStrongBinder();
+ parentToken = parcel.readStrongBinder();
+ focused = (parcel.readInt() == 1);
+ boundsInScreen.readFromParcel(parcel);
+
+ final boolean hasChildren = (parcel.readInt() == 1);
+ if (hasChildren) {
+ if (childTokens == null) {
+ childTokens = new ArrayList<IBinder>();
+ }
+ parcel.readBinderList(childTokens);
+ }
+ }
+
+ private void clear() {
+ type = 0;
+ layer = 0;
+ token = null;
+ parentToken = null;
+ focused = false;
+ boundsInScreen.setEmpty();
+ if (childTokens != null) {
+ childTokens.clear();
+ }
+ }
+
+ public static final Parcelable.Creator<WindowInfo> CREATOR =
+ new Creator<WindowInfo>() {
+ @Override
+ public WindowInfo createFromParcel(Parcel parcel) {
+ WindowInfo window = obtain();
+ window.initFromParcel(parcel);
+ return window;
+ }
+
+ @Override
+ public WindowInfo[] newArray(int size) {
+ return new WindowInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index a1bd4bd..14dc356 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -16,7 +16,12 @@
package android.view;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
+import android.os.IBinder;
+
+import java.util.List;
/**
* Window manager local system service interface.
@@ -24,10 +29,136 @@ import android.hardware.display.DisplayManagerInternal;
* @hide Only for use within the system server.
*/
public abstract class WindowManagerInternal {
+
+ /**
+ * Interface to receive a callback when the windows reported for
+ * accessibility changed.
+ */
+ public interface WindowsForAccessibilityCallback {
+
+ /**
+ * Called when the windows for accessibility changed.
+ *
+ * @param windows The windows for accessibility.
+ */
+ public void onWindowsForAccessibilityChanged(List<WindowInfo> windows);
+ }
+
+ /**
+ * Callbacks for contextual changes that affect the screen magnification
+ * feature.
+ */
+ public interface MagnificationCallbacks {
+
+ /**
+ * Called when the bounds of the screen content that is magnified changed.
+ * Note that not the entire screen is magnified.
+ *
+ * @param bounds The bounds.
+ */
+ public void onMagnifedBoundsChanged(Region bounds);
+
+ /**
+ * Called when an application requests a rectangle on the screen to allow
+ * the client to apply the appropriate pan and scale.
+ *
+ * @param left The rectangle left.
+ * @param top The rectangle top.
+ * @param right The rectangle right.
+ * @param bottom The rectangle bottom.
+ */
+ public void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
+
+ /**
+ * Notifies that the rotation changed.
+ *
+ * @param rotation The current rotation.
+ */
+ public void onRotationChanged(int rotation);
+
+ /**
+ * Notifies that the context of the user changed. For example, an application
+ * was started.
+ */
+ public void onUserContextChanged();
+ }
+
/**
* Request that the window manager call
* {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager}
* within a surface transaction at a later time.
*/
public abstract void requestTraversalFromDisplayManager();
-} \ No newline at end of file
+
+ /**
+ * Set by the accessibility layer to observe changes in the magnified region,
+ * rotation, and other window transformations related to display magnification
+ * as the window manager is responsible for doing the actual magnification
+ * and has access to the raw window data while the accessibility layer serves
+ * as a controller.
+ *
+ * @param callbacks The callbacks to invoke.
+ */
+ public abstract void setMagnificationCallbacks(MagnificationCallbacks callbacks);
+
+ /**
+ * Set by the accessibility layer to specify the magnification and panning to
+ * be applied to all windows that should be magnified.
+ *
+ * @param callbacks The callbacks to invoke.
+ *
+ * @see #setMagnificationCallbacks(MagnificationCallbacks)
+ */
+ public abstract void setMagnificationSpec(MagnificationSpec spec);
+
+ /**
+ * Gets the magnification and translation applied to a window given its token.
+ * Not all windows are magnified and the window manager policy determines which
+ * windows are magnified. The returned result also takes into account the compat
+ * scale if necessary.
+ *
+ * @param windowToken The window's token.
+ *
+ * @return The magnification spec for the window.
+ *
+ * @see #setMagnificationCallbacks(MagnificationCallbacks)
+ */
+ public abstract MagnificationSpec getCompatibleMagnificationSpecForWindow(
+ IBinder windowToken);
+
+ /**
+ * Sets a callback for observing which windows are touchable for the purposes
+ * of accessibility.
+ *
+ * @param callback The callback.
+ */
+ public abstract void setWindowsForAccessibilityCallback(
+ WindowsForAccessibilityCallback callback);
+
+ /**
+ * Sets a filter for manipulating the input event stream.
+ *
+ * @param filter The filter implementation.
+ */
+ public abstract void setInputFilter(IInputFilter filter);
+
+ /**
+ * Gets the token of the window that has input focus.
+ *
+ * @return The token.
+ */
+ public abstract IBinder getFocusedWindowToken();
+
+ /**
+ * @return Whether the keyguard is engaged.
+ */
+ public abstract boolean isKeyguardLocked();
+
+ /**
+ * Gets the frame of a window given its token.
+ *
+ * @param token The token.
+ * @param outBounds The frame to populate.
+ */
+ public abstract void getWindowFrame(IBinder token, Rect outBounds);
+}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 75656545..bd203c8 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1187,11 +1187,4 @@ public interface WindowManagerPolicy {
* @return True if the window is a top level one.
*/
public boolean isTopLevelWindow(int windowType);
-
- /**
- * Sets the current touch exploration state.
- *
- * @param enabled Whether touch exploration is enabled.
- */
- public void setTouchExplorationEnabled(boolean enabled);
}
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
new file mode 100644
index 0000000..77d48e2
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2012 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.accessibility;
+
+import android.os.Build;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.LongArray;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Cache for AccessibilityWindowInfos and AccessibilityNodeInfos.
+ * It is updated when windows change or nodes change.
+ */
+final class AccessibilityCache {
+
+ private static final String LOG_TAG = "AccessibilityCache";
+
+ private static final boolean DEBUG = false;
+
+ private static final boolean CHECK_INTEGRITY = Build.IS_DEBUGGABLE;
+
+ private final Object mLock = new Object();
+
+ private final LongArray mTempLongArray = new LongArray();
+
+ private final SparseArray<AccessibilityWindowInfo> mWindowCache =
+ new SparseArray<AccessibilityWindowInfo>();
+
+ private final SparseArray<LongSparseArray<AccessibilityNodeInfo>> mNodeCache =
+ new SparseArray<LongSparseArray<AccessibilityNodeInfo>>();
+
+ private final SparseArray<AccessibilityWindowInfo> mTempWindowArray =
+ new SparseArray<AccessibilityWindowInfo>();
+
+ public void addWindow(AccessibilityWindowInfo window) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Caching window: " + window.getId());
+ }
+ mWindowCache.put(window.getId(), window);
+ }
+ }
+
+ public void removeWindows(int[] windowIds) {
+ synchronized (mLock) {
+ final int windowCount = windowIds.length;
+ for (int i = 0; i < windowCount; i++) {
+ final int windowId = windowIds[i];
+ AccessibilityWindowInfo window = mWindowCache.get(windowId);
+ if (window != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Removing window: " + windowId);
+ }
+ window.recycle();
+ mWindowCache.remove(windowId);
+ }
+ clearNodesForWindowLocked(windowIds[i]);
+ }
+ }
+ }
+
+ /**
+ * Notifies the cache that the something in the UI changed. As a result
+ * the cache will either refresh some nodes or evict some nodes.
+ *
+ * @param event An event.
+ */
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ synchronized (mLock) {
+ final int eventType = event.getEventType();
+ switch (eventType) {
+ case AccessibilityEvent.TYPE_VIEW_FOCUSED:
+ case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
+ case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED:
+ case AccessibilityEvent.TYPE_VIEW_SELECTED:
+ case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
+ case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
+ refreshCachedNodeLocked(event.getWindowId(), event.getSourceNodeId());
+ } break;
+
+ case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
+ synchronized (mLock) {
+ final int windowId = event.getWindowId();
+ final long sourceId = event.getSourceNodeId();
+ if ((event.getContentChangeTypes()
+ & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
+ clearSubTreeLocked(windowId, sourceId);
+ } else {
+ refreshCachedNodeLocked(windowId, sourceId);
+ }
+ }
+ } break;
+
+ case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
+ clearSubTreeLocked(event.getWindowId(), event.getSourceNodeId());
+ } break;
+ }
+ }
+
+ if (CHECK_INTEGRITY) {
+ checkIntegrity();
+ }
+ }
+
+ private void refreshCachedNodeLocked(int windowId, long sourceId) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Refreshing cached node.");
+ }
+
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+ if (nodes == null) {
+ return;
+ }
+ AccessibilityNodeInfo cachedInfo = nodes.get(sourceId);
+ // If the source is not in the cache - nothing to do.
+ if (cachedInfo == null) {
+ return;
+ }
+ // The node changed so we will just refresh it right now.
+ if (cachedInfo.refresh(true)) {
+ return;
+ }
+ // Weird, we could not refresh. Just evict the entire sub-tree.
+ clearSubTreeLocked(windowId, sourceId);
+ }
+
+ /**
+ * Gets a cached {@link AccessibilityNodeInfo} given the id of the hosting
+ * window and the accessibility id of the node.
+ *
+ * @param windowId The id of the window hosting the node.
+ * @param accessibilityNodeId The info accessibility node id.
+ * @return The cached {@link AccessibilityNodeInfo} or null if such not found.
+ */
+ public AccessibilityNodeInfo getNode(int windowId, long accessibilityNodeId) {
+ synchronized(mLock) {
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+ if (nodes == null) {
+ return null;
+ }
+ AccessibilityNodeInfo info = nodes.get(accessibilityNodeId);
+ if (info != null) {
+ // Return a copy since the client calls to AccessibilityNodeInfo#recycle()
+ // will wipe the data of the cached info.
+ info = AccessibilityNodeInfo.obtain(info);
+ }
+ if (DEBUG) {
+ Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
+ }
+ return info;
+ }
+ }
+
+ public List<AccessibilityWindowInfo> getWindows() {
+ synchronized (mLock) {
+ final int windowCount = mWindowCache.size();
+ if (windowCount > 0) {
+ // Careful to return the windows in a decreasing layer order.
+ SparseArray<AccessibilityWindowInfo> sortedWindows = mTempWindowArray;
+ sortedWindows.clear();
+
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = mWindowCache.valueAt(i);
+ sortedWindows.put(window.getLayer(), window);
+ }
+
+ List<AccessibilityWindowInfo> windows = new ArrayList<AccessibilityWindowInfo>();
+ for (int i = windowCount - 1; i >= 0; i--) {
+ AccessibilityWindowInfo window = sortedWindows.valueAt(i);
+ windows.add(AccessibilityWindowInfo.obtain(window));
+ }
+
+ sortedWindows.clear();
+
+ return windows;
+ }
+ return null;
+ }
+ }
+
+ public AccessibilityWindowInfo getWindow(int windowId) {
+ synchronized (mLock) {
+ AccessibilityWindowInfo window = mWindowCache.get(windowId);
+ if (window != null) {
+ return AccessibilityWindowInfo.obtain(window);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Caches an {@link AccessibilityNodeInfo}.
+ *
+ * @param info The node to cache.
+ */
+ public void add(AccessibilityNodeInfo info) {
+ synchronized(mLock) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "add(" + info + ")");
+ }
+
+ final int windowId = info.getWindowId();
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+ if (nodes == null) {
+ nodes = new LongSparseArray<AccessibilityNodeInfo>();
+ mNodeCache.put(windowId, nodes);
+ }
+
+ final long sourceId = info.getSourceNodeId();
+ AccessibilityNodeInfo oldInfo = nodes.get(sourceId);
+ if (oldInfo != null) {
+ // If the added node is in the cache we have to be careful if
+ // the new one represents a source state where some of the
+ // children have been removed to remove the descendants that
+ // are no longer present.
+ final LongArray newChildrenIds = info.getChildNodeIds();
+ if (newChildrenIds != null) {
+ // Cache the new ids as we will do some lookups.
+ LongArray newChildNodeIds = mTempLongArray;
+ final int newChildCount = newChildNodeIds.size();
+ for (int i = 0; i < newChildCount; i++) {
+ newChildNodeIds.add(newChildrenIds.get(i));
+ }
+
+ final int oldChildCount = oldInfo.getChildCount();
+ for (int i = 0; i < oldChildCount; i++) {
+ final long oldChildId = oldInfo.getChildId(i);
+ if (newChildNodeIds.indexOf(oldChildId) < 0) {
+ clearSubTreeLocked(windowId, oldChildId);
+ }
+ }
+
+ newChildNodeIds.clear();
+ }
+
+ // Also be careful if the parent has changed since the new
+ // parent may be a predecessor of the old parent which will
+ // add cyclse to the cache.
+ final long oldParentId = oldInfo.getParentNodeId();
+ if (info.getParentNodeId() != oldParentId) {
+ clearSubTreeLocked(windowId, oldParentId);
+ }
+ }
+
+ // Cache a copy since the client calls to AccessibilityNodeInfo#recycle()
+ // will wipe the data of the cached info.
+ AccessibilityNodeInfo clone = AccessibilityNodeInfo.obtain(info);
+ nodes.put(sourceId, clone);
+ }
+ }
+
+ /**
+ * Clears the cache.
+ */
+ public void clear() {
+ synchronized(mLock) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "clear()");
+ }
+ final int windowCount = mWindowCache.size();
+ for (int i = windowCount - 1; i >= 0; i--) {
+ AccessibilityWindowInfo window = mWindowCache.valueAt(i);
+ window.recycle();
+ mWindowCache.removeAt(i);
+ }
+ final int nodesForWindowCount = mNodeCache.size();
+ for (int i = 0; i < nodesForWindowCount; i++) {
+ final int windowId = mNodeCache.keyAt(i);
+ clearNodesForWindowLocked(windowId);
+ }
+ }
+ }
+
+ private void clearNodesForWindowLocked(int windowId) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "clearWindowLocked(" + windowId + ")");
+ }
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+ if (nodes == null) {
+ return;
+ }
+ // Recycle the nodes before clearing the cache.
+ final int nodeCount = nodes.size();
+ for (int i = nodeCount - 1; i >= 0; i--) {
+ AccessibilityNodeInfo info = nodes.valueAt(i);
+ nodes.removeAt(i);
+ info.recycle();
+ }
+ mNodeCache.remove(windowId);
+ }
+
+ /**
+ * Clears a subtree rooted at the node with the given id that is
+ * hosted in a given window.
+ *
+ * @param windowId The id of the hosting window.
+ * @param rootNodeId The root id.
+ */
+ private void clearSubTreeLocked(int windowId, long rootNodeId) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Clearing cached subtree.");
+ }
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+ if (nodes != null) {
+ clearSubTreeRecursiveLocked(nodes, rootNodeId);
+ }
+ }
+
+ /**
+ * Clears a subtree given a pointer to the root id and the nodes
+ * in the hosting window.
+ *
+ * @param nodes The nodes in the hosting window.
+ * @param rootNodeId The id of the root to evict.
+ */
+ private void clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes,
+ long rootNodeId) {
+ AccessibilityNodeInfo current = nodes.get(rootNodeId);
+ if (current == null) {
+ return;
+ }
+ nodes.remove(rootNodeId);
+ final int childCount = current.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final long childNodeId = current.getChildId(i);
+ clearSubTreeRecursiveLocked(nodes, childNodeId);
+ }
+ }
+
+ /**
+ * Check the integrity of the cache which is nodes from different windows
+ * are not mixed, there is a single active window, there is a single focused
+ * window, for every window there are no duplicates nodes, all nodes for a
+ * window are connected, for every window there is a single input focused
+ * node, and for every window there is a single accessibility focused node.
+ */
+ public void checkIntegrity() {
+ synchronized (mLock) {
+ // Get the root.
+ if (mWindowCache.size() <= 0 && mNodeCache.size() == 0) {
+ return;
+ }
+
+ AccessibilityWindowInfo focusedWindow = null;
+ AccessibilityWindowInfo activeWindow = null;
+
+ final int windowCount = mWindowCache.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = mWindowCache.valueAt(i);
+
+ // Check for one active window.
+ if (window.isActive()) {
+ if (activeWindow != null) {
+ Log.e(LOG_TAG, "Duplicate active window:" + window);
+ } else {
+ activeWindow = window;
+ }
+ }
+
+ // Check for one focused window.
+ if (window.isFocused()) {
+ if (focusedWindow != null) {
+ Log.e(LOG_TAG, "Duplicate focused window:" + window);
+ } else {
+ focusedWindow = window;
+ }
+ }
+ }
+
+ // Traverse the tree and do some checks.
+ AccessibilityNodeInfo accessFocus = null;
+ AccessibilityNodeInfo inputFocus = null;
+
+ final int nodesForWindowCount = mNodeCache.size();
+ for (int i = 0; i < nodesForWindowCount; i++) {
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.valueAt(i);
+ if (nodes.size() <= 0) {
+ continue;
+ }
+
+ ArraySet<AccessibilityNodeInfo> seen = new ArraySet<AccessibilityNodeInfo>();
+ final int windowId = mNodeCache.keyAt(i);
+
+ final int nodeCount = nodes.size();
+ for (int j = 0; j < nodeCount; j++) {
+ AccessibilityNodeInfo node = nodes.valueAt(j);
+
+ // Check for duplicates
+ if (!seen.add(node)) {
+ Log.e(LOG_TAG, "Duplicate node: " + node
+ + " in window:" + windowId);
+ }
+
+ // Check for one accessibility focus.
+ if (node.isAccessibilityFocused()) {
+ if (accessFocus != null) {
+ Log.e(LOG_TAG, "Duplicate accessibility focus:" + node
+ + " in window:" + windowId);
+ } else {
+ accessFocus = node;
+ }
+ }
+
+ // Check for one input focus.
+ if (node.isFocused()) {
+ if (inputFocus != null) {
+ Log.e(LOG_TAG, "Duplicate input focus: " + node
+ + " in window:" + windowId);
+ } else {
+ inputFocus = node;
+ }
+ }
+
+ // The node should be a child of its parent if we have the parent.
+ AccessibilityNodeInfo nodeParent = nodes.get(node.getParentNodeId());
+ if (nodeParent != null) {
+ boolean childOfItsParent = false;
+ final int childCount = nodeParent.getChildCount();
+ for (int k = 0; k < childCount; k++) {
+ AccessibilityNodeInfo child = nodes.get(nodeParent.getChildId(k));
+ if (child == node) {
+ childOfItsParent = true;
+ break;
+ }
+ }
+ if (!childOfItsParent) {
+ Log.e(LOG_TAG, "Invalid parent-child ralation between parent: "
+ + nodeParent + " and child: " + node);
+ }
+ }
+
+ // The node should be the parent of its child if we have the child.
+ final int childCount = node.getChildCount();
+ for (int k = 0; k < childCount; k++) {
+ AccessibilityNodeInfo child = nodes.get(node.getChildId(k));
+ if (child != null) {
+ AccessibilityNodeInfo parent = nodes.get(child.getParentNodeId());
+ if (parent != node) {
+ Log.e(LOG_TAG, "Invalid child-parent ralation between child: "
+ + node + " and parent: " + nodeParent);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 8b91155..417e22c 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -343,6 +343,23 @@ import java.util.List;
* view.</br>
* </p>
* <p>
+ * <b>Windows changed</b> - represents the event of changes in the windows shown on
+ * the screen such as a window appeared, a window disappeared, a window size changed,
+ * a window layer changed, etc.</br>
+ * <em>Type:</em> {@link #TYPE_WINDOWS_CHANGED}</br>
+ * <em>Properties:</em></br>
+ * <ul>
+ * <li>{@link #getEventType()} - The type of the event.</li>
+ * <li>{@link #getEventTime()} - The event time.</li>
+ * </ul>
+ * <em>Note:</em> You can retrieve the {@link AccessibilityWindowInfo} for the window
+ * source of the event via {@link AccessibilityEvent#getSource()} to get the source
+ * node on which then call {@link AccessibilityNodeInfo#getWindow()
+ * AccessibilityNodeInfo.getWindow()} to get the window. Also all windows on the screen can
+ * be retrieved by a call to {@link android.accessibilityservice.AccessibilityService#getWindows()
+ * android.accessibilityservice.AccessibilityService.getWindows()}.
+ * </p>
+ * <p>
* <b>NOTIFICATION TYPES</b></br>
* </p>
* <p>
@@ -662,6 +679,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
public static final int TYPE_TOUCH_INTERACTION_END = 0x00200000;
/**
+ * Represents the event change in the windows shown on the screen.
+ */
+ public static final int TYPE_WINDOWS_CHANGED = 0x00400000;
+
+ /**
* Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
* The type of change is not defined.
*/
@@ -708,6 +730,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
* @see #TYPE_GESTURE_DETECTION_END
* @see #TYPE_TOUCH_INTERACTION_START
* @see #TYPE_TOUCH_INTERACTION_END
+ * @see #TYPE_WINDOWS_CHANGED
*/
public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
@@ -1366,6 +1389,13 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
builder.append("TYPE_TOUCH_INTERACTION_END");
eventTypeCount++;
} break;
+ case TYPE_WINDOWS_CHANGED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_WINDOWS_CHANGED");
+ eventTypeCount++;
+ } break;
}
}
if (eventTypeCount > 1) {
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 5a55e34..4dd8dcb 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -100,14 +100,11 @@ public final class AccessibilityInteractionClient
private Message mSameThreadMessage;
- // The connection cache is shared between all interrogating threads.
private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
new SparseArray<IAccessibilityServiceConnection>();
- // The connection cache is shared between all interrogating threads since
- // at any given time there is only one window allowing querying.
- private static final AccessibilityNodeInfoCache sAccessibilityNodeInfoCache =
- new AccessibilityNodeInfoCache();
+ private static final AccessibilityCache sAccessibilityCache =
+ new AccessibilityCache();
/**
* @return The client for the current thread.
@@ -166,6 +163,86 @@ public final class AccessibilityInteractionClient
}
/**
+ * Gets the info for a window.
+ *
+ * @param connectionId The id of a connection for interacting with the system.
+ * @param accessibilityWindowId A unique window id. Use
+ * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * to query the currently active window.
+ * @return The {@link AccessibilityWindowInfo}.
+ */
+ public AccessibilityWindowInfo getWindow(int connectionId, int accessibilityWindowId) {
+ try {
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection != null) {
+ AccessibilityWindowInfo window = sAccessibilityCache.getWindow(
+ accessibilityWindowId);
+ if (window != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Window cache hit");
+ }
+ return window;
+ }
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Window cache miss");
+ }
+ window = connection.getWindow(accessibilityWindowId);
+ if (window != null) {
+ sAccessibilityCache.addWindow(window);
+ return window;
+ }
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+ }
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while calling remote getWindow", re);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the info for all windows.
+ *
+ * @param connectionId The id of a connection for interacting with the system.
+ * @return The {@link AccessibilityWindowInfo} list.
+ */
+ public List<AccessibilityWindowInfo> getWindows(int connectionId) {
+ try {
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection != null) {
+ List<AccessibilityWindowInfo> windows = sAccessibilityCache.getWindows();
+ if (windows != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Window cache hit");
+ }
+ return windows;
+ }
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Window cache miss");
+ }
+ windows = connection.getWindows();
+ if (windows != null) {
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = windows.get(i);
+ sAccessibilityCache.addWindow(window);
+ }
+ return windows;
+ }
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+ }
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while calling remote getWindows", re);
+ }
+ return Collections.emptyList();
+ }
+
+ /**
* Finds an {@link AccessibilityNodeInfo} by accessibility id.
*
* @param connectionId The id of a connection for interacting with the system.
@@ -183,15 +260,26 @@ public final class AccessibilityInteractionClient
public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
int accessibilityWindowId, long accessibilityNodeId, boolean bypassCache,
int prefetchFlags) {
+ if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0
+ && (prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) == 0) {
+ throw new IllegalArgumentException("FLAG_PREFETCH_SIBLINGS"
+ + " requires FLAG_PREFETCH_PREDECESSORS");
+ }
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
if (!bypassCache) {
- AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(
- accessibilityNodeId);
+ AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getNode(
+ accessibilityWindowId, accessibilityNodeId);
if (cachedInfo != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Node cache hit");
+ }
return cachedInfo;
}
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Node cache miss");
+ }
}
final int interactionId = mInteractionIdCounter.getAndIncrement();
final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId(
@@ -212,10 +300,8 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote"
- + " findAccessibilityNodeInfoByAccessibilityId", re);
- }
+ Log.e(LOG_TAG, "Error while calling remote"
+ + " findAccessibilityNodeInfoByAccessibilityId", re);
}
return null;
}
@@ -259,10 +345,8 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote"
- + " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
- }
+ Log.w(LOG_TAG, "Error while calling remote"
+ + " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
}
return Collections.emptyList();
}
@@ -307,10 +391,8 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote"
- + " findAccessibilityNodeInfosByViewText", re);
- }
+ Log.w(LOG_TAG, "Error while calling remote"
+ + " findAccessibilityNodeInfosByViewText", re);
}
return Collections.emptyList();
}
@@ -352,9 +434,7 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote findFocus", re);
- }
+ Log.w(LOG_TAG, "Error while calling remote findFocus", re);
}
return null;
}
@@ -396,9 +476,7 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
- }
+ Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
}
return null;
}
@@ -436,19 +514,21 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
- }
+ Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
}
return false;
}
public void clearCache() {
- sAccessibilityNodeInfoCache.clear();
+ sAccessibilityCache.clear();
}
public void onAccessibilityEvent(AccessibilityEvent event) {
- sAccessibilityNodeInfoCache.onAccessibilityEvent(event);
+ sAccessibilityCache.onAccessibilityEvent(event);
+ }
+
+ public void removeWindows(int[] windowIds) {
+ sAccessibilityCache.removeWindows(windowIds);
}
/**
@@ -613,7 +693,7 @@ public final class AccessibilityInteractionClient
if (info != null) {
info.setConnectionId(connectionId);
info.setSealed(true);
- sAccessibilityNodeInfoCache.add(info);
+ sAccessibilityCache.add(info);
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index a711f48..cbc38c6 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -115,9 +115,9 @@ public final class AccessibilityManager {
private static AccessibilityManager sInstance;
- private static final int DO_SET_STATE = 10;
+ private final Object mLock = new Object();
- final IAccessibilityManager mService;
+ private IAccessibilityManager mService;
final int mUserId;
@@ -166,29 +166,14 @@ public final class AccessibilityManager {
public void onTouchExplorationStateChanged(boolean enabled);
}
- final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() {
+ private final IAccessibilityManagerClient.Stub mClient =
+ new IAccessibilityManagerClient.Stub() {
public void setState(int state) {
- mHandler.obtainMessage(DO_SET_STATE, state, 0).sendToTarget();
- }
- };
-
- class MyHandler extends Handler {
-
- MyHandler(Looper mainLooper) {
- super(mainLooper);
- }
-
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case DO_SET_STATE :
- setState(message.arg1);
- return;
- default :
- Log.w(LOG_TAG, "Unknown message type: " + message.what);
+ synchronized (mLock) {
+ setStateLocked(state);
}
}
- }
+ };
/**
* Get an AccessibilityManager instance (create one if necessary).
@@ -234,26 +219,29 @@ public final class AccessibilityManager {
mHandler = new MyHandler(context.getMainLooper());
mService = service;
mUserId = userId;
- if (mService == null) {
- mIsEnabled = false;
- }
- try {
- if (mService != null) {
- final int stateFlags = mService.addClient(mClient, userId);
- setState(stateFlags);
- }
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
+ synchronized (mLock) {
+ tryConnectToServiceLocked();
}
}
/**
+ * @hide
+ */
+ public IAccessibilityManagerClient getClient() {
+ return mClient;
+ }
+
+ /**
* Returns if the accessibility in the system is enabled.
*
* @return True if accessibility is enabled, false otherwise.
*/
public boolean isEnabled() {
- synchronized (mHandler) {
+ synchronized (mLock) {
+ IAccessibilityManager service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
return mIsEnabled;
}
}
@@ -264,24 +252,16 @@ public final class AccessibilityManager {
* @return True if touch exploration is enabled, false otherwise.
*/
public boolean isTouchExplorationEnabled() {
- synchronized (mHandler) {
+ synchronized (mLock) {
+ IAccessibilityManager service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
return mIsTouchExplorationEnabled;
}
}
/**
- * Returns the client interface this instance registers in
- * the centralized accessibility manager service.
- *
- * @return The client.
- *
- * @hide
- */
- public IAccessibilityManagerClient getClient() {
- return (IAccessibilityManagerClient) mClient.asBinder();
- }
-
- /**
* Sends an {@link AccessibilityEvent}.
*
* @param event The event to send.
@@ -295,8 +275,17 @@ public final class AccessibilityManager {
* their descendants.
*/
public void sendAccessibilityEvent(AccessibilityEvent event) {
- if (!mIsEnabled) {
- throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ if (!mIsEnabled) {
+ throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+ }
+ userId = mUserId;
}
boolean doRecycle = false;
try {
@@ -305,7 +294,7 @@ public final class AccessibilityManager {
// client using it is called through Binder from another process. Example: MMS
// app adds a SMS notification and the NotificationManagerService calls this method
long identityToken = Binder.clearCallingIdentity();
- doRecycle = mService.sendAccessibilityEvent(event, mUserId);
+ doRecycle = service.sendAccessibilityEvent(event, userId);
Binder.restoreCallingIdentity(identityToken);
if (DEBUG) {
Log.i(LOG_TAG, event + " sent");
@@ -323,11 +312,20 @@ public final class AccessibilityManager {
* Requests feedback interruption from all accessibility services.
*/
public void interrupt() {
- if (!mIsEnabled) {
- throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ if (!mIsEnabled) {
+ throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+ }
+ userId = mUserId;
}
try {
- mService.interrupt(mUserId);
+ service.interrupt(userId);
if (DEBUG) {
Log.i(LOG_TAG, "Requested interrupt from all services");
}
@@ -361,18 +359,30 @@ public final class AccessibilityManager {
* @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
*/
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ userId = mUserId;
+ }
+
List<AccessibilityServiceInfo> services = null;
try {
- if (mService != null) {
- services = mService.getInstalledAccessibilityServiceList(mUserId);
- if (DEBUG) {
- Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
- }
+ services = service.getInstalledAccessibilityServiceList(userId);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
}
- return services != null ? Collections.unmodifiableList(services) : Collections.EMPTY_LIST;
+ if (services != null) {
+ return Collections.unmodifiableList(services);
+ } else {
+ return Collections.emptyList();
+ }
}
/**
@@ -390,18 +400,30 @@ public final class AccessibilityManager {
*/
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
int feedbackTypeFlags) {
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ userId = mUserId;
+ }
+
List<AccessibilityServiceInfo> services = null;
try {
- if (mService != null) {
- services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags, mUserId);
- if (DEBUG) {
- Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
- }
+ services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
}
- return services != null ? Collections.unmodifiableList(services) : Collections.EMPTY_LIST;
+ if (services != null) {
+ return Collections.unmodifiableList(services);
+ } else {
+ return Collections.emptyList();
+ }
}
/**
@@ -413,6 +435,7 @@ public final class AccessibilityManager {
*/
public boolean addAccessibilityStateChangeListener(
AccessibilityStateChangeListener listener) {
+ // Final CopyOnArrayList - no lock needed.
return mAccessibilityStateChangeListeners.add(listener);
}
@@ -424,6 +447,7 @@ public final class AccessibilityManager {
*/
public boolean removeAccessibilityStateChangeListener(
AccessibilityStateChangeListener listener) {
+ // Final CopyOnArrayList - no lock needed.
return mAccessibilityStateChangeListeners.remove(listener);
}
@@ -436,6 +460,7 @@ public final class AccessibilityManager {
*/
public boolean addTouchExplorationStateChangeListener(
TouchExplorationStateChangeListener listener) {
+ // Final CopyOnArrayList - no lock needed.
return mTouchExplorationStateChangeListeners.add(listener);
}
@@ -447,6 +472,7 @@ public final class AccessibilityManager {
*/
public boolean removeTouchExplorationStateChangeListener(
TouchExplorationStateChangeListener listener) {
+ // Final CopyOnArrayList - no lock needed.
return mTouchExplorationStateChangeListeners.remove(listener);
}
@@ -455,50 +481,24 @@ public final class AccessibilityManager {
*
* @param stateFlags The state flags.
*/
- private void setState(int stateFlags) {
+ private void setStateLocked(int stateFlags) {
final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
final boolean touchExplorationEnabled =
(stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
- synchronized (mHandler) {
- final boolean wasEnabled = mIsEnabled;
- final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
- // Ensure listeners get current state from isZzzEnabled() calls.
- mIsEnabled = enabled;
- mIsTouchExplorationEnabled = touchExplorationEnabled;
+ final boolean wasEnabled = mIsEnabled;
+ final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
- if (wasEnabled != enabled) {
- notifyAccessibilityStateChangedLh();
- }
+ // Ensure listeners get current state from isZzzEnabled() calls.
+ mIsEnabled = enabled;
+ mIsTouchExplorationEnabled = touchExplorationEnabled;
- if (wasTouchExplorationEnabled != touchExplorationEnabled) {
- notifyTouchExplorationStateChangedLh();
- }
+ if (wasEnabled != enabled) {
+ mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED);
}
- }
- /**
- * Notifies the registered {@link AccessibilityStateChangeListener}s.
- * <p>
- * The caller must be locked on {@link #mHandler}.
- */
- private void notifyAccessibilityStateChangedLh() {
- final int listenerCount = mAccessibilityStateChangeListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(mIsEnabled);
- }
- }
-
- /**
- * Notifies the registered {@link TouchExplorationStateChangeListener}s.
- * <p>
- * The caller must be locked on {@link #mHandler}.
- */
- private void notifyTouchExplorationStateChangedLh() {
- final int listenerCount = mTouchExplorationStateChangeListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- mTouchExplorationStateChangeListeners.get(i)
- .onTouchExplorationStateChanged(mIsTouchExplorationEnabled);
+ if (wasTouchExplorationEnabled != touchExplorationEnabled) {
+ mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_EXPLORATION_STATE_CHANGED);
}
}
@@ -511,11 +511,17 @@ public final class AccessibilityManager {
*/
public int addAccessibilityInteractionConnection(IWindow windowToken,
IAccessibilityInteractionConnection connection) {
- if (mService == null) {
- return View.NO_ID;
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return View.NO_ID;
+ }
+ userId = mUserId;
}
try {
- return mService.addAccessibilityInteractionConnection(windowToken, connection, mUserId);
+ return service.addAccessibilityInteractionConnection(windowToken, connection, userId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
}
@@ -529,12 +535,90 @@ public final class AccessibilityManager {
* @hide
*/
public void removeAccessibilityInteractionConnection(IWindow windowToken) {
- try {
- if (mService != null) {
- mService.removeAccessibilityInteractionConnection(windowToken);
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
}
+ }
+ try {
+ service.removeAccessibilityInteractionConnection(windowToken);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re);
}
}
+
+ private IAccessibilityManager getServiceLocked() {
+ if (mService == null) {
+ tryConnectToServiceLocked();
+ }
+ return mService;
+ }
+
+ private void tryConnectToServiceLocked() {
+ IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+ if (iBinder == null) {
+ return;
+ }
+ IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
+ try {
+ final int stateFlags = service.addClient(mClient, mUserId);
+ setStateLocked(stateFlags);
+ mService = service;
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
+ }
+ }
+
+ /**
+ * Notifies the registered {@link AccessibilityStateChangeListener}s.
+ */
+ private void handleNotifyAccessibilityStateChanged() {
+ final boolean isEnabled;
+ synchronized (mLock) {
+ isEnabled = mIsEnabled;
+ }
+ final int listenerCount = mAccessibilityStateChangeListeners.size();
+ for (int i = 0; i < listenerCount; i++) {
+ mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(isEnabled);
+ }
+ }
+
+ /**
+ * Notifies the registered {@link TouchExplorationStateChangeListener}s.
+ */
+ private void handleNotifyTouchExplorationStateChanged() {
+ final boolean isTouchExplorationEnabled;
+ synchronized (mLock) {
+ isTouchExplorationEnabled = mIsTouchExplorationEnabled;
+ }
+ final int listenerCount = mTouchExplorationStateChangeListeners.size();
+ for (int i = 0; i < listenerCount; i++) {
+ mTouchExplorationStateChangeListeners.get(i)
+ .onTouchExplorationStateChanged(isTouchExplorationEnabled);
+ }
+ }
+
+ private final class MyHandler extends Handler {
+ public static final int MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED = 1;
+ public static final int MSG_NOTIFY_EXPLORATION_STATE_CHANGED = 2;
+
+ public MyHandler(Looper looper) {
+ super(looper, null, false);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED: {
+ handleNotifyAccessibilityStateChanged();
+ } break;
+
+ case MSG_NOTIFY_EXPLORATION_STATE_CHANGED: {
+ handleNotifyTouchExplorationStateChanged();
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 560d0c9..a6904f7 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -62,13 +62,19 @@ public class AccessibilityNodeInfo implements Parcelable {
private static final boolean DEBUG = false;
/** @hide */
- public static final int UNDEFINED = -1;
+ public static final int UNDEFINED_CONNECTION_ID = -1;
/** @hide */
- public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED);
+ public static final int UNDEFINED_SELECTION_INDEX = -1;
/** @hide */
- public static final int ACTIVE_WINDOW_ID = UNDEFINED;
+ public static final int UNDEFINED_ITEM_ID = Integer.MAX_VALUE;
+
+ /** @hide */
+ public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID);
+
+ /** @hide */
+ public static final int ACTIVE_WINDOW_ID = UNDEFINED_ITEM_ID;
/** @hide */
public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
@@ -504,6 +510,13 @@ public class AccessibilityNodeInfo implements Parcelable {
* @hide
*/
public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) {
+ // We changed the value for undefined node to positive due to wrong
+ // global id composition (two 32-bin ints into one 64-bit long) but
+ // the value used for the host node provider view has id -1 so we
+ // remap it here.
+ if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
+ virtualDescendantId = UNDEFINED_ITEM_ID;
+ }
return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
}
@@ -515,7 +528,7 @@ public class AccessibilityNodeInfo implements Parcelable {
private boolean mSealed;
// Data.
- private int mWindowId = UNDEFINED;
+ private int mWindowId = UNDEFINED_ITEM_ID;
private long mSourceNodeId = ROOT_NODE_ID;
private long mParentNodeId = ROOT_NODE_ID;
private long mLabelForId = ROOT_NODE_ID;
@@ -536,14 +549,14 @@ public class AccessibilityNodeInfo implements Parcelable {
private int mMovementGranularities;
- private int mTextSelectionStart = UNDEFINED;
- private int mTextSelectionEnd = UNDEFINED;
+ private int mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
+ private int mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
private int mInputType = InputType.TYPE_NULL;
private int mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
private Bundle mExtras;
- private int mConnectionId = UNDEFINED;
+ private int mConnectionId = UNDEFINED_CONNECTION_ID;
private RangeInfo mRangeInfo;
private CollectionInfo mCollectionInfo;
@@ -567,7 +580,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param source The info source.
*/
public void setSource(View source) {
- setSource(source, UNDEFINED);
+ setSource(source, UNDEFINED_ITEM_ID);
}
/**
@@ -591,9 +604,9 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
public void setSource(View root, int virtualDescendantId) {
enforceNotSealed();
- mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
+ mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED_ITEM_ID;
final int rootAccessibilityViewId =
- (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
+ (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
@@ -766,7 +779,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void addChild(View child) {
- addChildInternal(child, UNDEFINED, true);
+ addChildInternal(child, UNDEFINED_ITEM_ID, true);
}
/**
@@ -776,7 +789,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @hide
*/
public void addChildUnchecked(View child) {
- addChildInternal(child, UNDEFINED, false);
+ addChildInternal(child, UNDEFINED_ITEM_ID, false);
}
/**
@@ -794,7 +807,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @throws IllegalStateException If called from an AccessibilityService.
*/
public boolean removeChild(View child) {
- return removeChild(child, UNDEFINED);
+ return removeChild(child, UNDEFINED_ITEM_ID);
}
/**
@@ -821,7 +834,7 @@ public class AccessibilityNodeInfo implements Parcelable {
mChildNodeIds = new LongArray();
}
final int rootAccessibilityViewId =
- (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
+ (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
// If we're checking uniqueness and the ID already exists, abort.
if (checked && mChildNodeIds.indexOf(childNodeId) >= 0) {
@@ -847,7 +860,7 @@ public class AccessibilityNodeInfo implements Parcelable {
return false;
}
final int rootAccessibilityViewId =
- (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
+ (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
final int index = childIds.indexOf(childNodeId);
if (index < 0) {
@@ -1043,6 +1056,22 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Gets the window to which this node belongs.
+ *
+ * @return The window.
+ *
+ * @see android.accessibilityservice.AccessibilityService#getWindows()
+ */
+ public AccessibilityWindowInfo getWindow() {
+ enforceSealed();
+ if (!canPerformRequestOverConnection(mSourceNodeId)) {
+ return null;
+ }
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ return client.getWindow(mConnectionId, mWindowId);
+ }
+
+ /**
* Gets the parent.
* <p>
* <strong>Note:</strong> It is a client responsibility to recycle the
@@ -1059,7 +1088,8 @@ public class AccessibilityNodeInfo implements Parcelable {
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mWindowId, mParentNodeId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ mWindowId, mParentNodeId, false, FLAG_PREFETCH_PREDECESSORS
+ | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
}
/**
@@ -1084,7 +1114,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void setParent(View parent) {
- setParent(parent, UNDEFINED);
+ setParent(parent, UNDEFINED_ITEM_ID);
}
/**
@@ -1109,7 +1139,7 @@ public class AccessibilityNodeInfo implements Parcelable {
public void setParent(View root, int virtualDescendantId) {
enforceNotSealed();
final int rootAccessibilityViewId =
- (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
+ (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
@@ -1811,7 +1841,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param labeled The view for which this info serves as a label.
*/
public void setLabelFor(View labeled) {
- setLabelFor(labeled, UNDEFINED);
+ setLabelFor(labeled, UNDEFINED_ITEM_ID);
}
/**
@@ -1836,7 +1866,7 @@ public class AccessibilityNodeInfo implements Parcelable {
public void setLabelFor(View root, int virtualDescendantId) {
enforceNotSealed();
final int rootAccessibilityViewId = (root != null)
- ? root.getAccessibilityViewId() : UNDEFINED;
+ ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
@@ -1858,7 +1888,8 @@ public class AccessibilityNodeInfo implements Parcelable {
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mWindowId, mLabelForId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ mWindowId, mLabelForId, false, FLAG_PREFETCH_PREDECESSORS
+ | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
}
/**
@@ -1868,7 +1899,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param label The view that labels this node's source.
*/
public void setLabeledBy(View label) {
- setLabeledBy(label, UNDEFINED);
+ setLabeledBy(label, UNDEFINED_ITEM_ID);
}
/**
@@ -1893,7 +1924,7 @@ public class AccessibilityNodeInfo implements Parcelable {
public void setLabeledBy(View root, int virtualDescendantId) {
enforceNotSealed();
final int rootAccessibilityViewId = (root != null)
- ? root.getAccessibilityViewId() : UNDEFINED;
+ ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
@@ -1915,7 +1946,8 @@ public class AccessibilityNodeInfo implements Parcelable {
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mWindowId, mLabeledById, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ mWindowId, mLabeledById, false, FLAG_PREFETCH_PREDECESSORS
+ | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
}
/**
@@ -2362,6 +2394,7 @@ public class AccessibilityNodeInfo implements Parcelable {
if (mChildNodeIds == null) {
mChildNodeIds = otherChildNodeIds.clone();
} else {
+ mChildNodeIds.clear();
mChildNodeIds.addAll(otherChildNodeIds);
}
}
@@ -2474,8 +2507,8 @@ public class AccessibilityNodeInfo implements Parcelable {
mParentNodeId = ROOT_NODE_ID;
mLabelForId = ROOT_NODE_ID;
mLabeledById = ROOT_NODE_ID;
- mWindowId = UNDEFINED;
- mConnectionId = UNDEFINED;
+ mWindowId = UNDEFINED_ITEM_ID;
+ mConnectionId = UNDEFINED_CONNECTION_ID;
mMovementGranularities = 0;
if (mChildNodeIds != null) {
mChildNodeIds.clear();
@@ -2489,8 +2522,8 @@ public class AccessibilityNodeInfo implements Parcelable {
mContentDescription = null;
mViewIdResourceName = null;
mActions = 0;
- mTextSelectionStart = UNDEFINED;
- mTextSelectionEnd = UNDEFINED;
+ mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
+ mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
mInputType = InputType.TYPE_NULL;
mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
if (mExtras != null) {
@@ -2583,9 +2616,9 @@ public class AccessibilityNodeInfo implements Parcelable {
}
private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
- return (mWindowId != UNDEFINED
- && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED
- && mConnectionId != UNDEFINED);
+ return (mWindowId != UNDEFINED_ITEM_ID
+ && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID
+ && mConnectionId != UNDEFINED_CONNECTION_ID);
}
@Override
@@ -2625,6 +2658,7 @@ public class AccessibilityNodeInfo implements Parcelable {
builder.append(super.toString());
if (DEBUG) {
+ builder.append("; sourceNodeId: " + mSourceNodeId);
builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
builder.append("; mParentNodeId: " + mParentNodeId);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
deleted file mode 100644
index b4944be..0000000
--- a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright (C) 2012 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.accessibility;
-
-import android.os.Build;
-import android.util.Log;
-import android.util.LongArray;
-import android.util.LongSparseArray;
-
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * Simple cache for AccessibilityNodeInfos. The cache is mapping an
- * accessibility id to an info. The cache allows storing of
- * <code>null</code> values. It also tracks accessibility events
- * and invalidates accordingly.
- *
- * @hide
- */
-public class AccessibilityNodeInfoCache {
-
- private static final String LOG_TAG = AccessibilityNodeInfoCache.class.getSimpleName();
-
- private static final boolean ENABLED = true;
-
- private static final boolean DEBUG = false;
-
- private static final boolean CHECK_INTEGRITY_IF_DEBUGGABLE_BUILD = true;
-
- private final Object mLock = new Object();
-
- private final LongSparseArray<AccessibilityNodeInfo> mCacheImpl;
-
- private int mWindowId;
-
- public AccessibilityNodeInfoCache() {
- if (ENABLED) {
- mCacheImpl = new LongSparseArray<AccessibilityNodeInfo>();
- } else {
- mCacheImpl = null;
- }
- }
-
- /**
- * The cache keeps track of {@link AccessibilityEvent}s and invalidates
- * cached nodes as appropriate.
- *
- * @param event An event.
- */
- public void onAccessibilityEvent(AccessibilityEvent event) {
- if (ENABLED) {
- final int eventType = event.getEventType();
- switch (eventType) {
- case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END:
- case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
- case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
- case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
- // If the active window changes, clear the cache.
- final int windowId = event.getWindowId();
- if (mWindowId != windowId) {
- mWindowId = windowId;
- clear();
- }
- } break;
- case AccessibilityEvent.TYPE_VIEW_FOCUSED:
- case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
- case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED:
- case AccessibilityEvent.TYPE_VIEW_SELECTED:
- case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
- case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
- refreshCachedNode(event.getSourceNodeId());
- } break;
- case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
- synchronized (mLock) {
- clearSubTreeLocked(event.getSourceNodeId());
- }
- } break;
- case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
- synchronized (mLock) {
- final long sourceId = event.getSourceNodeId();
- if ((event.getContentChangeTypes()
- & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
- clearSubTreeLocked(sourceId);
- } else {
- refreshCachedNode(sourceId);
- }
- }
- } break;
- }
- if (CHECK_INTEGRITY_IF_DEBUGGABLE_BUILD && Build.IS_DEBUGGABLE) {
- checkIntegrity();
- }
- }
- }
-
- private void refreshCachedNode(long sourceId) {
- if (DEBUG) {
- Log.i(LOG_TAG, "Refreshing cached node.");
- }
- synchronized (mLock) {
- AccessibilityNodeInfo cachedInfo = mCacheImpl.get(sourceId);
- // If the source is not in the cache - nothing to do.
- if (cachedInfo == null) {
- return;
- }
- // The node changed so we will just refresh it right now.
- if (cachedInfo.refresh(true)) {
- return;
- }
- // Weird, we could not refresh. Just evict the entire sub-tree.
- clearSubTreeLocked(sourceId);
- }
- }
-
- /**
- * Gets a cached {@link AccessibilityNodeInfo} given its accessibility node id.
- *
- * @param accessibilityNodeId The info accessibility node id.
- * @return The cached {@link AccessibilityNodeInfo} or null if such not found.
- */
- public AccessibilityNodeInfo get(long accessibilityNodeId) {
- if (ENABLED) {
- synchronized(mLock) {
- AccessibilityNodeInfo info = mCacheImpl.get(accessibilityNodeId);
- if (info != null) {
- // Return a copy since the client calls to AccessibilityNodeInfo#recycle()
- // will wipe the data of the cached info.
- info = AccessibilityNodeInfo.obtain(info);
- }
- if (DEBUG) {
- Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
- }
- return info;
- }
- } else {
- return null;
- }
- }
-
- /**
- * Caches an {@link AccessibilityNodeInfo} given its accessibility node id.
- *
- * @param info The {@link AccessibilityNodeInfo} to cache.
- */
- public void add(AccessibilityNodeInfo info) {
- if (ENABLED) {
- synchronized(mLock) {
- if (DEBUG) {
- Log.i(LOG_TAG, "add(" + info + ")");
- }
-
- final long sourceId = info.getSourceNodeId();
- AccessibilityNodeInfo oldInfo = mCacheImpl.get(sourceId);
- if (oldInfo != null) {
- // If the added node is in the cache we have to be careful if
- // the new one represents a source state where some of the
- // children have been removed to avoid having disconnected
- // subtrees in the cache.
- // TODO: Runs in O(n^2), could optimize to O(n + n log n)
- final LongArray newChildrenIds = info.getChildNodeIds();
- if (newChildrenIds != null) {
- final int oldChildCount = oldInfo.getChildCount();
- for (int i = 0; i < oldChildCount; i++) {
- final long oldChildId = oldInfo.getChildId(i);
- if (newChildrenIds.indexOf(oldChildId) < 0) {
- clearSubTreeLocked(oldChildId);
- }
- }
- }
-
- // Also be careful if the parent has changed since the new
- // parent may be a predecessor of the old parent which will
- // make the cached tree cyclic.
- final long oldParentId = oldInfo.getParentNodeId();
- if (info.getParentNodeId() != oldParentId) {
- clearSubTreeLocked(oldParentId);
- }
- }
-
- // Cache a copy since the client calls to AccessibilityNodeInfo#recycle()
- // will wipe the data of the cached info.
- AccessibilityNodeInfo clone = AccessibilityNodeInfo.obtain(info);
- mCacheImpl.put(sourceId, clone);
- }
- }
- }
-
- /**
- * Clears the cache.
- */
- public void clear() {
- if (ENABLED) {
- synchronized(mLock) {
- if (DEBUG) {
- Log.i(LOG_TAG, "clear()");
- }
- // Recycle the nodes before clearing the cache.
- final int nodeCount = mCacheImpl.size();
- for (int i = 0; i < nodeCount; i++) {
- AccessibilityNodeInfo info = mCacheImpl.valueAt(i);
- info.recycle();
- }
- mCacheImpl.clear();
- }
- }
- }
-
- /**
- * Clears a subtree rooted at the node with the given id.
- *
- * @param rootNodeId The root id.
- */
- private void clearSubTreeLocked(long rootNodeId) {
- if (DEBUG) {
- Log.i(LOG_TAG, "Clearing cached subtree.");
- }
- clearSubTreeRecursiveLocked(rootNodeId);
- }
-
- private void clearSubTreeRecursiveLocked(long rootNodeId) {
- AccessibilityNodeInfo current = mCacheImpl.get(rootNodeId);
- if (current == null) {
- return;
- }
- mCacheImpl.remove(rootNodeId);
- final int childCount = current.getChildCount();
- for (int i = 0; i < childCount; i++) {
- final long childNodeId = current.getChildId(i);
- clearSubTreeRecursiveLocked(childNodeId);
- }
- }
-
- /**
- * Check the integrity of the cache which is it does not have nodes
- * from more than one window, there are no duplicates, all nodes are
- * connected, there is a single input focused node, and there is a
- * single accessibility focused node.
- */
- private void checkIntegrity() {
- synchronized (mLock) {
- // Get the root.
- if (mCacheImpl.size() <= 0) {
- return;
- }
-
- // If the cache is a tree it does not matter from
- // which node we start to search for the root.
- AccessibilityNodeInfo root = mCacheImpl.valueAt(0);
- AccessibilityNodeInfo parent = root;
- while (parent != null) {
- root = parent;
- parent = mCacheImpl.get(parent.getParentNodeId());
- }
-
- // Traverse the tree and do some checks.
- final int windowId = root.getWindowId();
- AccessibilityNodeInfo accessFocus = null;
- AccessibilityNodeInfo inputFocus = null;
- HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
- Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
- fringe.add(root);
-
- while (!fringe.isEmpty()) {
- AccessibilityNodeInfo current = fringe.poll();
- // Check for duplicates
- if (!seen.add(current)) {
- Log.e(LOG_TAG, "Duplicate node: " + current);
- return;
- }
-
- // Check for one accessibility focus.
- if (current.isAccessibilityFocused()) {
- if (accessFocus != null) {
- Log.e(LOG_TAG, "Duplicate accessibility focus:" + current);
- } else {
- accessFocus = current;
- }
- }
-
- // Check for one input focus.
- if (current.isFocused()) {
- if (inputFocus != null) {
- Log.e(LOG_TAG, "Duplicate input focus: " + current);
- } else {
- inputFocus = current;
- }
- }
-
- final int childCount = current.getChildCount();
- for (int i = 0; i < childCount; i++) {
- final long childId = current.getChildId(i);
- final AccessibilityNodeInfo child = mCacheImpl.get(childId);
- if (child != null) {
- fringe.add(child);
- }
- }
- }
-
- int disconnectedNodeCount = 0;
- // Check for disconnected nodes or ones from another window.
- for (int i = 0; i < mCacheImpl.size(); i++) {
- AccessibilityNodeInfo info = mCacheImpl.valueAt(i);
- if (!seen.contains(info)) {
- if (info.getWindowId() == windowId) {
- if (DEBUG) {
- Log.e(LOG_TAG, "Disconnected node: " + info);
- }
- disconnectedNodeCount++;
- } else {
- Log.e(LOG_TAG, "Node from: " + info.getWindowId() + " not from:"
- + windowId + " " + info);
- }
- }
- }
- if (disconnectedNodeCount > 0) {
- Log.e(LOG_TAG, String.format("Found %d disconnected nodes", disconnectedNodeCount));
- }
- }
- }
-}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
index 718c32f..abcbb70 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
@@ -70,9 +70,14 @@ import java.util.List;
public abstract class AccessibilityNodeProvider {
/**
+ * The virtual id for the hosting View.
+ */
+ public static final int HOST_VIEW_ID = -1;
+
+ /**
* Returns an {@link AccessibilityNodeInfo} representing a virtual view,
* i.e. a descendant of the host View, with the given <code>virtualViewId</code>
- * or the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ * or the host View itself if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
* <p>
* A virtual descendant is an imaginary View that is reported as a part of the view
* hierarchy for accessibility purposes. This enables custom views that draw complex
@@ -99,7 +104,7 @@ public abstract class AccessibilityNodeProvider {
/**
* Performs an accessibility action on a virtual view, i.e. a descendant of the
* host View, with the given <code>virtualViewId</code> or the host View itself
- * if <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ * if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
*
* @param virtualViewId A client defined virtual view id.
* @param action The action to perform.
@@ -117,8 +122,8 @@ public abstract class AccessibilityNodeProvider {
/**
* Finds {@link AccessibilityNodeInfo}s by text. The match is case insensitive
* containment. The search is relative to the virtual view, i.e. a descendant of the
- * host View, with the given <code>virtualViewId</code> or the host View itself
- * <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ * host View, with the given <code>virtualViewId</code> or the host View itself
+ * <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
*
* @param virtualViewId A client defined virtual view id which defined
* the root of the tree in which to perform the search.
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 3fcd218..cc6a71d 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -78,7 +78,7 @@ public class AccessibilityRecord {
private boolean mIsInPool;
boolean mSealed;
- int mBooleanProperties = PROPERTY_IMPORTANT_FOR_ACCESSIBILITY;
+ int mBooleanProperties = 0;
int mCurrentItemIndex = UNDEFINED;
int mItemCount = UNDEFINED;
int mFromIndex = UNDEFINED;
@@ -791,7 +791,7 @@ public class AccessibilityRecord {
*/
void clear() {
mSealed = false;
- mBooleanProperties = PROPERTY_IMPORTANT_FOR_ACCESSIBILITY;
+ mBooleanProperties = 0;
mCurrentItemIndex = UNDEFINED;
mItemCount = UNDEFINED;
mFromIndex = UNDEFINED;
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl b/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl
new file mode 100644
index 0000000..fdb25fb
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.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.view.accessibility;
+
+parcelable AccessibilityWindowInfo;
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
new file mode 100644
index 0000000..80b5c50
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -0,0 +1,574 @@
+/*
+ * 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.view.accessibility;
+
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.LongArray;
+import android.util.Pools.SynchronizedPool;
+
+/**
+ * This class represents a state snapshot of a window for accessibility
+ * purposes. The screen content contains one or more windows where some
+ * windows can be descendants of other windows, which is the windows are
+ * hierarchically ordered. Note that there is no root window. Hence, the
+ * screen content can be seen as a collection of window trees.
+ */
+public final class AccessibilityWindowInfo implements Parcelable {
+
+ private static final boolean DEBUG = false;
+
+ /**
+ * Window type: This is an application window. Such a window shows UI for
+ * interacting with an application.
+ */
+ public static final int TYPE_APPLICATION = 1;
+
+ /**
+ * Window type: This is an input method window. Such a window shows UI for
+ * inputting text such as keyboard, suggestions, etc.
+ */
+ public static final int TYPE_INPUT_METHOD = 2;
+
+ /**
+ * Window type: This is an system window. Such a window shows UI for
+ * interacting with the system.
+ */
+ public static final int TYPE_SYSTEM = 3;
+
+ private static final int UNDEFINED = -1;
+
+ private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0;
+ private static final int BOOLEAN_PROPERTY_FOCUSED = 1 << 1;
+
+ // Housekeeping.
+ private static final int MAX_POOL_SIZE = 10;
+ private static final SynchronizedPool<AccessibilityWindowInfo> sPool =
+ new SynchronizedPool<AccessibilityWindowInfo>(MAX_POOL_SIZE);
+
+ // Data.
+ private int mType = UNDEFINED;
+ private int mLayer = UNDEFINED;
+ private int mBooleanProperties;
+ private int mId = UNDEFINED;
+ private int mParentId = UNDEFINED;
+ private final Rect mBoundsInScreen = new Rect();
+ private LongArray mChildIds;
+
+ private int mConnectionId = UNDEFINED;
+
+ private AccessibilityWindowInfo() {
+ /* do nothing - hide constructor */
+ }
+
+ /**
+ * Gets the type of the window.
+ *
+ * @return The type.
+ *
+ * @see #TYPE_APPLICATION
+ * @see #TYPE_INPUT_METHOD
+ * @see #TYPE_SYSTEM
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Sets the type of the window.
+ *
+ * @param The type
+ *
+ * @hide
+ */
+ public void setType(int type) {
+ mType = type;
+ }
+
+ /**
+ * Gets the layer which determines the Z-order of the window. Windows
+ * with greater layer appear on top of windows with lesser layer.
+ *
+ * @return The window layer.
+ */
+ public int getLayer() {
+ return mLayer;
+ }
+
+ /**
+ * Sets the layer which determines the Z-order of the window. Windows
+ * with greater layer appear on top of windows with lesser layer.
+ *
+ * @param The window layer.
+ *
+ * @hide
+ */
+ public void setLayer(int layer) {
+ mLayer = layer;
+ }
+
+ /**
+ * Gets the root node in the window's hierarchy.
+ *
+ * @return The root node.
+ */
+ public AccessibilityNodeInfo getRoot() {
+ if (mConnectionId == UNDEFINED) {
+ return null;
+ }
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
+ mId, AccessibilityNodeInfo.ROOT_NODE_ID,
+ true, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
+ }
+
+ /**
+ * Gets the parent window if such.
+ *
+ * @return The parent window.
+ */
+ public AccessibilityWindowInfo getParent() {
+ if (mConnectionId == UNDEFINED || mParentId == UNDEFINED) {
+ return null;
+ }
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ return client.getWindow(mConnectionId, mParentId);
+ }
+
+ /**
+ * Sets the parent window id.
+ *
+ * @param parentId The parent id.
+ *
+ * @hide
+ */
+ public void setParentId(int parentId) {
+ mParentId = parentId;
+ }
+
+ /**
+ * Gets the unique window id.
+ *
+ * @return windowId The window id.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Sets the unique window id.
+ *
+ * @param windowId The window id.
+ *
+ * @hide
+ */
+ public void setId(int id) {
+ mId = id;
+ }
+
+ /**
+ * Sets the unique id of the IAccessibilityServiceConnection over which
+ * this instance can send requests to the system.
+ *
+ * @param connectionId The connection id.
+ *
+ * @hide
+ */
+ public void setConnectionId(int connectionId) {
+ mConnectionId = connectionId;
+ }
+
+ /**
+ * Gets the bounds of this window in the screen.
+ *
+ * @param outBounds The out window bounds.
+ */
+ public void getBoundsInScreen(Rect outBounds) {
+ outBounds.set(mBoundsInScreen);
+ }
+
+ /**
+ * Sets the bounds of this window in the screen.
+ *
+ * @param bounds The out window bounds.
+ *
+ * @hide
+ */
+ public void setBoundsInScreen(Rect bounds) {
+ mBoundsInScreen.set(bounds);
+ }
+
+ /**
+ * Gets if this window is active. An active window is the one
+ * the user is currently touching or the window has input focus
+ * and the user is not touching any window.
+ *
+ * @return Whether this is the active window.
+ */
+ public boolean isActive() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE);
+ }
+
+ /**
+ * Sets if this window is active, which is this is the window
+ * the user is currently touching or the window has input focus
+ * and the user is not touching any window.
+ *
+ * @param Whether this is the active window.
+ *
+ * @hide
+ */
+ public void setActive(boolean active) {
+ setBooleanProperty(BOOLEAN_PROPERTY_ACTIVE, active);
+ }
+
+ /**
+ * Gets if this window has input focus.
+ *
+ * @return Whether has input focus.
+ */
+ public boolean isFocused() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED);
+ }
+
+ /**
+ * Sets if this window has input focus.
+ *
+ * @param Whether has input focus.
+ *
+ * @hide
+ */
+ public void setFocused(boolean focused) {
+ setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused);
+ }
+
+ /**
+ * Gets the number of child windows.
+ *
+ * @return The child count.
+ */
+ public int getChildCount() {
+ return (mChildIds != null) ? mChildIds.size() : 0;
+ }
+
+ /**
+ * Gets the child window at a given index.
+ *
+ * @param index The index.
+ * @return The child.
+ */
+ public AccessibilityWindowInfo getChild(int index) {
+ if (mChildIds == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (mConnectionId == UNDEFINED) {
+ return null;
+ }
+ final int childId = (int) mChildIds.get(index);
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ return client.getWindow(mConnectionId, childId);
+ }
+
+ /**
+ * Adds a child window.
+ *
+ * @param childId The child window id.
+ *
+ * @hide
+ */
+ public void addChild(int childId) {
+ if (mChildIds == null) {
+ mChildIds = new LongArray();
+ }
+ mChildIds.add(childId);
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
+ * created.
+ *
+ * @return An instance.
+ */
+ public static AccessibilityWindowInfo obtain() {
+ AccessibilityWindowInfo info = sPool.acquire();
+ if (info == null) {
+ info = new AccessibilityWindowInfo();
+ }
+ return info;
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
+ * created. The returned instance is initialized from the given
+ * <code>info</code>.
+ *
+ * @param info The other info.
+ * @return An instance.
+ */
+ public static AccessibilityWindowInfo obtain(AccessibilityWindowInfo info) {
+ AccessibilityWindowInfo infoClone = obtain();
+
+ infoClone.mType = info.mType;
+ infoClone.mLayer = info.mLayer;
+ infoClone.mBooleanProperties = info.mBooleanProperties;
+ infoClone.mId = info.mId;
+ infoClone.mParentId = info.mParentId;
+ infoClone.mBoundsInScreen.set(info.mBoundsInScreen);
+
+ if (info.mChildIds != null && info.mChildIds.size() > 0) {
+ if (infoClone.mChildIds == null) {
+ infoClone.mChildIds = info.mChildIds.clone();
+ } else {
+ infoClone.mChildIds.addAll(info.mChildIds);
+ }
+ }
+
+ infoClone.mConnectionId = info.mConnectionId;
+
+ return infoClone;
+ }
+
+ /**
+ * Return an instance back to be reused.
+ * <p>
+ * <strong>Note:</strong> You must not touch the object after calling this function.
+ * </p>
+ *
+ * @throws IllegalStateException If the info is already recycled.
+ */
+ public void recycle() {
+ clear();
+ sPool.release(this);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mType);
+ parcel.writeInt(mLayer);
+ parcel.writeInt(mBooleanProperties);
+ parcel.writeInt(mId);
+ parcel.writeInt(mParentId);
+ mBoundsInScreen.writeToParcel(parcel, flags);
+
+ final LongArray childIds = mChildIds;
+ if (childIds == null) {
+ parcel.writeInt(0);
+ } else {
+ final int childCount = childIds.size();
+ parcel.writeInt(childCount);
+ for (int i = 0; i < childCount; i++) {
+ parcel.writeInt((int) childIds.get(i));
+ }
+ }
+
+ parcel.writeInt(mConnectionId);
+ }
+
+ private void initFromParcel(Parcel parcel) {
+ mType = parcel.readInt();
+ mLayer = parcel.readInt();
+ mBooleanProperties = parcel.readInt();
+ mId = parcel.readInt();
+ mParentId = parcel.readInt();
+ mBoundsInScreen.readFromParcel(parcel);
+
+ final int childCount = parcel.readInt();
+ if (childCount > 0) {
+ if (mChildIds == null) {
+ mChildIds = new LongArray(childCount);
+ }
+ for (int i = 0; i < childCount; i++) {
+ final int childId = parcel.readInt();
+ mChildIds.add(childId);
+ }
+ }
+
+ mConnectionId = parcel.readInt();
+ }
+
+ @Override
+ public int hashCode() {
+ return mId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ AccessibilityWindowInfo other = (AccessibilityWindowInfo) obj;
+ return (mId == other.mId);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("AccessibilityWindowInfo[");
+ builder.append("id=").append(mId);
+ builder.append(", type=").append(typeToString(mType));
+ builder.append(", layer=").append(mLayer);
+ builder.append(", bounds=").append(mBoundsInScreen);
+ builder.append(", focused=").append(isFocused());
+ builder.append(", active=").append(isActive());
+ if (DEBUG) {
+ builder.append(", parent=").append(mParentId);
+ builder.append(", children=[");
+ if (mChildIds != null) {
+ final int childCount = mChildIds.size();
+ for (int i = 0; i < childCount; i++) {
+ builder.append(mChildIds.get(i));
+ if (i < childCount - 1) {
+ builder.append(',');
+ }
+ }
+ } else {
+ builder.append("null");
+ }
+ builder.append(']');
+ } else {
+ builder.append(", hasParent=").append(mParentId != UNDEFINED);
+ builder.append(", hasChildren=").append(mChildIds != null
+ && mChildIds.size() > 0);
+ }
+ builder.append(']');
+ return builder.toString();
+ }
+
+ /**
+ * Clears the internal state.
+ */
+ private void clear() {
+ mType = UNDEFINED;
+ mLayer = UNDEFINED;
+ mBooleanProperties = 0;
+ mId = UNDEFINED;
+ mParentId = UNDEFINED;
+ mBoundsInScreen.setEmpty();
+ if (mChildIds != null) {
+ mChildIds.clear();
+ }
+ mConnectionId = UNDEFINED;
+ }
+
+ /**
+ * Gets the value of a boolean property.
+ *
+ * @param property The property.
+ * @return The value.
+ */
+ private boolean getBooleanProperty(int property) {
+ return (mBooleanProperties & property) != 0;
+ }
+
+ /**
+ * Sets a boolean property.
+ *
+ * @param property The property.
+ * @param value The value.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ private void setBooleanProperty(int property, boolean value) {
+ if (value) {
+ mBooleanProperties |= property;
+ } else {
+ mBooleanProperties &= ~property;
+ }
+ }
+
+ private static String typeToString(int type) {
+ switch (type) {
+ case TYPE_APPLICATION: {
+ return "TYPE_APPLICATION";
+ }
+ case TYPE_INPUT_METHOD: {
+ return "TYPE_INPUT_METHOD";
+ }
+ case TYPE_SYSTEM: {
+ return "TYPE_SYSTEM";
+ }
+ default:
+ return "<UNKNOWN>";
+ }
+ }
+
+ /**
+ * Checks whether this window changed. The argument should be
+ * another state of the same window, which is have the same id
+ * and type as they never change.
+ *
+ * @param other The new state.
+ * @return Whether something changed.
+ *
+ * @hide
+ */
+ public boolean changed(AccessibilityWindowInfo other) {
+ if (other.mId != mId) {
+ throw new IllegalArgumentException("Not same window.");
+ }
+ if (other.mType != mType) {
+ throw new IllegalArgumentException("Not same type.");
+ }
+ if (!mBoundsInScreen.equals(mBoundsInScreen)) {
+ return true;
+ }
+ if (mLayer != other.mLayer) {
+ return true;
+ }
+ if (mBooleanProperties != other.mBooleanProperties) {
+ return true;
+ }
+ if (mParentId != other.mParentId) {
+ return true;
+ }
+ if (mChildIds == null) {
+ if (other.mChildIds != null) {
+ return true;
+ }
+ } else if (!mChildIds.equals(other.mChildIds)) {
+ return true;
+ }
+ return false;
+ }
+
+ public static final Parcelable.Creator<AccessibilityWindowInfo> CREATOR =
+ new Creator<AccessibilityWindowInfo>() {
+ @Override
+ public AccessibilityWindowInfo createFromParcel(Parcel parcel) {
+ AccessibilityWindowInfo info = obtain();
+ info.initFromParcel(parcel);
+ return info;
+ }
+
+ @Override
+ public AccessibilityWindowInfo[] newArray(int size) {
+ return new AccessibilityWindowInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/view/animation/ClipRectAnimation.java b/core/java/android/view/animation/ClipRectAnimation.java
new file mode 100644
index 0000000..2361501
--- /dev/null
+++ b/core/java/android/view/animation/ClipRectAnimation.java
@@ -0,0 +1,59 @@
+/*
+ * 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.view.animation;
+
+import android.graphics.Rect;
+
+/**
+ * An animation that controls the clip of an object. See the
+ * {@link android.view.animation full package} description for details and
+ * sample code.
+ *
+ * @hide
+ */
+public class ClipRectAnimation extends Animation {
+ private Rect mFromRect = new Rect();
+ private Rect mToRect = new Rect();
+
+ /**
+ * Constructor to use when building a ClipRectAnimation from code
+ *
+ * @param fromClip the clip rect to animate from
+ * @param toClip the clip rect to animate to
+ */
+ public ClipRectAnimation(Rect fromClip, Rect toClip) {
+ if (fromClip == null || toClip == null) {
+ throw new RuntimeException("Expected non-null animation clip rects");
+ }
+ mFromRect.set(fromClip);
+ mToRect.set(toClip);
+ }
+
+ @Override
+ protected void applyTransformation(float it, Transformation tr) {
+ int l = mFromRect.left + (int) ((mToRect.left - mFromRect.left) * it);
+ int t = mFromRect.top + (int) ((mToRect.top - mFromRect.top) * it);
+ int r = mFromRect.right + (int) ((mToRect.right - mFromRect.right) * it);
+ int b = mFromRect.bottom + (int) ((mToRect.bottom - mFromRect.bottom) * it);
+ tr.setClipRect(l, t, r, b);
+ }
+
+ @Override
+ public boolean willChangeTransformationMatrix() {
+ return false;
+ }
+}
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
index 890909b..2f4fe73 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -17,6 +17,7 @@
package android.view.animation;
import android.graphics.Matrix;
+import android.graphics.Rect;
import java.io.PrintWriter;
@@ -47,6 +48,9 @@ public class Transformation {
protected float mAlpha;
protected int mTransformationType;
+ private boolean mHasClipRect;
+ private Rect mClipRect = new Rect();
+
/**
* Creates a new transformation with alpha = 1 and the identity matrix.
*/
@@ -65,6 +69,8 @@ public class Transformation {
} else {
mMatrix.reset();
}
+ mClipRect.setEmpty();
+ mHasClipRect = false;
mAlpha = 1.0f;
mTransformationType = TYPE_BOTH;
}
@@ -98,9 +104,15 @@ public class Transformation {
public void set(Transformation t) {
mAlpha = t.getAlpha();
mMatrix.set(t.getMatrix());
+ if (t.mHasClipRect) {
+ setClipRect(t.getClipRect());
+ } else {
+ mHasClipRect = false;
+ mClipRect.setEmpty();
+ }
mTransformationType = t.getTransformationType();
}
-
+
/**
* Apply this Transformation to an existing Transformation, e.g. apply
* a scale effect to something that has already been rotated.
@@ -109,6 +121,9 @@ public class Transformation {
public void compose(Transformation t) {
mAlpha *= t.getAlpha();
mMatrix.preConcat(t.getMatrix());
+ if (t.mHasClipRect) {
+ setClipRect(t.getClipRect());
+ }
}
/**
@@ -119,6 +134,9 @@ public class Transformation {
public void postCompose(Transformation t) {
mAlpha *= t.getAlpha();
mMatrix.postConcat(t.getMatrix());
+ if (t.mHasClipRect) {
+ setClipRect(t.getClipRect());
+ }
}
/**
@@ -138,6 +156,39 @@ public class Transformation {
}
/**
+ * Sets the current Transform's clip rect
+ * @hide
+ */
+ public void setClipRect(Rect r) {
+ setClipRect(r.left, r.top, r.right, r.bottom);
+ }
+
+ /**
+ * Sets the current Transform's clip rect
+ * @hide
+ */
+ public void setClipRect(int l, int t, int r, int b) {
+ mClipRect.set(l, t, r, b);
+ mHasClipRect = true;
+ }
+
+ /**
+ * Returns the current Transform's clip rect
+ * @hide
+ */
+ public Rect getClipRect() {
+ return mClipRect;
+ }
+
+ /**
+ * Returns whether the current Transform's clip rect is set
+ * @hide
+ */
+ public boolean hasClipRect() {
+ return mHasClipRect;
+ }
+
+ /**
* @return The degree of transparency
*/
public float getAlpha() {
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 16c41f3..cd75010 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -36,4 +36,6 @@ interface IAppOpsService {
List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
void setMode(int code, int uid, String packageName, int mode);
void resetAllModes();
+ int checkAudioOperation(int code, int stream, int uid, String packageName);
+ void setAudioRestriction(int code, int stream, int uid, int mode, in String[] exceptionPackages);
}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 7ddd5d2..5214dd9 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -59,6 +59,5 @@ interface IAppWidgetService {
void bindRemoteViewsService(int appWidgetId, in Intent intent, in IBinder connection, int userId);
void unbindRemoteViewsService(int appWidgetId, in Intent intent, int userId);
int[] getAppWidgetIds(in ComponentName provider, int userId);
-
}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index a604d84..1bfad05 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -34,6 +34,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
import libcore.io.ErrnoException;
import libcore.io.Libcore;
@@ -55,20 +57,24 @@ public class LocalTransport extends IBackupTransport.Stub {
private static final String TRANSPORT_DESTINATION_STRING
= "Backing up to debug-only private cache";
- // The single hardcoded restore set always has the same (nonzero!) token
- private static final long RESTORE_TOKEN = 1;
+ // The currently-active restore set always has the same (nonzero!) token
+ private static final long CURRENT_SET_TOKEN = 1;
private Context mContext;
private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
+ private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
+
private PackageInfo[] mRestorePackages = null;
private int mRestorePackage = -1; // Index into mRestorePackages
+ private File mRestoreDataDir;
+ private long mRestoreToken;
public LocalTransport(Context context) {
mContext = context;
- mDataDir.mkdirs();
- if (!SELinux.restorecon(mDataDir)) {
- Log.e(TAG, "SELinux restorecon failed for " + mDataDir);
+ mCurrentSetDir.mkdirs();
+ if (!SELinux.restorecon(mCurrentSetDir)) {
+ Log.e(TAG, "SELinux restorecon failed for " + mCurrentSetDir);
}
}
@@ -96,7 +102,7 @@ public class LocalTransport extends IBackupTransport.Stub {
public int initializeDevice() {
if (DEBUG) Log.v(TAG, "wiping all data");
- deleteContents(mDataDir);
+ deleteContents(mCurrentSetDir);
return BackupConstants.TRANSPORT_OK;
}
@@ -112,7 +118,7 @@ public class LocalTransport extends IBackupTransport.Stub {
}
}
- File packageDir = new File(mDataDir, packageInfo.packageName);
+ File packageDir = new File(mCurrentSetDir, packageInfo.packageName);
packageDir.mkdirs();
// Each 'record' in the restore set is kept in its own file, named by
@@ -193,7 +199,7 @@ public class LocalTransport extends IBackupTransport.Stub {
public int clearBackupData(PackageInfo packageInfo) {
if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
- File packageDir = new File(mDataDir, packageInfo.packageName);
+ File packageDir = new File(mCurrentSetDir, packageInfo.packageName);
final File[] fileset = packageDir.listFiles();
if (fileset != null) {
for (File f : fileset) {
@@ -210,22 +216,38 @@ public class LocalTransport extends IBackupTransport.Stub {
}
// Restore handling
+ static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 };
public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
- // one hardcoded restore set
- RestoreSet set = new RestoreSet("Local disk image", "flash", RESTORE_TOKEN);
- RestoreSet[] array = { set };
- return array;
+ long[] existing = new long[POSSIBLE_SETS.length + 1];
+ int num = 0;
+
+ // see which possible non-current sets exist, then put the current set at the end
+ for (long token : POSSIBLE_SETS) {
+ if ((new File(mDataDir, Long.toString(token))).exists()) {
+ existing[num++] = token;
+ }
+ }
+ // and always the currently-active set last
+ existing[num++] = CURRENT_SET_TOKEN;
+
+ RestoreSet[] available = new RestoreSet[num];
+ for (int i = 0; i < available.length; i++) {
+ available[i] = new RestoreSet("Local disk image", "flash", existing[i]);
+ }
+ return available;
}
public long getCurrentRestoreSet() {
- // The hardcoded restore set always has the same token
- return RESTORE_TOKEN;
+ // The current restore set always has the same token
+ return CURRENT_SET_TOKEN;
}
public int startRestore(long token, PackageInfo[] packages) {
if (DEBUG) Log.v(TAG, "start restore " + token);
mRestorePackages = packages;
mRestorePackage = -1;
+ mRestoreToken = token;
+ mRestoreDataDir = new File(mDataDir, Long.toString(token));
return BackupConstants.TRANSPORT_OK;
}
@@ -234,7 +256,7 @@ public class LocalTransport extends IBackupTransport.Stub {
while (++mRestorePackage < mRestorePackages.length) {
String name = mRestorePackages[mRestorePackage].packageName;
// skip packages where we have a data dir but no actual contents
- String[] contents = (new File(mDataDir, name)).list();
+ String[] contents = (new File(mRestoreDataDir, name)).list();
if (contents != null && contents.length > 0) {
if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name);
return name;
@@ -248,29 +270,32 @@ public class LocalTransport extends IBackupTransport.Stub {
public int getRestoreData(ParcelFileDescriptor outFd) {
if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
- File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);
+ File packageDir = new File(mRestoreDataDir, mRestorePackages[mRestorePackage].packageName);
// The restore set is the concatenation of the individual record blobs,
- // each of which is a file in the package's directory
- File[] blobs = packageDir.listFiles();
+ // each of which is a file in the package's directory. We return the
+ // data in lexical order sorted by key, so that apps which use synthetic
+ // keys like BLOB_1, BLOB_2, etc will see the date in the most obvious
+ // order.
+ ArrayList<DecodedFilename> blobs = contentsByKey(packageDir);
if (blobs == null) { // nextRestorePackage() ensures the dir exists, so this is an error
- Log.e(TAG, "Error listing directory: " + packageDir);
+ Log.e(TAG, "No keys for package: " + packageDir);
return BackupConstants.TRANSPORT_ERROR;
}
// We expect at least some data if the directory exists in the first place
- if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files");
+ if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.size() + " key files");
BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
try {
- for (File f : blobs) {
+ for (DecodedFilename keyEntry : blobs) {
+ File f = keyEntry.file;
FileInputStream in = new FileInputStream(f);
try {
int size = (int) f.length();
byte[] buf = new byte[size];
in.read(buf);
- String key = new String(Base64.decode(f.getName()));
- if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size);
- out.writeEntityHeader(key, size);
+ if (DEBUG) Log.v(TAG, " ... key=" + keyEntry.key + " size=" + size);
+ out.writeEntityHeader(keyEntry.key, size);
out.writeEntityData(buf, size);
} finally {
in.close();
@@ -283,6 +308,39 @@ public class LocalTransport extends IBackupTransport.Stub {
}
}
+ static class DecodedFilename implements Comparable<DecodedFilename> {
+ public File file;
+ public String key;
+
+ public DecodedFilename(File f) {
+ file = f;
+ key = new String(Base64.decode(f.getName()));
+ }
+
+ @Override
+ public int compareTo(DecodedFilename other) {
+ // sorts into ascending lexical order by decoded key
+ return key.compareTo(other.key);
+ }
+ }
+
+ // Return a list of the files in the given directory, sorted lexically by
+ // the Base64-decoded file name, not by the on-disk filename
+ private ArrayList<DecodedFilename> contentsByKey(File dir) {
+ File[] allFiles = dir.listFiles();
+ if (allFiles == null || allFiles.length == 0) {
+ return null;
+ }
+
+ // Decode the filenames into keys then sort lexically by key
+ ArrayList<DecodedFilename> contents = new ArrayList<DecodedFilename>();
+ for (File f : allFiles) {
+ contents.add(new DecodedFilename(f));
+ }
+ Collections.sort(contents);
+ return contents;
+ }
+
public void finishRestore() {
if (DEBUG) Log.v(TAG, "finishRestore()");
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 26d7f5f..082f1a5 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -208,7 +208,8 @@ public final class BatteryStatsImpl extends BatteryStats {
int mNumHistoryTagChars = 0;
int mHistoryBufferLastPos = -1;
boolean mHistoryOverflow = false;
- long mLastHistoryTime = 0;
+ long mLastHistoryElapsedRealtime = 0;
+ long mLastHistoryUptime = 0;
final HistoryItem mHistoryCur = new HistoryItem();
@@ -1957,12 +1958,12 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- void addHistoryBufferLocked(long curTime) {
+ void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs) {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
- final long timeDiff = (mHistoryBaseTime+curTime) - mHistoryLastWritten.time;
+ final long timeDiff = (mHistoryBaseTime+elapsedRealtimeMs) - mHistoryLastWritten.time;
final int diffStates = mHistoryLastWritten.states^mHistoryCur.states;
final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
if (DEBUG) Slog.i(TAG, "ADD: tdelta=" + timeDiff + " diff="
@@ -1981,14 +1982,14 @@ public final class BatteryStatsImpl extends BatteryStats {
&& mHistoryLastWritten.batteryTemperature == mHistoryCur.batteryTemperature
&& mHistoryLastWritten.batteryVoltage == mHistoryCur.batteryVoltage) {
// We can merge this new change in with the last one. Merging is
- // allows as long as only the states have changed, and within those states
+ // allowed as long as only the states have changed, and within those states
// as long as no bit has changed both between now and the last entry, as
// well as the last entry and the one before it (so we capture any toggles).
if (DEBUG) Slog.i(TAG, "ADD: rewinding back to " + mHistoryBufferLastPos);
mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
mHistoryBufferLastPos = -1;
- curTime = mHistoryLastWritten.time - mHistoryBaseTime;
+ elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTime;
// If the last written history had a wakelock tag, we need to retain it.
// Note that the condition above made sure that we aren't in a case where
// both it and the current history item have a wakelock tag.
@@ -2018,8 +2019,8 @@ public final class BatteryStatsImpl extends BatteryStats {
if (dataSize >= MAX_HISTORY_BUFFER) {
if (!mHistoryOverflow) {
mHistoryOverflow = true;
- addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
- addHistoryBufferLocked(curTime, HistoryItem.CMD_OVERFLOW);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW);
return;
}
@@ -2034,22 +2035,23 @@ public final class BatteryStatsImpl extends BatteryStats {
return;
}
- addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
return;
}
- addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
}
- private void addHistoryBufferLocked(long curTime, byte cmd) {
+ private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd) {
if (mIteratingHistory) {
throw new IllegalStateException("Can't do this while iterating history!");
}
mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
mHistoryLastLastWritten.setTo(mHistoryLastWritten);
- mHistoryLastWritten.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+ mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, mHistoryCur);
writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
- mLastHistoryTime = curTime;
+ mLastHistoryElapsedRealtime = elapsedRealtimeMs;
+ mLastHistoryUptime = uptimeMs;
mHistoryCur.wakelockTag = null;
mHistoryCur.wakeReasonTag = null;
mHistoryCur.eventCode = HistoryItem.EVENT_NONE;
@@ -2061,8 +2063,8 @@ public final class BatteryStatsImpl extends BatteryStats {
int mChangedStates = 0;
- void addHistoryRecordLocked(long curTime) {
- addHistoryBufferLocked(curTime);
+ void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs);
if (!USE_OLD_HISTORY) {
return;
@@ -2077,12 +2079,12 @@ public final class BatteryStatsImpl extends BatteryStats {
// are now resetting back to their original value, then just collapse
// into one record.
if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
- && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+2000)
+ && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+1000)
&& ((mHistoryEnd.states^mHistoryCur.states)&mChangedStates) == 0) {
// If the current is the same as the one before, then we no
// longer need the entry.
if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
- && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)
+ && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+500)
&& mHistoryLastEnd.sameNonEvent(mHistoryCur)) {
mHistoryLastEnd.next = null;
mHistoryEnd.next = mHistoryCache;
@@ -2100,7 +2102,7 @@ public final class BatteryStatsImpl extends BatteryStats {
if (mNumHistoryItems == MAX_HISTORY_ITEMS
|| mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
- addHistoryRecordLocked(curTime, HistoryItem.CMD_OVERFLOW);
+ addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW);
}
if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
@@ -2117,25 +2119,26 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- addHistoryRecordLocked(curTime, HistoryItem.CMD_UPDATE);
+ addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE);
}
- void addHistoryEventLocked(long curTime, int code, String name, int uid) {
+ void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
+ String name, int uid) {
mHistoryCur.eventCode = code;
mHistoryCur.eventTag = mHistoryCur.localEventTag;
mHistoryCur.eventTag.string = name;
mHistoryCur.eventTag.uid = uid;
- addHistoryBufferLocked(curTime);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs);
}
- void addHistoryRecordLocked(long curTime, byte cmd) {
+ void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd) {
HistoryItem rec = mHistoryCache;
if (rec != null) {
mHistoryCache = rec.next;
} else {
rec = new HistoryItem();
}
- rec.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+ rec.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, mHistoryCur);
addHistoryRecordLocked(rec);
}
@@ -2164,7 +2167,8 @@ public final class BatteryStatsImpl extends BatteryStats {
}
mHistoryBaseTime = 0;
- mLastHistoryTime = 0;
+ mLastHistoryElapsedRealtime = 0;
+ mLastHistoryUptime = 0;
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
@@ -2262,7 +2266,9 @@ public final class BatteryStatsImpl extends BatteryStats {
active.remove(name);
}
}
- addHistoryEventLocked(SystemClock.elapsedRealtime(), code, name, uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ addHistoryEventLocked(elapsedRealtime, uptime, code, name, uid);
}
private void requestWakelockCpuUpdate() {
@@ -2273,7 +2279,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type,
- boolean unimportantForLogging, long elapsedRealtime) {
+ boolean unimportantForLogging, long elapsedRealtime, long uptime) {
uid = mapUid(uid);
if (type == WAKE_TYPE_PARTIAL) {
// Only care about partial wake locks, since full wake locks
@@ -2286,7 +2292,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
mHistoryCur.wakelockTag.uid = uid;
mWakeLockImportant = !unimportantForLogging;
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
} else if (!mWakeLockImportant && !unimportantForLogging) {
if (mHistoryLastWritten.wakelockTag != null) {
// We'll try to update the last tag.
@@ -2294,19 +2300,23 @@ public final class BatteryStatsImpl extends BatteryStats {
mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
mHistoryCur.wakelockTag.uid = uid;
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWakeLockImportant = true;
}
mWakeLockNesting++;
}
if (uid >= 0) {
+ //if (uid == 0) {
+ // Slog.wtf(TAG, "Acquiring wake lock from root: " + name);
+ //}
requestWakelockCpuUpdate();
getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type, elapsedRealtime);
}
}
- public void noteStopWakeLocked(int uid, int pid, String name, int type, long elapsedRealtime) {
+ public void noteStopWakeLocked(int uid, int pid, String name, int type, long elapsedRealtime,
+ long uptime) {
uid = mapUid(uid);
if (type == WAKE_TYPE_PARTIAL) {
mWakeLockNesting--;
@@ -2314,7 +2324,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
}
if (uid >= 0) {
@@ -2326,10 +2336,11 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, boolean unimportantForLogging) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
final int N = ws.size();
for (int i=0; i<N; i++) {
noteStartWakeLocked(ws.get(i), pid, name, historyName, type, unimportantForLogging,
- elapsedRealtime);
+ elapsedRealtime, uptime);
}
}
@@ -2337,36 +2348,39 @@ public final class BatteryStatsImpl extends BatteryStats {
WorkSource newWs, int newPid, String newName,
String newHistoryName, int newType, boolean newUnimportantForLogging) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
// For correct semantics, we start the need worksources first, so that we won't
// make inappropriate history items as if all wake locks went away and new ones
// appeared. This is okay because tracking of wake locks allows nesting.
- final int NN = ws.size();
+ final int NN = newWs.size();
for (int i=0; i<NN; i++) {
noteStartWakeLocked(newWs.get(i), newPid, newName, newHistoryName, newType,
- newUnimportantForLogging, elapsedRealtime);
+ newUnimportantForLogging, elapsedRealtime, uptime);
}
final int NO = ws.size();
for (int i=0; i<NO; i++) {
- noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime);
+ noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime, uptime);
}
}
public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
final int N = ws.size();
for (int i=0; i<N; i++) {
- noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime);
+ noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime, uptime);
}
}
public void noteWakeupReasonLocked(int irq, String reason) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason irq #" + irq + "\"" + reason +"\": "
+ Integer.toHexString(mHistoryCur.states));
mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
mHistoryCur.wakeReasonTag.string = reason;
mHistoryCur.wakeReasonTag.uid = irq;
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
public int startAddingCpuLocked() {
@@ -2511,11 +2525,12 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteStartSensorLocked(int uid, int sensor) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mSensorNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mSensorNesting++;
getUidStatsLocked(uid).noteStartSensor(sensor, elapsedRealtime);
@@ -2524,12 +2539,13 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteStopSensorLocked(int uid, int sensor) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mSensorNesting--;
if (mSensorNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
getUidStatsLocked(uid).noteStopSensor(sensor, elapsedRealtime);
}
@@ -2539,11 +2555,12 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteStartGpsLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mGpsNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mGpsNesting++;
getUidStatsLocked(uid).noteStartGps(elapsedRealtime);
@@ -2552,12 +2569,13 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteStopGpsLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mGpsNesting--;
if (mGpsNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_GPS_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
}
@@ -2565,10 +2583,11 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteScreenOnLocked() {
if (!mScreenOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mScreenOn = true;
mScreenOnTimer.startRunningLocked(elapsedRealtime);
if (mScreenBrightnessBin >= 0) {
@@ -2580,7 +2599,8 @@ public final class BatteryStatsImpl extends BatteryStats {
// Fake a wake lock, so we consider the device waked as long
// as the screen is on.
- noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false, elapsedRealtime);
+ noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false,
+ elapsedRealtime, uptime);
// Update discharge amounts.
if (mOnBatteryInternal) {
@@ -2592,17 +2612,19 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteScreenOffLocked() {
if (mScreenOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mScreenOn = false;
mScreenOnTimer.stopRunningLocked(elapsedRealtime);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
}
- noteStopWakeLocked(-1, -1, "screen", WAKE_TYPE_PARTIAL, elapsedRealtime);
+ noteStopWakeLocked(-1, -1, "screen", WAKE_TYPE_PARTIAL,
+ elapsedRealtime, uptime);
updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), true,
SystemClock.uptimeMillis() * 1000, elapsedRealtime * 1000);
@@ -2621,11 +2643,12 @@ public final class BatteryStatsImpl extends BatteryStats {
else if (bin >= NUM_SCREEN_BRIGHTNESS_BINS) bin = NUM_SCREEN_BRIGHTNESS_BINS-1;
if (mScreenBrightnessBin != bin) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_BRIGHTNESS_MASK)
| (bin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
if (DEBUG_HISTORY) Slog.v(TAG, "Screen brightness " + bin + " to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
if (mScreenOn) {
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
@@ -2650,12 +2673,13 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteDataConnectionActive(int type, boolean active) {
if (ConnectivityManager.isNetworkTypeMobile(type)) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mMobileRadioActive != active) {
if (active) mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
else mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mMobileRadioActive = active;
if (active) {
mMobileRadioActiveTimer.startRunningLocked(elapsedRealtime);
@@ -2672,10 +2696,11 @@ public final class BatteryStatsImpl extends BatteryStats {
public void notePhoneOnLocked() {
if (!mPhoneOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_PHONE_IN_CALL_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mPhoneOn = true;
mPhoneOnTimer.startRunningLocked(elapsedRealtime);
}
@@ -2684,10 +2709,11 @@ public final class BatteryStatsImpl extends BatteryStats {
public void notePhoneOffLocked() {
if (mPhoneOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states &= ~HistoryItem.STATE_PHONE_IN_CALL_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mPhoneOn = false;
mPhoneOnTimer.stopRunningLocked(elapsedRealtime);
}
@@ -2727,6 +2753,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mPhoneSignalStrengthBinRaw = strengthBin;
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (simState == TelephonyManager.SIM_STATE_ABSENT) {
// In this case we will always be STATE_OUT_OF_SERVICE, so need
@@ -2800,7 +2827,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
if (newHistory) {
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
}
@@ -2875,11 +2902,12 @@ public final class BatteryStatsImpl extends BatteryStats {
if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
if (mPhoneDataConnectionType != bin) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
| (bin << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
if (mPhoneDataConnectionType >= 0) {
mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(
elapsedRealtime);
@@ -2892,10 +2920,11 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiOnLocked() {
if (!mWifiOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_WIFI_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mWifiOn = true;
mWifiOnTimer.startRunningLocked(elapsedRealtime);
}
@@ -2903,11 +2932,12 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiOffLocked() {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mWifiOn) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mWifiOn = false;
mWifiOnTimer.stopRunningLocked(elapsedRealtime);
}
@@ -2916,11 +2946,12 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteAudioOnLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (!mAudioOn) {
mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mAudioOn = true;
mAudioOnTimer.startRunningLocked(elapsedRealtime);
}
@@ -2930,11 +2961,12 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteAudioOffLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mAudioOn) {
mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mAudioOn = false;
mAudioOnTimer.stopRunningLocked(elapsedRealtime);
}
@@ -2944,11 +2976,12 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteVideoOnLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (!mVideoOn) {
mHistoryCur.states |= HistoryItem.STATE_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mVideoOn = true;
mVideoOnTimer.startRunningLocked(elapsedRealtime);
}
@@ -2958,11 +2991,12 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteVideoOffLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mVideoOn) {
mHistoryCur.states &= ~HistoryItem.STATE_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mVideoOn = false;
mVideoOnTimer.stopRunningLocked(elapsedRealtime);
}
@@ -2992,10 +3026,11 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiRunningLocked(WorkSource ws) {
if (!mGlobalWifiRunning) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_WIFI_RUNNING_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mGlobalWifiRunning = true;
mGlobalWifiRunningTimer.startRunningLocked(elapsedRealtime);
int N = ws.size();
@@ -3029,10 +3064,11 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiStoppedLocked(WorkSource ws) {
if (mGlobalWifiRunning) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RUNNING_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mGlobalWifiRunning = false;
mGlobalWifiRunningTimer.stopRunningLocked(elapsedRealtime);
int N = ws.size();
@@ -3060,10 +3096,11 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteBluetoothOnLocked() {
if (!mBluetoothOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_BLUETOOTH_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mBluetoothOn = true;
mBluetoothOnTimer.startRunningLocked(elapsedRealtime);
}
@@ -3072,10 +3109,11 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteBluetoothOffLocked() {
if (mBluetoothOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states &= ~HistoryItem.STATE_BLUETOOTH_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mBluetoothOn = false;
mBluetoothOnTimer.stopRunningLocked(elapsedRealtime);
}
@@ -3098,11 +3136,12 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteFullWifiLockAcquiredLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mWifiFullLockNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWifiFullLockNesting++;
getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(elapsedRealtime);
@@ -3111,12 +3150,13 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteFullWifiLockReleasedLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mWifiFullLockNesting--;
if (mWifiFullLockNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(elapsedRealtime);
}
@@ -3126,11 +3166,12 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiScanStartedLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mWifiScanNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan started for: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWifiScanNesting++;
getUidStatsLocked(uid).noteWifiScanStartedLocked(elapsedRealtime);
@@ -3139,12 +3180,13 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiScanStoppedLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mWifiScanNesting--;
if (mWifiScanNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_SCAN_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan stopped for: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
getUidStatsLocked(uid).noteWifiScanStoppedLocked(elapsedRealtime);
}
@@ -3166,11 +3208,12 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiMulticastEnabledLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mWifiMulticastNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWifiMulticastNesting++;
getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime);
@@ -3179,12 +3222,13 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiMulticastDisabledLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mWifiMulticastNesting--;
if (mWifiMulticastNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime);
}
@@ -5627,12 +5671,13 @@ public final class BatteryStatsImpl extends BatteryStats {
public void resetAllStatsCmdLocked() {
resetAllStatsLocked();
- long uptime = SystemClock.uptimeMillis() * 1000;
+ final long mSecUptime = SystemClock.uptimeMillis();
+ long uptime = mSecUptime * 1000;
long mSecRealtime = SystemClock.elapsedRealtime();
long realtime = mSecRealtime * 1000;
mDischargeStartLevel = mHistoryCur.batteryLevel;
pullPendingStateUpdatesLocked();
- addHistoryRecordLocked(mSecRealtime);
+ addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = mHistoryCur.batteryLevel;
mOnBatteryTimeBase.reset(uptime, realtime);
mOnBatteryScreenOffTimeBase.reset(uptime, realtime);
@@ -5647,7 +5692,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOff = 0;
}
- initActiveHistoryEventsLocked(mSecRealtime);
+ initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
}
private void resetAllStatsLocked() {
@@ -5705,7 +5750,7 @@ public final class BatteryStatsImpl extends BatteryStats {
clearHistoryLocked();
}
- private void initActiveHistoryEventsLocked(long nowRealtime) {
+ private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
HashMap<String, SparseBooleanArray> active = mActiveEvents[i];
if (active == null) {
@@ -5715,7 +5760,8 @@ public final class BatteryStatsImpl extends BatteryStats {
SparseBooleanArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
if (uids.valueAt(j)) {
- addHistoryEventLocked(nowRealtime, i, ent.getKey(), uids.keyAt(j));
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
+ uids.keyAt(j));
}
}
}
@@ -5753,16 +5799,16 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- void setOnBatteryLocked(boolean onBattery, int oldStatus, int level) {
+ void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
+ final int oldStatus, final int level) {
boolean doWrite = false;
Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
m.arg1 = onBattery ? 1 : 0;
mHandler.sendMessage(m);
mOnBattery = mOnBatteryInternal = onBattery;
- long uptime = SystemClock.uptimeMillis() * 1000;
- long mSecRealtime = SystemClock.elapsedRealtime();
- long realtime = mSecRealtime * 1000;
+ final long uptime = mSecUptime * 1000;
+ final long realtime = mSecRealtime * 1000;
if (onBattery) {
// We will reset our status if we are unplugging after the
// battery was last full, or the level is at 100, or
@@ -5783,9 +5829,9 @@ public final class BatteryStatsImpl extends BatteryStats {
if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
+ Integer.toHexString(mHistoryCur.states));
mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(mSecRealtime, HistoryItem.CMD_CURRENT_TIME);
+ addHistoryBufferLocked(mSecRealtime, mSecUptime, HistoryItem.CMD_CURRENT_TIME);
mHistoryCur.currentTime = 0;
- addHistoryRecordLocked(mSecRealtime);
+ addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = level;
if (mScreenOn) {
mDischargeScreenOnUnplugLevel = level;
@@ -5798,7 +5844,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mDischargeAmountScreenOff = 0;
updateTimeBasesLocked(true, !mScreenOn, uptime, realtime);
if (reset) {
- initActiveHistoryEventsLocked(mSecRealtime);
+ initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
}
} else {
pullPendingStateUpdatesLocked();
@@ -5806,7 +5852,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(mSecRealtime);
+ addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = level;
if (level < mDischargeUnplugLevel) {
mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
@@ -5828,7 +5874,9 @@ public final class BatteryStatsImpl extends BatteryStats {
public void setBatteryState(int status, int health, int plugType, int level,
int temp, int volt) {
synchronized(this) {
- boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+ final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+ final long uptime = SystemClock.uptimeMillis();
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
int oldStatus = mHistoryCur.batteryStatus;
if (!mHaveBatteryLevel) {
mHaveBatteryLevel = true;
@@ -5856,7 +5904,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mHistoryCur.batteryPlugType = (byte)plugType;
mHistoryCur.batteryTemperature = (short)temp;
mHistoryCur.batteryVoltage = (char)volt;
- setOnBatteryLocked(onBattery, oldStatus, level);
+ setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
} else {
boolean changed = false;
if (mHistoryCur.batteryLevel != level) {
@@ -5886,7 +5934,7 @@ public final class BatteryStatsImpl extends BatteryStats {
changed = true;
}
if (changed) {
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
}
if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
@@ -6161,7 +6209,18 @@ public final class BatteryStatsImpl extends BatteryStats {
return val;
}
}
-
+
+ @Override
+ public int getDischargeAmount(int which) {
+ int dischargeAmount = which == STATS_SINCE_CHARGED
+ ? getHighDischargeAmountSinceCharge()
+ : (getDischargeStartLevel() - getDischargeCurrentLevel());
+ if (dischargeAmount < 0) {
+ dischargeAmount = 0;
+ }
+ return dischargeAmount;
+ }
+
public int getDischargeAmountScreenOn() {
synchronized(this) {
int val = mDischargeAmountScreenOn;
@@ -6438,13 +6497,14 @@ public final class BatteryStatsImpl extends BatteryStats {
}
if (mHistoryBuffer.dataPosition() > 0) {
- long now = SystemClock.elapsedRealtime();
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (USE_OLD_HISTORY) {
- addHistoryRecordLocked(now, HistoryItem.CMD_START);
+ addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START);
}
- addHistoryBufferLocked(now, HistoryItem.CMD_START);
+ addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START);
mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(now, HistoryItem.CMD_CURRENT_TIME);
+ addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_CURRENT_TIME);
mHistoryCur.currentTime = 0;
}
}
@@ -6540,11 +6600,11 @@ public final class BatteryStatsImpl extends BatteryStats {
StringBuilder sb = new StringBuilder(128);
sb.append("****************** WRITING mHistoryBaseTime: ");
TimeUtils.formatDuration(mHistoryBaseTime, sb);
- sb.append(" mLastHistoryTime: ");
- TimeUtils.formatDuration(mLastHistoryTime, sb);
+ sb.append(" mLastHistoryElapsedRealtime: ");
+ TimeUtils.formatDuration(mLastHistoryElapsedRealtime, sb);
Slog.i(TAG, sb.toString());
}
- out.writeLong(mHistoryBaseTime + mLastHistoryTime);
+ out.writeLong(mHistoryBaseTime + mLastHistoryElapsedRealtime);
out.writeInt(mHistoryTagPool.size());
for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
HistoryTag tag = ent.getKey();
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index c957b67..adb9bf8 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -286,7 +286,7 @@ public class ActionBarOverlayLayout extends ViewGroup {
topInset += mActionBarHeight;
}
}
- } else if (mActionBarTop.getVisibility() == VISIBLE) {
+ } else if (mActionBarTop.getVisibility() != GONE) {
// This is the space needed on top of the window for all of the action bar
// and tabs.
topInset = mActionBarTop.getMeasuredHeight();
diff --git a/core/java/com/android/server/AppWidgetBackupBridge.java b/core/java/com/android/server/AppWidgetBackupBridge.java
new file mode 100644
index 0000000..2ea2f79
--- /dev/null
+++ b/core/java/com/android/server/AppWidgetBackupBridge.java
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+import java.util.List;
+
+/**
+ * Runtime bridge between the Backup Manager Service and the App Widget Service,
+ * since those two modules are intentionally decoupled for modularity.
+ *
+ * @hide
+ */
+public class AppWidgetBackupBridge {
+ private static WidgetBackupProvider sAppWidgetService;
+
+ public static void register(WidgetBackupProvider instance) {
+ sAppWidgetService = instance;
+ }
+
+ public static List<String> getWidgetParticipants(int userId) {
+ return (sAppWidgetService != null)
+ ? sAppWidgetService.getWidgetParticipants(userId)
+ : null;
+ }
+
+ public static byte[] getWidgetState(String packageName, int userId) {
+ return (sAppWidgetService != null)
+ ? sAppWidgetService.getWidgetState(packageName, userId)
+ : null;
+ }
+
+ public static void restoreStarting(int userId) {
+ if (sAppWidgetService != null) {
+ sAppWidgetService.restoreStarting(userId);
+ }
+ }
+
+ public static void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
+ if (sAppWidgetService != null) {
+ sAppWidgetService.restoreWidgetState(packageName, restoredState, userId);
+ }
+ }
+
+ public static void restoreFinished(int userId) {
+ if (sAppWidgetService != null) {
+ sAppWidgetService.restoreFinished(userId);
+ }
+ }
+}
diff --git a/core/java/com/android/server/WidgetBackupProvider.java b/core/java/com/android/server/WidgetBackupProvider.java
new file mode 100644
index 0000000..a2efbdd
--- /dev/null
+++ b/core/java/com/android/server/WidgetBackupProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import java.util.List;
+
+/**
+ * Shim to allow core/backup to communicate with the app widget service
+ * about various important events without needing to be able to see the
+ * implementation of the service.
+ *
+ * @hide
+ */
+public interface WidgetBackupProvider {
+ public List<String> getWidgetParticipants(int userId);
+ public byte[] getWidgetState(String packageName, int userId);
+ public void restoreStarting(int userId);
+ public void restoreWidgetState(String packageName, byte[] restoredState, int userId);
+ public void restoreFinished(int userId);
+}
diff --git a/core/jni/android_content_res_Configuration.cpp b/core/jni/android_content_res_Configuration.cpp
index 246e3bd..201ffe8 100644
--- a/core/jni/android_content_res_Configuration.cpp
+++ b/core/jni/android_content_res_Configuration.cpp
@@ -70,15 +70,6 @@ void android_Configuration_getFromJava(
gConfigurationClassInfo.smallestScreenWidthDp);
}
-/*
- * JNI registration.
- */
-static JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- //{ "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z",
- // (void*) android_content_res_ObbScanner_getObbInfo },
-};
-
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class " className);
@@ -123,8 +114,7 @@ int register_android_content_res_Configuration(JNIEnv* env)
GET_FIELD_ID(gConfigurationClassInfo.smallestScreenWidthDp, clazz,
"smallestScreenWidthDp", "I");
- return AndroidRuntime::registerNativeMethods(env, "android/content/res/Configuration", gMethods,
- NELEM(gMethods));
+ return 0;
}
}; // namespace android
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index aa6a035..e72aff9 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -42,13 +42,13 @@
#include <SkTemplates.h>
#include <SkXfermode.h>
-#include <DisplayList.h>
#include <DisplayListRenderer.h>
#include <LayerRenderer.h>
#include <OpenGLRenderer.h>
#include <SkiaShader.h>
#include <Stencil.h>
#include <Rect.h>
+#include <RenderNode.h>
#include <TextLayout.h>
#include <TextLayoutCache.h>
diff --git a/core/jni/android_view_GLRenderer.cpp b/core/jni/android_view_GLRenderer.cpp
index 228a92e..00d92fc 100644
--- a/core/jni/android_view_GLRenderer.cpp
+++ b/core/jni/android_view_GLRenderer.cpp
@@ -27,9 +27,9 @@
#include <utils/Timers.h>
#include <Caches.h>
-#include <DisplayList.h>
#include <Extensions.h>
#include <LayerRenderer.h>
+#include <RenderNode.h>
#ifdef USE_OPENGL_RENDERER
EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index ad2e9ff..4bf5f78 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -32,10 +32,10 @@
#include <SkXfermode.h>
#include <DeferredLayerUpdater.h>
-#include <DisplayList.h>
#include <LayerRenderer.h>
#include <SkiaShader.h>
#include <Rect.h>
+#include <RenderNode.h>
namespace android {
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 823f8d7..fa08a78 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -23,8 +23,8 @@
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
-#include <DisplayList.h>
#include <DisplayListRenderer.h>
+#include <RenderNode.h>
namespace android {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c2159fb..4e1efc6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -83,6 +83,8 @@
<protected-broadcast android:name="android.appwidget.action.APPWIDGET_DELETED" />
<protected-broadcast android:name="android.appwidget.action.APPWIDGET_DISABLED" />
<protected-broadcast android:name="android.appwidget.action.APPWIDGET_ENABLED" />
+ <protected-broadcast android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED" />
+ <protected-broadcast android:name="android.appwidget.action.APPWIDGET_RESTORED" />
<protected-broadcast android:name="android.backup.intent.RUN" />
<protected-broadcast android:name="android.backup.intent.CLEAR" />
@@ -1913,24 +1915,12 @@
android:description="@string/permdesc_filter_events"
android:protectionLevel="signature" />
- <!-- @hide Allows an application to retrieve info for a window from the window manager. -->
- <permission android:name="android.permission.RETRIEVE_WINDOW_INFO"
- android:label="@string/permlab_retrieve_window_info"
- android:description="@string/permdesc_retrieve_window_info"
- android:protectionLevel="signature" />
-
<!-- @hide Allows an application to temporary enable accessibility on the device. -->
<permission android:name="android.permission.TEMPORARY_ENABLE_ACCESSIBILITY"
android:label="@string/permlab_temporary_enable_accessibility"
android:description="@string/permdesc_temporary_enable_accessibility"
android:protectionLevel="signature" />
- <!-- @hide Allows an application to magnify the content of a display. -->
- <permission android:name="android.permission.MAGNIFY_DISPLAY"
- android:label="@string/permlab_magnify_display"
- android:description="@string/permdesc_magnify_display"
- android:protectionLevel="signature" />
-
<!-- Allows an application to watch and control how activities are
started globally in the system. Only for is in debugging
(usually the monkey command).
@@ -2400,6 +2390,12 @@
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signature" />
+ <!-- Internal permission to allows an application to read indexable data.
+ @hide -->
+ <permission android:name="android.permission.READ_SEARCH_INDEXABLES"
+ android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:protectionLevel="signature|system" />
+
<!-- Allows applications to set a live wallpaper.
@hide XXX Change to signature once the picker is moved to its
own apk as Ghod Intended. -->
diff --git a/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.png
index b47d666..e215b96 100644
--- a/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_light.png
index 03b0d2a..a014e91 100644
--- a/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.png
index 13d803c..bb8bec1 100644
--- a/core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_off_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_off_holo_light.png
index 3ae436b..aa1737e 100644
--- a/core/res/res/drawable-hdpi/ic_media_route_off_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_media_route_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.png
index 24824fc..2c1434b 100644
--- a/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.png
index af3819b..dbdce3e 100644
--- a/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.png
index 83dc251..1101864 100644
--- a/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.png
index 8d9d592..e8e9069 100644
--- a/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.png
index 1310ec9..8595158 100644
--- a/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.png
index 1705074..14844d4 100644
--- a/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.png
index 7027b88..1565a29 100644
--- a/core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_on_holo_light.png
index 7027b88..9b8fe87 100644
--- a/core/res/res/drawable-hdpi/ic_media_route_on_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_media_route_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_0.png b/core/res/res/drawable-hdpi/ic_notification_cast_0.png
index a35f281..74f7dc0 100644
--- a/core/res/res/drawable-hdpi/ic_notification_cast_0.png
+++ b/core/res/res/drawable-hdpi/ic_notification_cast_0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_1.png b/core/res/res/drawable-hdpi/ic_notification_cast_1.png
index 9f6e2ad..c6d267d 100644
--- a/core/res/res/drawable-hdpi/ic_notification_cast_1.png
+++ b/core/res/res/drawable-hdpi/ic_notification_cast_1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_2.png b/core/res/res/drawable-hdpi/ic_notification_cast_2.png
index 737137a..699b299 100644
--- a/core/res/res/drawable-hdpi/ic_notification_cast_2.png
+++ b/core/res/res/drawable-hdpi/ic_notification_cast_2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_on.png b/core/res/res/drawable-hdpi/ic_notification_cast_on.png
index ff2753a..3eaf13a 100644
--- a/core/res/res/drawable-hdpi/ic_notification_cast_on.png
+++ b/core/res/res/drawable-hdpi/ic_notification_cast_on.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_dark.png
index fa22d82..52e3a5a 100644
--- a/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_light.png
index a686cd1..319c57e 100644
--- a/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.png
index 6764598..f98c0a8 100644
--- a/core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_off_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_off_holo_light.png
index 94e0bb6..b74cdb5 100644
--- a/core/res/res/drawable-mdpi/ic_media_route_off_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_media_route_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.png
index 5ce2f20..a6a4bd0 100644
--- a/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.png
index 5105e90..106fd3a 100644
--- a/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.png
index 68c06ed..2c141ab 100644
--- a/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.png
index 6e9b144..0b62d0b 100644
--- a/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.png
index 45dc56f3d..23442b0 100644
--- a/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.png
index 46e743a..42b329f 100644
--- a/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.png
index e384691..58ff506 100644
--- a/core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_on_holo_light.png
index e384691..25257f8 100644
--- a/core/res/res/drawable-mdpi/ic_media_route_on_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_media_route_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_0.png b/core/res/res/drawable-mdpi/ic_notification_cast_0.png
index d9cedbd..a51a3cb 100644
--- a/core/res/res/drawable-mdpi/ic_notification_cast_0.png
+++ b/core/res/res/drawable-mdpi/ic_notification_cast_0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_1.png b/core/res/res/drawable-mdpi/ic_notification_cast_1.png
index 414c67f..e081367 100644
--- a/core/res/res/drawable-mdpi/ic_notification_cast_1.png
+++ b/core/res/res/drawable-mdpi/ic_notification_cast_1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_2.png b/core/res/res/drawable-mdpi/ic_notification_cast_2.png
index 280a888..a7f4de4 100644
--- a/core/res/res/drawable-mdpi/ic_notification_cast_2.png
+++ b/core/res/res/drawable-mdpi/ic_notification_cast_2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_on.png b/core/res/res/drawable-mdpi/ic_notification_cast_on.png
index ab5f1d7..42de8c4 100644
--- a/core/res/res/drawable-mdpi/ic_notification_cast_on.png
+++ b/core/res/res/drawable-mdpi/ic_notification_cast_on.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png
index 6b31579..7b6d48b 100644
--- a/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png
index df0121b..bafe878 100644
--- a/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.png
index 1d48e12..4119cff 100644
--- a/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.png
+++ b/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.png
index 2c8d1ec..b629a57 100644
--- a/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.png
+++ b/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.png
index 00b2043..fe81128 100644
--- a/core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.png
+++ b/core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.png
index ce1d939..9b59eaf 100644
--- a/core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.png
+++ b/core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.png
index 3064b46..1a513c1 100644
--- a/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.png
+++ b/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.png
index 4316686..ff78803 100644
--- a/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.png
+++ b/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.png
index 25c4e31..4c4b624 100644
--- a/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.png
+++ b/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.png
index 8e32bd2..60f8c4d 100644
--- a/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.png
+++ b/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.png
index aeaa78f..cdb2f30 100644
--- a/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.png
+++ b/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.png
index 85277fa..97a10a3 100644
--- a/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.png
+++ b/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.png
index b01dbe8..a19a083 100644
--- a/core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.png
+++ b/core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.png
index c19a2ad..db30613 100644
--- a/core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.png
+++ b/core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_0.png b/core/res/res/drawable-xhdpi/ic_notification_cast_0.png
index 5fb23a0..818c1cd 100644
--- a/core/res/res/drawable-xhdpi/ic_notification_cast_0.png
+++ b/core/res/res/drawable-xhdpi/ic_notification_cast_0.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_1.png b/core/res/res/drawable-xhdpi/ic_notification_cast_1.png
index f01d17d..2a56e31 100644
--- a/core/res/res/drawable-xhdpi/ic_notification_cast_1.png
+++ b/core/res/res/drawable-xhdpi/ic_notification_cast_1.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_2.png b/core/res/res/drawable-xhdpi/ic_notification_cast_2.png
index 4f4ba7f..3515a76 100644
--- a/core/res/res/drawable-xhdpi/ic_notification_cast_2.png
+++ b/core/res/res/drawable-xhdpi/ic_notification_cast_2.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_on.png b/core/res/res/drawable-xhdpi/ic_notification_cast_on.png
index 38f15dd..142065b 100644
--- a/core/res/res/drawable-xhdpi/ic_notification_cast_on.png
+++ b/core/res/res/drawable-xhdpi/ic_notification_cast_on.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png
index 418f322..cbb4f4c 100644
--- a/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png
+++ b/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png
index a5a59d4..6d467f7 100644
--- a/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png
+++ b/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.png
index 7b0c383..6fad4a64 100644
--- a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.png
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.png
index efb624e..865617c 100644
--- a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.png
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.png
index 5ee57e4..44d98d5 100644
--- a/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.png
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.png
index 6bc2e4a..b5b29b0 100644
--- a/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.png
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.png
index c13af9c..c807b50 100644
--- a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.png
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.png
index 744fb42..3fc7188 100644
--- a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.png
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.png
index ca4d59c..d54f44a 100644
--- a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.png
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.png
index fde5688..092fe8c 100644
--- a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.png
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.png
index b8715c3..17c1d99 100644
--- a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.png
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.png
index 668bb25..4fd5808 100644
--- a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.png
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.png
index 7f54a62..906401e 100644
--- a/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.png
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.png
index 2df924d..d29e563 100644
--- a/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.png
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png
index f5b16ed..7ef0d3d 100644
--- a/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png
+++ b/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png
index 22efeec..ed04beb 100644
--- a/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png
+++ b/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png
index e24cd97..d62d27d 100644
--- a/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png
+++ b/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png
index da1a627..d562602 100644
--- a/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png
+++ b/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png
Binary files differ
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 682e0ea..0330b7b 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -690,7 +690,7 @@
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"conèixer les observacions sobre les condicions de la xarxa"</string>
<string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Permet que una aplicació conegui les observacions sobre les condicions de la xarxa. No s\'ha de necessitar mai per a aplicacions normals."</string>
<string name="permlab_setInputCalibration" msgid="4902620118878467615">"canviar el calibratge del dispositiu d\'entrada"</string>
- <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permet que l\'aplicació modifiqui els paràmetres de calibratge de la pantalla tàctil. S\'ha de procurar no fer servir mai aquesta opció per a les aplicacions normals."</string>
+ <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permet que l\'aplicació modifiqui els paràmetres de calibratge de la pantalla tàctil. No ha de ser mai necessari per a aplicacions normals."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Definir les normes de contrasenya"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Controla la longitud i els caràcters permesos a les contrasenyes de desbloqueig de pantalla."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Controlar intents de desbloqueig de pantalla"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index ffe2501..e144e9d 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1435,8 +1435,8 @@
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
<string name="activitychooserview_choose_application" msgid="2125168057199941199">"Επιλέξτε κάποια εφαρμογή"</string>
<string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"Δεν ήταν δυνατή η εκκίνηση του <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
- <string name="shareactionprovider_share_with" msgid="806688056141131819">"Κοινή χρήση με"</string>
- <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Κοινή χρήση με <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+ <string name="shareactionprovider_share_with" msgid="806688056141131819">"Κοινοποίηση με"</string>
+ <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Κοινοποίηση με <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
<string name="content_description_sliding_handle" msgid="415975056159262248">"Στοιχείο χειρισμού με δυνατότητα ολίσθησης. Αγγίξτε και πατήστε παρατεταμένα."</string>
<string name="description_target_unlock_tablet" msgid="3833195335629795055">"Σύρετε για ξεκλείδωμα."</string>
<string name="keyboard_headset_required_to_hear_password" msgid="7011927352267668657">"Συνδέστε ακουστικά για να ακούσετε τα πλήκτρα του κωδικού πρόσβασης να εκφωνούνται."</string>
@@ -1480,7 +1480,7 @@
<string name="sha1_fingerprint" msgid="7930330235269404581">"Αποτύπωμα SHA-1"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Εμφάνιση όλων"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Επιλογή δραστηριότητας"</string>
- <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινή χρήση με"</string>
+ <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινοποίηση με"</string>
<string name="list_delimeter" msgid="3975117572185494152">", "</string>
<string name="sending" msgid="3245653681008218030">"Γίνεται αποστολή…"</string>
<string name="launchBrowserDefault" msgid="2057951947297614725">"Εκκίνηση προγράμματος περιήγησης;"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index ec4ed33..2c9599c 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -689,7 +689,7 @@
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Permite al propietario ejecutar la aplicación de configuración proporcionada por el proveedor. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"Detectar cambios en el estado de la red"</string>
<string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Permite que una aplicación detecte cambios en el estado de la red. Las aplicaciones normales no deberían necesitar este permiso."</string>
- <string name="permlab_setInputCalibration" msgid="4902620118878467615">"Cambiar la calibración del dispositivo de entrada"</string>
+ <string name="permlab_setInputCalibration" msgid="4902620118878467615">"cambiar la calibración del dispositivo de entrada"</string>
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permite que la aplicación modifique los parámetros de calibración de la pantalla táctil. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Establecer reglas de contraseña"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar la longitud y los caracteres permitidos en las contraseñas para desbloquear la pantalla"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 857a1ea..308ea59 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -690,7 +690,7 @@
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"گوش دادن برای بررسی شرایط شبکه"</string>
<string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"به برنامه امکان می‌دهد برای بررسی شرایط شبکه گوش دهد. این امکان هرگز نباید برای برنامه‌های معمولی مورد نیاز باشد."</string>
<string name="permlab_setInputCalibration" msgid="4902620118878467615">"تغییر کالیبراسیون دستگاه ورودی"</string>
- <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"به برنامه امکان می‌دهد پارامترهای صفحه لمسی را کالیبره کند. هرگز نباید برای برنامه‌های عادی مورد نیاز باشد."</string>
+ <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"به برنامه امکان می‌دهد پارامترهای کالیبراسیون صفحه لمسی را تغییر دهد. هرگز نباید برای برنامه‌های عادی مورد نیاز باشد."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین رمز ورود"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"‏طول و نویسه‎های مجاز در گذرواژه‌های بازکردن قفل صفحه را کنترل کنید."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"نمایش تلاش‌های قفل گشایی صفحه"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 1d6b16d..b40117b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -690,7 +690,7 @@
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"hálózati körülményekkel kapcsolatos észrevételek figyelemmel kísérése"</string>
<string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Lehetővé teszi egy alkalmazás számára, hogy figyelemmel kísérje a hálózati körülményekkel kapcsolatos észrevételeket. A normál alkalmazásoknak erre soha nincs szükségük."</string>
<string name="permlab_setInputCalibration" msgid="4902620118878467615">"beviteli eszköz kalibrációjának módosítása"</string>
- <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Lehetővé teszi, hogy alkalmazás módosítsa az érintőképernyő kalibrációs paramétereit. A normál alkalmazásoknál erre elvileg soha nincs szükség."</string>
+ <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Lehetővé teszi, hogy az alkalmazás módosítsa az érintőképernyő kalibrációs paramétereit. A normál alkalmazásoknál erre elvileg soha nincs szükség."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Jelszavakkal kapcsolatos szabályok beállítása"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"A képernyőzár-feloldási jelszavakban engedélyezett karakterek és hosszúság vezérlése."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Képernyőzár-feloldási kísérletek figyelése"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index c8f0dd5..f330aaa 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1111,7 +1111,7 @@
<string name="cut" msgid="3092569408438626261">"חתוך"</string>
<string name="copy" msgid="2681946229533511987">"העתק"</string>
<string name="paste" msgid="5629880836805036433">"הדבק"</string>
- <string name="replace" msgid="5781686059063148930">"להחליף..."</string>
+ <string name="replace" msgid="5781686059063148930">"החלף..."</string>
<string name="delete" msgid="6098684844021697789">"מחק"</string>
<string name="copyUrl" msgid="2538211579596067402">"העתק כתובת אתר"</string>
<string name="selectTextMode" msgid="1018691815143165326">"בחר טקסט"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 22fc1c4..4284032 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -690,7 +690,7 @@
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"vykdyti tinklo sąlygų stebėjimą"</string>
<string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Leidžiama programai vykdyti tinklo sąlygų stebėjimą. To niekada neturėtų prireikti naudojant įprastas programas."</string>
<string name="permlab_setInputCalibration" msgid="4902620118878467615">"keisti įvesties įrenginio kalibravimą"</string>
- <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Leidžiama programai keisti jutiklinio ekrano kalibravimo parametrus. Neturėti prireikti naudojant įprastas programas."</string>
+ <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Leidžiama programai keisti jutiklinio ekrano kalibravimo parametrus. Neturėtų prireikti naudojant įprastas programas."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Nustatyti slaptažodžio taisykles"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Valdyti leidžiamą ekrano atrakinimo slaptažodžių ilgį ir leidžiamus naudoti simbolius."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Stebėti bandymus atrakinti ekraną"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index f8e9782..4df9785 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -38,7 +38,7 @@
<string name="mmiFdnError" msgid="5224398216385316471">"Operácia je obmedzená len na režim čísla pevného vytáčania."</string>
<string name="serviceEnabled" msgid="8147278346414714315">"Služba bola povolená."</string>
<string name="serviceEnabledFor" msgid="6856228140453471041">"Služba bola povolená pre:"</string>
- <string name="serviceDisabled" msgid="1937553226592516411">"Služba bola zakázaná."</string>
+ <string name="serviceDisabled" msgid="1937553226592516411">"Služba bola vypnutá."</string>
<string name="serviceRegistered" msgid="6275019082598102493">"Registrácia prebehla úspešne."</string>
<string name="serviceErased" msgid="1288584695297200972">"Vymazanie prebehlo úspešne."</string>
<string name="passwordIncorrect" msgid="7612208839450128715">"Nesprávne heslo."</string>
@@ -528,14 +528,14 @@
<string name="permdesc_mount_format_filesystems" msgid="8784268246779198627">"Umožňuje aplikácii formátovať vymeniteľný ukladací priestor."</string>
<string name="permlab_asec_access" msgid="3411338632002193846">"získať informácie o internom ukladacom priestore"</string>
<string name="permdesc_asec_access" msgid="3094563844593878548">"Umožňuje aplikácii získať informácie o internom ukladacom priestore."</string>
- <string name="permlab_asec_create" msgid="6414757234789336327">"vytvoriť interný ukladací priestor"</string>
- <string name="permdesc_asec_create" msgid="4558869273585856876">"Umožňuje aplikácii vytvoriť interný ukladací priestor."</string>
- <string name="permlab_asec_destroy" msgid="526928328301618022">"zničiť interný ukladací priestor"</string>
- <string name="permdesc_asec_destroy" msgid="7218749286145526537">"Umožňuje aplikácii zničiť interný ukladací priestor."</string>
- <string name="permlab_asec_mount_unmount" msgid="8877998101944999386">"pripojiť alebo odpojiť interný ukladací priestor"</string>
- <string name="permdesc_asec_mount_unmount" msgid="3451360114902490929">"Umožňuje aplikácii pripojiť alebo odpojiť interný ukladací priestor."</string>
- <string name="permlab_asec_rename" msgid="7496633954080472417">"premenovať interný ukladací priestor"</string>
- <string name="permdesc_asec_rename" msgid="1794757588472127675">"Umožňuje aplikácii premenovať interný ukladací priestor."</string>
+ <string name="permlab_asec_create" msgid="6414757234789336327">"vytvoriť interné úložisko"</string>
+ <string name="permdesc_asec_create" msgid="4558869273585856876">"Umožňuje aplikácii vytvoriť interné úložisko."</string>
+ <string name="permlab_asec_destroy" msgid="526928328301618022">"zničiť interné úložisko"</string>
+ <string name="permdesc_asec_destroy" msgid="7218749286145526537">"Umožňuje aplikácii zničiť interné úložisko."</string>
+ <string name="permlab_asec_mount_unmount" msgid="8877998101944999386">"pripojiť alebo odpojiť interné úložisko"</string>
+ <string name="permdesc_asec_mount_unmount" msgid="3451360114902490929">"Umožňuje aplikácii pripojiť alebo odpojiť interné úložisko."</string>
+ <string name="permlab_asec_rename" msgid="7496633954080472417">"premenovať interné úložisko"</string>
+ <string name="permdesc_asec_rename" msgid="1794757588472127675">"Umožňuje aplikácii premenovať interné úložisko."</string>
<string name="permlab_vibrate" msgid="7696427026057705834">"ovládať vibrovanie"</string>
<string name="permdesc_vibrate" msgid="6284989245902300945">"Umožňuje aplikácii ovládať vibrácie."</string>
<string name="permlab_flashlight" msgid="2155920810121984215">"ovládanie kontrolky"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index e07bd7d..305036d 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1667,7 +1667,7 @@
<item quantity="other" msgid="4730868920742952817">"ลองอีกใน <xliff:g id="COUNT">%d</xliff:g> วินาที"</item>
</plurals>
<string name="restr_pin_try_later" msgid="973144472490532377">"ลองอีกครั้งในภายหลัง"</string>
- <string name="immersive_mode_confirmation" msgid="7227416894979047467">"กวาดนิ้วจากบนลงล่างเพื่อออกจากโหมดเต็มหน้าจอ"</string>
+ <string name="immersive_mode_confirmation" msgid="7227416894979047467">"กวาดนิ้วบนลงล่างเพื่อออกจากโหมดเต็มหน้าจอ"</string>
<string name="done_label" msgid="2093726099505892398">"เสร็จสิ้น"</string>
<string name="hour_picker_description" msgid="6698199186859736512">"ตัวเลื่อนหมุนระบุชั่วโมง"</string>
<string name="minute_picker_description" msgid="8606010966873791190">"ตัวเลื่อนหมุนระบุนาที"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8482fdb..582ed1b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2641,6 +2641,8 @@
<flag name="flagReportViewIds" value="0x00000010" />
<!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_FILTER_KEY_EVENTS} -->
<flag name="flagRequestFilterKeyEvents" value="0x00000020" />
+ <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} -->
+ <flag name="flagRetrieveInteractiveWindows" value="0x00000040" />
</attr>
<!-- Component name of an activity that allows the user to modify
the settings for this service. This setting cannot be changed at runtime. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f1bcf65..0699e8b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -830,13 +830,6 @@
user consent.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_retrieve_window_info">retrieve window info</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_retrieve_window_info">Allows an application to retrieve
- information about the the windows from the window manager. Malicious apps may
- retrieve information that is intended for internal system usage.</string>
-
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_filter_events">filter events</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_filter_events">Allows an application to register an input filter
@@ -844,13 +837,6 @@
may control the system UI whtout user intervention.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_magnify_display">magnify display</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_magnify_display">Allows an application to magnify the content of a
- display. Malicious apps may transform the display content in a way that renders the
- device unusable.</string>
-
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_shutdown">partial shutdown</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_shutdown">Puts the activity manager into a shutdown
diff --git a/docs/downloads/training/BitmapFun.zip b/docs/downloads/training/BitmapFun.zip
deleted file mode 100644
index d3dd02a..0000000
--- a/docs/downloads/training/BitmapFun.zip
+++ /dev/null
Binary files differ
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index 5f0779c..fc60e1f 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -313,6 +313,9 @@ redirects:
# -------------------- MISC ----------------------
+- from: /shareables/training/BitmapFun.zip
+ to: /downloads/samples/DisplayingBitmaps.zip
+
- from: /shareables/...
to: http://commondatastorage.googleapis.com/androiddevelopers/shareables/...
diff --git a/docs/html/guide/topics/graphics/hardware-accel.jd b/docs/html/guide/topics/graphics/hardware-accel.jd
index 54ef20c..7c957b8 100644
--- a/docs/html/guide/topics/graphics/hardware-accel.jd
+++ b/docs/html/guide/topics/graphics/hardware-accel.jd
@@ -263,256 +263,153 @@ changed.</li>
<p>The following table describes the support level of various operations across API levels:</p>
<style type="text/css">
- .tblGenFixed, .tblGeneric{font-size:15px}.tblGenFixed td {padding:0 3px;letter-spacing:0;word-spacing:0;background-color:#fff;z-index:1;border-top:0px none;border-left:0px none;border-bottom:1px solid #CCC;border-right:1px solid #CCC;} .dn {display:none} .tblGenFixed td.s0 {background-color:white;border-top:1px solid #CCC;border-left:1px solid #CCC;} .tblGenFixed td.s1 {background-color:#434343;color:#ffffff;text-align:center;border-top:1px solid #CCC;} .tblGenFixed td.s2 {background-color:#d9d9d9;color:#000000;text-align:center;} .tblGenFixed td.s3 {background-color:white;color:#000000;text-align:center;} .tblGenFixed td.s5 {background-color:#434343;color:#ffffff;text-align:left;border-left:1px solid #CCC;} .tblGenFixed td.s10 {background-color:white;font-family:courier new,monospace;color:#000000;text-align:right;border-left:1px solid #CCC;} .tblGenFixed td.g_pos {background-color:#d9d9d9;color:#6aa84f;text-align:center;} .tblGenFixed td.g_neg {background-color:#d9d9d9;color:#980000;text-align:center;} .tblGenFixed td.w_pos {background-color:white;color:#6aa84f;text-align:center;} .tblGenFixed td.w_neg {background-color:white;color:#980000;text-align:center;}
+ .tblGenFixed, .tblGeneric{font-size:15px}.tblGenFixed td {padding:0 3px;letter-spacing:0;word-spacing:0;background-color:#fff;z-index:1;border-top:0px none;border-left:0px none;border-bottom:1px solid #CCC;border-right:1px solid #CCC;} .dn {display:none} .tblGenFixed td.s0 {background-color:white;border-top:1px solid #CCC;border-left:1px solid #CCC;} .tblGenFixed td.s1 {background-color:#434343;color:#ffffff;text-align:center;border-top:1px solid #CCC;} .tblGenFixed td.s3 {background-color:white;color:#000000;text-align:center;} .tblGenFixed td.s5 {background-color:#434343;color:#ffffff;text-align:left;border-left:1px solid #CCC;} .tblGenFixed td.label_pos {background-color:white;font-family:courier new,monospace;color:#000000;text-align:right;border-left:1px solid #CCC;} .tblGenFixed td.label_neg {background-color:#ececec;font-family:courier new,monospace;color:#000000;text-align:right;border-left:1px solid #CCC;} .tblGenFixed td.value_pos {background-color:white;color:#000000;text-align:center;} .tblGenFixed td.value_neg {background-color:#ececec;color:#980000;text-align:center;}
</style>
<table border="0" cellpadding="0" cellspacing="0" class="tblGenFixed" id="tblMain">
<tbody>
<tr class="rShim">
<td class="rShim" style="width:380px;"></td>
- <td class="rShim" style="width:120px;"></td>
- <td class="rShim" style="width:120px;"></td>
- <td class="rShim" style="width:120px;"></td>
- <td class="rShim" style="width:120px;"></td>
+ <td class="rShim" style="width:240px;"></td>
</tr>
<tr>
- <td rowspan="2" class="s0"></td>
- <td colspan="4" class="s1">API level</td>
- </tr>
- <tr>
- <td style="display:none;"></td>
- <td class="s2">&lt; 16</td>
- <td class="s3">16</td>
- <td class="s2">17</td>
- <td class="s3">18</td>
+ <td class="s0"></td>
+ <td class="s1">First supported API level</td>
</tr>
<tr>
<td colspan="5" class="s5">Canvas</td>
</tr>
<tr>
- <td class="s10">drawBitmapMesh() (colors array)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">drawBitmapMesh() (colors array)</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">drawPicture()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">drawPicture()</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">drawPosText()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
- <td class="g_pos">&#10003;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">drawPosText()</td>
+ <td class="value_pos">16</td>
</tr>
<tr>
- <td class="s10">drawTextOnPath()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
- <td class="g_pos">&#10003;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">drawTextOnPath()</td>
+ <td class="value_pos">16</td>
</tr>
<tr>
- <td class="s10">drawVertices()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">drawVertices()</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">setDrawFilter()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
- <td class="g_pos">&#10003;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">setDrawFilter()</td>
+ <td class="value_pos">16</td>
</tr>
<tr>
- <td class="s10">clipPath()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">clipPath()</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">clipRegion()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">clipRegion()</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">clipRect(Region.Op.XOR)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">clipRect(Region.Op.XOR)</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">clipRect(Region.Op.Difference)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">clipRect(Region.Op.Difference)</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">clipRect(Region.Op.ReverseDifference)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">clipRect(Region.Op.ReverseDifference)</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">clipRect() with rotation/perspective</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">clipRect() with rotation/perspective</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
<td colspan="5" class="s5">Paint</td>
</tr>
<tr>
- <td class="s10">setAntiAlias() (for text)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">setAntiAlias() (for text)</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">setAntiAlias() (for lines)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
- <td class="g_pos">&#10003;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">setAntiAlias() (for lines)</td>
+ <td class="value_pos">16</td>
</tr>
<tr>
- <td class="s10">setFilterBitmap()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_pos">&#10003;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">setFilterBitmap()</td>
+ <td class="value_pos">17</td>
</tr>
<tr>
- <td class="s10">setLinearText()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">setLinearText()</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">setMaskFilter()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">setMaskFilter()</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">setPathEffect() (for lines)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">setPathEffect() (for lines)</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">setRasterizer()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">setRasterizer()</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">setShadowLayer() (other than text)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">setShadowLayer() (other than text)</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">setStrokeCap() (for lines)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">setStrokeCap() (for lines)</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">setStrokeCap() (for points)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_pos">setStrokeCap() (for points)</td>
+ <td class="value_pos">19</td>
</tr>
<tr>
- <td class="s10">setSubpixelText()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">setSubpixelText()</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
<td colspan="5" class="s5">Xfermode</td>
</tr>
<tr>
- <td class="s10">AvoidXfermode</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">AvoidXfermode</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">PixelXorXfermode</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">PixelXorXfermode</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">PorterDuff.Mode.DARKEN (framebuffer)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">PorterDuff.Mode.DARKEN (framebuffer)</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">PorterDuff.Mode.LIGHTEN (framebuffer)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">PorterDuff.Mode.LIGHTEN (framebuffer)</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">PorterDuff.Mode.OVERLAY (framebuffer)</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">PorterDuff.Mode.OVERLAY (framebuffer)</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
<td colspan="5" class="s5">Shader</td>
</tr>
<tr>
- <td class="s10">ComposeShader inside ComposeShader</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">ComposeShader inside ComposeShader</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">Same type shaders inside ComposeShader</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
+ <td class="label_neg">Same type shaders inside ComposeShader</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">Local matrix on ComposeShader</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
+ <td class="label_pos">Local matrix on ComposeShader</td>
+ <td class="value_pos">18</td>
</tr>
</tbody>
</table>
@@ -530,64 +427,39 @@ changed.</li>
<tbody>
<tr class="rShim">
<td class="rShim" style="width:380px;"></td>
- <td class="rShim" style="width:120px;"></td>
- <td class="rShim" style="width:120px;"></td>
- <td class="rShim" style="width:120px;"></td>
- </tr>
- <tr>
- <td rowspan="2" class="s0"></td>
- <td colspan="4" class="s1">API level</td>
- </tr>
- <tr>
- <td style="display:none;"></td>
- <td class="s2">&lt; 17</td>
- <td class="s3">17</td>
- <td class="s2">18</td>
+ <td class="rShim" style="width:240px;"></td>
</tr>
<tr>
- <td colspan="5" class="s5">Support for large scale factors</td>
+ <td class="s5">Drawing operation to be scaled</td>
+ <td class="s1">First supported API level</td>
</tr>
<tr>
- <td class="s10">drawText()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_pos">&#10003;</td>
+ <td class="label_pos">drawText()</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">drawPosText()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
+ <td class="label_neg">drawPosText()</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">drawTextOnPath()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
+ <td class="label_neg">drawTextOnPath()</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">Simple Shapes*</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_pos">&#10003;</td>
- <td class="g_pos">&#10003;</td>
+ <td class="label_pos">Simple Shapes*</td>
+ <td class="value_pos">17</td>
</tr>
<tr>
- <td class="s10">Complex Shapes*</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
+ <td class="label_neg">Complex Shapes*</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">drawPath()</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
+ <td class="label_neg">drawPath()</td>
+ <td class="value_neg">&#10007;</td>
</tr>
<tr>
- <td class="s10">Shadow layer</td>
- <td class="g_neg">&#10007;</td>
- <td class="w_neg">&#10007;</td>
- <td class="g_neg">&#10007;</td>
+ <td class="label_neg">Shadow layer</td>
+ <td class="value_neg">&#10007;</td>
</tr>
</tbody>
</table>
diff --git a/docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html b/docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html
index 94b5977..6375d9a 100644
--- a/docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html
+++ b/docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html
@@ -1030,7 +1030,17 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Cancel a previously shown notification. </p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Cancel a previously shown notification.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>id</td>
+ <td>the ID of the notification
+</td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
@@ -1059,7 +1069,21 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Cancel a previously shown notification. </p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Cancel a previously shown notification.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>tag</td>
+ <td>the string identifier of the notification.</td>
+ </tr>
+ <tr>
+ <th>id</td>
+ <td>the ID of the notification
+</td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
@@ -1177,7 +1201,21 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Post a notification to be shown in the status bar, stream, etc. </p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Post a notification to be shown in the status bar, stream, etc.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>id</td>
+ <td>the ID of the notification</td>
+ </tr>
+ <tr>
+ <th>notification</td>
+ <td>the notification to post to the system
+</td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
@@ -1206,7 +1244,25 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Post a notification to be shown in the status bar, stream, etc. </p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Post a notification to be shown in the status bar, stream, etc.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>tag</td>
+ <td>the string identifier for a notification. Can be <code>null</code>.</td>
+ </tr>
+ <tr>
+ <th>id</td>
+ <td>the ID of the notification. The pair (tag, id) must be unique within your app.</td>
+ </tr>
+ <tr>
+ <th>notification</td>
+ <td>the notification to post to the system
+</td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html b/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html
index 307fc2a..6fbf8b6 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html
@@ -886,12 +886,15 @@ From class
<table class="jd-tagtable">
<tr>
<th>returnKey</td>
- <td>the extras key to be set with input collected from the user
- when the intent is sent.
-</td>
+ <td>the intent extras key that refers to the input collected from the user</td>
</tr>
</table>
</div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining
+</li></ul>
+ </div>
</div>
</div>
@@ -966,10 +969,24 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Specifies whether the user can provide arbitrary values. The
- default is <code>true</code>. If this is set to <code>false</code>, a
- non-null non-empty value should be passed to <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html#setChoices(java.lang.String[])">setChoices(String[])</a></code>.
-</p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Specifies whether the user can provide arbitrary values.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>allowFreeFormInput</td>
+ <td>The default is <code>true</code>.
+ If you specify <code>false</code>, you must
+ provide a non-null and non-empty array to <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html#setChoices(java.lang.String[])">setChoices(String[])</a></code> or
+ an <code><a href="http://developer.android.com/reference/java/lang/IllegalArgumentException.html">IllegalArgumentException</a></code> is thrown.</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining
+</li></ul>
+ </div>
</div>
</div>
@@ -998,8 +1015,23 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Specifies choices available to the user to satisfy this input.
-</p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Specifies choices available to the user to satisfy this input.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>choices</td>
+ <td>an array of pre-defined choices for users input.
+ You must provide a non-null and non-empty array if
+ you set <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#allowFreeFormInput">allowFreeFormInput</a></code> to <code>false</code>.</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining
+</li></ul>
+ </div>
</div>
</div>
@@ -1028,8 +1060,21 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Set a label to be displayed to the user when collecting this input.
-</p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Set a label to be displayed to the user when collecting this input.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>label</td>
+ <td>The label to show to users when they input a response.</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining
+</li></ul>
+ </div>
</div>
</div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html b/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html
index e8aa651..0e1cebe 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html
@@ -467,22 +467,27 @@ Summary:
<h2>Class Overview</h2>
-<p itemprop="articleBody">A RemoteInput specifies a response to be collected from the user as part of an intent being
- sent. For example, when used with a notification Action, a response may be collected
- when the user triggers the action, and the results sent as data along with the action's
- PendingIntent. The result value is set in the extras of the triggered Intent with the key
- <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#returnKey">returnKey</a></code>.
-
- <p>Use the builder class <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html">RemoteInput.Builder</a></code> to create this object.
-
- <p>Example which adds a RemoteInput to an Action:
+<p itemprop="articleBody">A <code>RemoteInput</code> object collects a response from users and sets the
+ response as an intent extra inside the <code><a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a></code> that is sent.
+ Always use <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html">RemoteInput.Builder</a></code> to create instances of this class.
+ <p class="note"> See
+ <a href="/wear/notifications/remote-input.html">Receiving Voice Input from
+ a Notification</a> for more information on how to use this class.
+
+ <p>The following example adds a <code>RemoteInput</code> to a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code>,
+ sets the intent extra key as <code>quick_reply</code>, and sets the label as <code>Quick Reply</code>.
+ Users are prompted to input a response when they trigger the action. The results are sent as an
+ intent extra with the key of <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#returnKey">returnKey</a></code> in the action's
+ <code><a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a></code>.
<pre class="prettyprint">
+ public static final String EXTRA_QUICK_REPLY_TEXT = "quick_reply";
WearableNotifications.Action action = new WearableNotifications.Action.Builder(
R.drawable.reply, &quot;Reply&quot;, actionIntent)
- .addRemoteInput(new RemoteInput.Builder(EXTRA_QUICK_REPLY_TEXT)
- .setLabel("Quick reply").build())
+ <b>.addRemoteInput(new RemoteInput.Builder(EXTRA_QUICK_REPLY_TEXT)
+ .setLabel("Quick reply").build()</b>)
.build();</pre>
+
</p>
@@ -619,8 +624,8 @@ android.os.Parcelable
final
boolean</nobr></td>
<td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#allowFreeFormInput">allowFreeFormInput</a></td>
- <td class="jd-descrcol" width="100%">Indicates whether or not the user may provide an arbitrary value for
- this input.</td>
+ <td class="jd-descrcol" width="100%">Indicates whether or not users can provide an arbitrary value for
+ input.</td>
</tr>
@@ -631,7 +636,7 @@ android.os.Parcelable
final
<a href="http://developer.android.com/reference/java/lang/String.html">String[]</a></nobr></td>
<td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#choices">choices</a></td>
- <td class="jd-descrcol" width="100%">The choices available to the user.</td>
+ <td class="jd-descrcol" width="100%">Possible input choices.</td>
</tr>
@@ -642,7 +647,7 @@ android.os.Parcelable
final
<a href="http://developer.android.com/reference/java/lang/String.html">String</a></nobr></td>
<td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#label">label</a></td>
- <td class="jd-descrcol" width="100%">The label to be displayed to the user when collecting this input.</td>
+ <td class="jd-descrcol" width="100%">The label to display to users when collecting this input.</td>
</tr>
@@ -653,8 +658,7 @@ android.os.Parcelable
final
<a href="http://developer.android.com/reference/java/lang/String.html">String</a></nobr></td>
<td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#returnKey">returnKey</a></td>
- <td class="jd-descrcol" width="100%">The extras key to be populated with input from the user when the
- intent is sent.</td>
+ <td class="jd-descrcol" width="100%">The lookup key for the intent extra that the response is set in.</td>
</tr>
@@ -1062,10 +1066,10 @@ From interface
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Indicates whether or not the user may provide an arbitrary value for
- this input. If set to false, then the user should select one of the
- provided choices. It is an error to set this to <code>false</code> and
- not provide <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#choices">choices</a></code>.
+ <div class="jd-tagdata jd-tagdescr"><p>Indicates whether or not users can provide an arbitrary value for
+ input. If you set this to <code>false</code>, users must select one of the
+ provided <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#choices">choices</a></code>. An <code><a href="http://developer.android.com/reference/java/lang/IllegalArgumentException.html">IllegalArgumentException</a></code> is thrown
+ if you set this to false and <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#choices">choices</a></code> is <code>null</code> or empty.
</p></div>
@@ -1094,8 +1098,8 @@ From interface
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>The choices available to the user. May be null if there are no choices
- to present to the user.
+ <div class="jd-tagdata jd-tagdescr"><p>Possible input choices. This can be <code>null</code>
+ if there are no choices to present.
</p></div>
@@ -1124,7 +1128,7 @@ From interface
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>The label to be displayed to the user when collecting this input.
+ <div class="jd-tagdata jd-tagdescr"><p>The label to display to users when collecting this input.
</p></div>
@@ -1153,8 +1157,8 @@ From interface
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>The extras key to be populated with input from the user when the
- intent is sent.
+ <div class="jd-tagdata jd-tagdescr"><p>The lookup key for the intent extra that the response is set in. This is populated
+ when the <code><a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a></code> is sent.
</p></div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html
index 884de4a..f27d406 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html
@@ -448,15 +448,6 @@ Summary:
<h2>Class Overview</h2>
<p itemprop="articleBody">Builder class for <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> objects.
-
- <p>Example:
-
- <pre class="prettyprint">
- WearableNotifications.Builder builder = new WearableNotifications.Builder(mContext)
- .addAction(new WearableNotifications.Action.Builder(
- R.drawable.navigate, &quot;Navigate&quot, pendingIntent)
- .build());
- Notification notif = builder.build();</pre>
</p>
@@ -525,7 +516,7 @@ Summary:
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html#WearableNotifications.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent)">WearableNotifications.Action.Builder</a></span>(int icon, <a href="http://developer.android.com/reference/java/lang/CharSequence.html">CharSequence</a> title, <a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a> intent)</nobr>
- <div class="jd-descrdiv">Construct a new builder for an <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> object.</div>
+ <div class="jd-descrdiv">Construct a new builder for <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> object.</div>
</td></tr>
@@ -872,8 +863,25 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Construct a new builder for an <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> object.
-</p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Construct a new builder for <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> object.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>icon</td>
+ <td>icon to show for this action</td>
+ </tr>
+ <tr>
+ <th>title</td>
+ <td>the title of the action</td>
+ </tr>
+ <tr>
+ <th>intent</td>
+ <td>the <code><a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a></code> to fire when users trigger this action
+</td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
@@ -918,9 +926,22 @@ From class
<div class="jd-details-descr">
<div class="jd-tagdata jd-tagdescr"><p>Add an input to be collected from the user when this action is sent.
- Response values are sent as extras to this Action's pending intent when
- sent.
-</p></div>
+ Response values are sent as extras to this action's pending intent when
+ sent.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>remoteInput</td>
+ <td>a <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> to add to the action</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining
+</li></ul>
+ </div>
</div>
</div>
@@ -950,8 +971,12 @@ From class
<div class="jd-details-descr">
<div class="jd-tagdata jd-tagdescr"><p>Combine all of the options that have been set and return a new <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code>
- object.
-</p></div>
+ object.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>the built action
+</li></ul>
+ </div>
</div>
</div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html
index e073881..ff9c904 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html
@@ -474,14 +474,11 @@ Summary:
<h2>Class Overview</h2>
-<p itemprop="articleBody">Subclass of <code><a href="/reference/android/support/v4/app/NotificationCompat.Action.html">NotificationCompat.Action</a></code> which adds support for additional
- wearable extensions.
-
- <p>To create a new Action, use the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html">WearableNotifications.Action.Builder</a></code> class and then call
+<p itemprop="articleBody">Subclass of <code><a href="/reference/android/support/v4/app/NotificationCompat.Action.html">NotificationCompat.Action</a></code> that adds additional
+ wearable extensions for actions.
+ <p>Always use the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html">WearableNotifications.Action.Builder</a></code> to build instances of this class and call
<code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#addAction(android.preview.support.wearable.notifications.WearableNotifications.Action)">addAction(WearableNotifications.Action)</a></code> to add the action to a notification.
- <p>Example:
-
<pre class="prettyprint">
WearableNotifications.Builder builder = new WearableNotifications.Builder(mContext)
.addAction(new WearableNotifications.Action.Builder(
@@ -991,8 +988,12 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Get a list of inputs to be collected from the user when this action is sent.
-</p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Get a list of inputs to be collected from the user when this action is sent.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>the array of <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> objects associated with this action
+</li></ul>
+ </div>
</div>
</div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html
index 25e4520..d6ec260 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html
@@ -447,29 +447,11 @@ Summary:
<h2>Class Overview</h2>
-<p itemprop="articleBody">Builder object that wraps a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> to provide
- methods for adding wearable extensions to a notification.
+<p itemprop="articleBody">Builder class that wraps a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> to add
+ wearable extensions for a notification.
- <p>Methods on the wrapped <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> and this object
- can be called in any order, but the final Notification must be built with
- the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#build()">build()</a></code> method of this class.
-
- <p>Note: Notifications created using this builder should be posted to the notification
- system using the <code>NotificationManagerCompat.notify(...)</code> methods instead of
- <code>NotificationManager.notify(...)</code>.
-
- <p>Example:
-
- <pre class="prettyprint">
- NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
- .setContentTitle(&quot;New mail from &quot; + sender.toString())
- .setContentText(subject)
- .setSmallIcon(R.drawable.new_mail);
- Notification notif = new WearableNotifications.Builder(builder)
- .setLocalOnly(true)
- .setMinPriority()
- .build();
- NotificationManagerCompat.from(mContext).notify(0, notif);</pre>
+ <p>You can chain the "set" methods for this builder in any order,
+ but you must call the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#build()">build()</a></code> method last to build the final notification.
</p>
@@ -662,7 +644,7 @@ Summary:
<div class="jd-descrdiv">Combine all of the options that have been set by both this builder and
the wrapped <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> object and return a new
- <code><a href="/http://developer.android.com/reference/android/app/Notification.html">Notification</a></code> object.</div>
+ <code><a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a></code> object.</div>
</td></tr>
@@ -826,7 +808,7 @@ Summary:
<span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setMinPriority()">setMinPriority</a></span>()</nobr>
<div class="jd-descrdiv">Set the priority of this notification to be minimum priority level
- (<code><a href="/http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>).</div>
+ (<code><a href="http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>).</div>
</td></tr>
@@ -1105,14 +1087,12 @@ From class
<div class="jd-details-descr">
<div class="jd-tagdata jd-tagdescr"><p>Construct a builder to be used for adding wearable extensions to notifications. Both the
- wrapped builder (accessible via <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#getCompatBuilder()">getCompatBuilder()</a></code> and this builder can be used
+ wrapped builder (accessible via <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#getCompatBuilder()">getCompatBuilder()</a></code>) and this builder can be used
simultaneously, but the build() method from this object must be called in the end.
- <p>Note: Notifications created using this builder should be posted to the notification
- system using the <code>NotificationManagerCompat.notify(...)</code> methods instead of
- <code>NotificationManager.notify(...)</code>.
-
- <p>Example:
+ <p>Always post notifications to the notification
+ system with the <code>NotificationManagerCompat.notify(...)</code> methods
+ instead of the <code>NotificationManager.notify(...)</code> methods.
<pre class="prettyprint">
WearableNotifications.Builder builder = new WearableNotifications.Builder(mContext)
@@ -1156,12 +1136,9 @@ From class
a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code>. Both the wrapped builder and this
builder can be used simultaneously, but the build() method from this object must be
called in the end.
-
- <p>Note: Notifications created using this builder should be posted to the notification
- system using the <code>NotificationManagerCompat.notify(...)</code> methods instead of
- <code>NotificationManager.notify(...)</code>.
-
- <p>Example:
+ <p>Always post notifications to the notification
+ system with the <code>NotificationManagerCompat.notify(...)</code> methods
+ instead of the <code>NotificationManager.notify(...)</code> methods.
<pre class="prettyprint">
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
@@ -1221,6 +1198,19 @@ From class
accepts <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> extension wrappers. Actions added by this function
are appended when <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#build()">build()</a></code> is called.</p></div>
<div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>action</td>
+ <td>the action to add to this notification</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining</li></ul>
+ </div>
+ <div class="jd-tagdata">
<h5 class="jd-tagtitle">See Also</h5>
<ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code></li>
</ul>
@@ -1258,6 +1248,19 @@ From class
subsequent pages. This field can be used to separate a notification into multiple
sections.</p></div>
<div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>page</td>
+ <td>the notification to add as another page</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining</li></ul>
+ </div>
+ <div class="jd-tagdata">
<h5 class="jd-tagtitle">See Also</h5>
<ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getPages(android.app.Notification)">getPages(Notification)</a></code></li>
</ul>
@@ -1295,6 +1298,19 @@ From class
subsequent pages. This field can be used to separate a notification into multiple
sections.</p></div>
<div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>pages</td>
+ <td>a collection of notifications</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining</li></ul>
+ </div>
+ <div class="jd-tagdata">
<h5 class="jd-tagtitle">See Also</h5>
<ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getPages(android.app.Notification)">getPages(Notification)</a></code></li>
</ul>
@@ -1328,8 +1344,21 @@ From class
<div class="jd-details-descr">
<div class="jd-tagdata jd-tagdescr"><p>Adds a <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> for the content intent. The collected
- data will be overlayed onto the content intent.
-</p></div>
+ data will be overlayed onto the content intent.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>input</td>
+ <td>a <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> object to obtain a user response</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining
+</li></ul>
+ </div>
</div>
</div>
@@ -1360,8 +1389,12 @@ From class
<div class="jd-tagdata jd-tagdescr"><p>Combine all of the options that have been set by both this builder and
the wrapped <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> object and return a new
- <code><a href="/http://developer.android.com/reference/android/app/Notification.html">Notification</a></code> object.
-</p></div>
+ <code><a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a></code> object.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>the notification
+</li></ul>
+ </div>
</div>
</div>
@@ -1390,8 +1423,12 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Return the <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> being wrapped by this object.
-</p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Return the <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> being wrapped by this object.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>the wrapped <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code>
+</li></ul>
+ </div>
</div>
</div>
@@ -1423,8 +1460,12 @@ From class
<div class="jd-tagdata jd-tagdescr"><p>Get the current metadata Bundle used by this Builder, creating a new one
as necessary.
- <p>The returned Bundle is shared with this Builder.
-</p></div>
+ <p>The returned Bundle is shared with this Builder.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>the extras bundle
+</li></ul>
+ </div>
</div>
</div>
@@ -1460,15 +1501,19 @@ From class
<table class="jd-tagtable">
<tr>
<th>icon</td>
- <td>Icon to display for the content action.</td>
+ <td>icon to display for the content action.</td>
</tr>
<tr>
<th>subtext</td>
- <td>Optional subtext to display with the big action icon.
-</td>
+ <td>Optional subtext to display with the big action icon.</td>
</tr>
</table>
</div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining
+</li></ul>
+ </div>
</div>
</div>
@@ -1504,11 +1549,15 @@ From class
<table class="jd-tagtable">
<tr>
<th>icon</td>
- <td>Icon to display for the content action.
-</td>
+ <td>icon to display for the content action.</td>
</tr>
</table>
</div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining
+</li></ul>
+ </div>
</div>
</div>
@@ -1551,11 +1600,15 @@ From class
<th>groupOrder</td>
<td>The 0-indexed sort order within the group. Can also be set
to the sentinel value <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY">GROUP_ORDER_SUMMARY</a></code> to mark this
- notification as being the group summary.
-</td>
+ notification as being the group summary.</td>
</tr>
</table>
</div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining
+</li></ul>
+ </div>
</div>
</div>
@@ -1592,11 +1645,15 @@ From class
<table class="jd-tagtable">
<tr>
<th>groupKey</td>
- <td>The group key of the group. Unique within a package.
-</td>
+ <td>The group key of the group. Unique within a package.</td>
</tr>
</table>
</div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining
+</li></ul>
+ </div>
</div>
</div>
@@ -1625,8 +1682,21 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Set a hint that this notification's icon should not be displayed.
-</p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Set a hint that this notification's icon should not be displayed.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>hintHideIcon</td>
+ <td><code>true</code> to hide the icon, <code>false</code> otherwise.</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining
+</li></ul>
+ </div>
</div>
</div>
@@ -1660,6 +1730,20 @@ From class
<p>Some notifications can be bridged to other devices for remote display.
This hint can be set to recommend this notification not be bridged.</p></div>
<div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>localOnly</td>
+ <td><code>true</code> to keep the notification on this device,
+ <code>false</code> otherwise. Default value is <code>false</code>.</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining</li></ul>
+ </div>
+ <div class="jd-tagdata">
<h5 class="jd-tagtitle">See Also</h5>
<ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getLocalOnly(android.app.Notification)">getLocalOnly(Notification)</a></code></li>
</ul>
@@ -1693,10 +1777,14 @@ From class
<div class="jd-details-descr">
<div class="jd-tagdata jd-tagdescr"><p>Set the priority of this notification to be minimum priority level
- (<code><a href="/http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>). When set via WearableNotifications, these
+ (<code><a href="http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>). When set via WearableNotifications, these
minimum priority notifications will bypass the notification manager on platforms
- that do not support ambient level notifications.
-</p></div>
+ that do not support ambient level notifications.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>this object for method chaining
+</li></ul>
+ </div>
</div>
</div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html
index 45b77c6..e03e16e 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html
@@ -450,15 +450,39 @@ Summary:
<h2>Class Overview</h2>
-<p itemprop="articleBody">Helper providing extensions to android notifications for use with wearable devices.
-
- <p>To build notifications with wearable extensions, use the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></code> class.
- Notifications created using Builder should be posted to the notification system
- using the <code>NotificationManagerCompat.notify(...)</code> methods instead of
- <code>NotificationManager.notify(...)</code>.
-
- <p>Once a notification is built, a variety of methods are provide in this utility to access
- the value of various notification fields in a backwards compatible manner.
+<p itemprop="articleBody">Helper class that contains wearable extensions for notifications.
+ Always use the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></code> to create instances of this class.
+ <p class="note"> See
+ <a href="/wear/notifications/creating.html">Creating Notifications
+ for Android Wear</a> for more information on how to use this class.
+ <p>
+ To create a notification with wearable extensions:
+ <ol>
+ <li>Create a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code>, setting any desired
+ properties.
+ <li>Create a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></code>, passing in the
+ <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> as a starting point.
+ <li>Set wearable-specific properties using the
+ <code>add</code> and <code>set</code> methods of <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></code>.
+ <li>Call <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#build()">build()</a></code> to create the notification.
+ <li>Post the notification to the notification
+ system with the <code>NotificationManagerCompat.notify(...)</code> methods
+ and not the <code>NotificationManager.notify(...)</code> methods.
+ </ol>
+
+ <pre class="prettyprint">
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
+ .setContentTitle(&quot;New mail from &quot; + sender.toString())
+ .setContentText(subject)
+ .setSmallIcon(R.drawable.new_mail);
+ Notification notif = new WearableNotifications.Builder(builder)
+ .setLocalOnly(true)
+ .setMinPriority()
+ .build();
+ NotificationManagerCompat.from(mContext).notify(0, notif);</pre>
+ <p>When you receive a notification object from the builder, the methods in
+ this class let you access the values of various notification fields in
+ a backward-compatible manner.
</p>
@@ -502,8 +526,8 @@ Summary:
class</nobr></td>
<td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></td>
- <td class="jd-descrcol" width="100%">Subclass of <code><a href="/reference/android/support/v4/app/NotificationCompat.Action.html">NotificationCompat.Action</a></code> which adds support for additional
- wearable extensions.&nbsp;</td>
+ <td class="jd-descrcol" width="100%">Subclass of <code><a href="/reference/android/support/v4/app/NotificationCompat.Action.html">NotificationCompat.Action</a></code> that adds additional
+ wearable extensions for actions.&nbsp;</td>
</tr>
@@ -515,8 +539,8 @@ Summary:
class</nobr></td>
<td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></td>
- <td class="jd-descrcol" width="100%">Builder object that wraps a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> to provide
- methods for adding wearable extensions to a notification.&nbsp;</td>
+ <td class="jd-descrcol" width="100%">Builder class that wraps a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> to add
+ wearable extensions for a notification.&nbsp;</td>
</tr>
@@ -548,8 +572,9 @@ Summary:
<tr class=" api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY">GROUP_ORDER_SUMMARY</a></td>
- <td class="jd-descrcol" width="100%">Sentinel value provided to the groupOrder parameter <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setGroup(android.app.Notification, java.lang.String)">setGroup(Notification, String)</a></code> to indicate that
- this member of a notification group is the summary of the group.</td>
+ <td class="jd-descrcol" width="100%">Sentinel value provided to the <code>groupOrder</code> parameter of the
+ <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setGroup(android.app.Notification, java.lang.String, int)">setGroup(android.app.Notification, java.lang.String, int)</a></code>
+ method.</td>
</tr>
@@ -589,8 +614,8 @@ Summary:
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getAction(android.app.Notification, int)">getAction</a></span>(<a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a> notif, int actionIndex)</nobr>
- <div class="jd-descrdiv">Get a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> wrapper for the notification at index <code>actionIndex</code>
- in the <code><a href="/http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code> array.</div>
+ <div class="jd-descrdiv">Get a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> wrapper for the notification at <code>actionIndex</code>
+ in the <code><a href="http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code> array.</div>
</td></tr>
@@ -662,7 +687,7 @@ Summary:
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getContentIntentRemoteInputs(android.app.Notification)">getContentIntentRemoteInputs</a></span>(<a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a> notif)</nobr>
- <div class="jd-descrdiv">Gets the <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code>s associated with the content intent.</div>
+ <div class="jd-descrdiv">Gets the <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> objects associated with the content intent.</div>
</td></tr>
@@ -680,8 +705,8 @@ Summary:
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getExtras(android.app.Notification)">getExtras</a></span>(<a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a> notif)</nobr>
- <div class="jd-descrdiv">Gets the <code><a href="/http://developer.android.com/reference/android/app/Notification.html#extras">extras</a></code> field from a notification in a backwards
- compatible manner.</div>
+ <div class="jd-descrdiv">Gets the <code><a href="http://developer.android.com/reference/android/app/Notification.html#extras">extras</a></code> field from a notification in a backward-compatible
+ manner.</div>
</td></tr>
@@ -919,7 +944,7 @@ Summary:
<span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setMinPriority(android.app.Notification)">setMinPriority</a></span>(<a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a> notif)</nobr>
<div class="jd-descrdiv">Set the priority of this notification to be minimum priority level
- (<code><a href="/http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>).</div>
+ (<code><a href="http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>).</div>
</td></tr>
@@ -1246,8 +1271,10 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Sentinel value provided to the groupOrder parameter <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setGroup(android.app.Notification, java.lang.String)">setGroup(Notification, String)</a></code> to indicate that
- this member of a notification group is the summary of the group.
+ <div class="jd-tagdata jd-tagdescr"><p>Sentinel value provided to the <code>groupOrder</code> parameter of the
+ <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setGroup(android.app.Notification, java.lang.String, int)">setGroup(android.app.Notification, java.lang.String, int)</a></code>
+ method. This value indicates that this index of the
+ notification group is the summary of the group.
</p></div>
@@ -1309,9 +1336,22 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Get a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> wrapper for the notification at index <code>actionIndex</code>
- in the <code><a href="/http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code> array.
-</p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Get a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> wrapper for the notification at <code>actionIndex</code>
+ in the <code><a href="http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code> array.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to inspect</td>
+ </tr>
+ <tr>
+ <th>actionIndex</td>
+ <td>the index of the desired action
+</td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
@@ -1342,6 +1382,20 @@ From class
<div class="jd-tagdata jd-tagdescr"><p>Get the number of actions present on this notification.</p></div>
<div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to inspect</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>the number of actions for this notification
+</li></ul>
+ </div>
+ <div class="jd-tagdata">
<h5 class="jd-tagtitle">See Also</h5>
<ul class="nolist"><li><code><a href="/http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code></li>
</ul>
@@ -1377,6 +1431,19 @@ From class
<div class="jd-tagdata jd-tagdescr"><p>Get the big action icon to be displayed with this notification. Big actions show
a hint to users about the action taken when the content intent is triggered.</p></div>
<div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to inspect</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>the icon or <code>0</code> if it wasn't set</li></ul>
+ </div>
+ <div class="jd-tagdata">
<h5 class="jd-tagtitle">See Also</h5>
<ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setBigActionIcon(android.app.Notification, int)">setBigActionIcon(Notification, int)</a></code></li>
</ul>
@@ -1411,6 +1478,19 @@ From class
<div class="jd-tagdata jd-tagdescr"><p>Get the big action icon subtext to be shown with a big action icon.</p></div>
<div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to inspect</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>the subtext of the big action icon or <code>null</code> if it wasn't exist.</li></ul>
+ </div>
+ <div class="jd-tagdata">
<h5 class="jd-tagtitle">See Also</h5>
<ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setBigActionIcon(android.app.Notification, int)">setBigActionIcon(Notification, int)</a></code></li>
</ul>
@@ -1443,8 +1523,21 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Gets the <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code>s associated with the content intent.
-</p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Gets the <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> objects associated with the content intent.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to inspect</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>array of RemoteInput objects or <code>null</code> if it doesn't exist
+</li></ul>
+ </div>
</div>
</div>
@@ -1473,10 +1566,23 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Gets the <code><a href="/http://developer.android.com/reference/android/app/Notification.html#extras">extras</a></code> field from a notification in a backwards
- compatible manner. Extras field was supported from JellyBean (Api level 16)
- forwards. This function will return null on older api levels.
-</p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Gets the <code><a href="http://developer.android.com/reference/android/app/Notification.html#extras">extras</a></code> field from a notification in a backward-compatible
+ manner. Extras field was supported from JellyBean (API level 16)
+ forwards. This function will return null on older API levels.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to inspect</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>the extras associated with this notification.
+</li></ul>
+ </div>
</div>
</div>
@@ -1506,8 +1612,17 @@ From class
<div class="jd-details-descr">
<div class="jd-tagdata jd-tagdescr"><p>Get the key used to group this notification into a cluster or stack
- with other notifications. This key is unique within a package.
-</p></div>
+ with other notifications. This key is unique within a package.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to inspect
+</td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
@@ -1539,8 +1654,21 @@ From class
<div class="jd-tagdata jd-tagdescr"><p>Get the sort order of this notification within a group of notifications
with the same group key set. Group orders are 0-indexed integers that are used
to sort notifications in ascending order. Can also be the sentinel value
- <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY">GROUP_ORDER_SUMMARY</a></code> if this is the summary notification for a group.
-</p></div>
+ <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY">GROUP_ORDER_SUMMARY</a></code> if this is the summary notification for a group.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to inspect</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>the sort order of this notification within this group
+</li></ul>
+ </div>
</div>
</div>
@@ -1569,8 +1697,22 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Get a hint that this notification's icon should not be displayed.
-</p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Get a hint that this notification's icon should not be displayed.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to inspect</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li><code>true</code> if this icon should not be displayed, false otherwise.
+ The default value is <code>false</code> if this was never set.
+</li></ul>
+ </div>
</div>
</div>
@@ -1602,8 +1744,22 @@ From class
<div class="jd-tagdata jd-tagdescr"><p>Get whether or not this notification is only relevant to the current device.
<p>Some notifications can be bridged to other devices for remote display.
- If this hint is set, it is recommended that this notification not be bridged.
-</p></div>
+ If this hint is set, it is recommended that this notification not be bridged.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to inspect</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li><code>true</code> if this notification is local only, <code>false</code> otherwise.
+ Default value is <code>false</code> if not set.
+</li></ul>
+ </div>
</div>
</div>
@@ -1635,8 +1791,21 @@ From class
<div class="jd-tagdata jd-tagdescr"><p>Get the array of additional pages of content for displaying this notification. The
current notification forms the first page, and elements within this array form
subsequent pages. This field can be used to separate a notification into multiple
- sections.
-</p></div>
+ sections.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to inspect</td>
+ </tr>
+ </table>
+ </div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li>the pages for this notification
+</li></ul>
+ </div>
</div>
</div>
@@ -1671,8 +1840,12 @@ From class
<h5 class="jd-tagtitle">Parameters</h5>
<table class="jd-tagtable">
<tr>
+ <th>notif</td>
+ <td>the notification to modify</td>
+ </tr>
+ <tr>
<th>icon</td>
- <td>Icon to display for the content action.
+ <td>icon to display for the content action.
</td>
</tr>
</table>
@@ -1711,8 +1884,12 @@ From class
<h5 class="jd-tagtitle">Parameters</h5>
<table class="jd-tagtable">
<tr>
+ <th>notif</td>
+ <td>the notification to modify</td>
+ </tr>
+ <tr>
<th>icon</td>
- <td>Icon to display for the content action.</td>
+ <td>icon to display for the content action.</td>
</tr>
<tr>
<th>subtext</td>
@@ -1751,8 +1928,21 @@ From class
<div class="jd-tagdata jd-tagdescr"><p>Sets <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code>s to be collected when the user triggers the
<code>contentIntent</code>. These function just as if they were attached to
- an <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code>.
-</p></div>
+ an <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code>.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to modify</td>
+ </tr>
+ <tr>
+ <th>inputs</td>
+ <td>array of <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> objects desired from the user.
+</td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
@@ -1788,6 +1978,10 @@ From class
<h5 class="jd-tagtitle">Parameters</h5>
<table class="jd-tagtable">
<tr>
+ <th>notif</td>
+ <td>the notification to modify</td>
+ </tr>
+ <tr>
<th>groupKey</td>
<td>The group key of the group. Unique within a package.
</td>
@@ -1829,6 +2023,10 @@ From class
<h5 class="jd-tagtitle">Parameters</h5>
<table class="jd-tagtable">
<tr>
+ <th>notif</td>
+ <td>the notification to modify</td>
+ </tr>
+ <tr>
<th>groupKey</td>
<td>The group key of the group. Unique within a package.</td>
</tr>
@@ -1869,8 +2067,21 @@ From class
</div>
<div class="jd-details-descr">
- <div class="jd-tagdata jd-tagdescr"><p>Set a hint that this notification's icon should not be displayed.
-</p></div>
+ <div class="jd-tagdata jd-tagdescr"><p>Set a hint that this notification's icon should not be displayed.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to modify</td>
+ </tr>
+ <tr>
+ <th>hintHideIcon</td>
+ <td><code>true</code> to hide this icon, <code>false</code> otherwise.
+</td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
@@ -1902,8 +2113,22 @@ From class
<div class="jd-tagdata jd-tagdescr"><p>Set whether or not this notification is only relevant to the current device.
<p>Some notifications can be bridged to other devices for remote display.
- This hint can be set to recommend this notification not be bridged.
-</p></div>
+ This hint can be set to recommend this notification not be bridged.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to modify</td>
+ </tr>
+ <tr>
+ <th>localOnly</td>
+ <td>set to <code>true</code> to keep the notification on this device only,
+ <code>false</code> otherwise.
+</td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
@@ -1933,10 +2158,19 @@ From class
<div class="jd-details-descr">
<div class="jd-tagdata jd-tagdescr"><p>Set the priority of this notification to be minimum priority level
- (<code><a href="/http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>). When set via WearableNotifications, these
+ (<code><a href="http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>). When set via WearableNotifications, these
minimum priority notifications will bypass the notification manager on platforms
- that do not support ambient level notifications.
-</p></div>
+ that do not support ambient level notifications.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to modify
+</td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
@@ -1968,8 +2202,22 @@ From class
<div class="jd-tagdata jd-tagdescr"><p>Set additional pages of content to display with this notification. The current
notification forms the first page, and pages set using this function form
subsequent pages. This field can be used to separate a notification into multiple
- sections.
-</p></div>
+ sections.</p></div>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable">
+ <tr>
+ <th>notif</td>
+ <td>the notification to modify</td>
+ </tr>
+ <tr>
+ <th>pages</td>
+ <td>the pages to add to the current notification. Replaces any
+ existing pages with this value.
+</td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
diff --git a/docs/html/training/displaying-bitmaps/cache-bitmap.jd b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
index ff9c3a0..7b9216e 100644
--- a/docs/html/training/displaying-bitmaps/cache-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
@@ -24,8 +24,8 @@ trainingnavtop=true
<h2>Try it out</h2>
<div class="download-box">
- <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
- <p class="filename">BitmapFun.zip</p>
+ <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+ <p class="filename">DisplayingBitmaps.zip</p>
</div>
</div>
diff --git a/docs/html/training/displaying-bitmaps/display-bitmap.jd b/docs/html/training/displaying-bitmaps/display-bitmap.jd
index ed1836c..4147f83 100644
--- a/docs/html/training/displaying-bitmaps/display-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/display-bitmap.jd
@@ -24,8 +24,8 @@ trainingnavtop=true
<h2>Try it out</h2>
<div class="download-box">
- <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
- <p class="filename">BitmapFun.zip</p>
+ <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+ <p class="filename">DisplayingBitmaps.zip</p>
</div>
</div>
diff --git a/docs/html/training/displaying-bitmaps/index.jd b/docs/html/training/displaying-bitmaps/index.jd
index 7003585..831c64d 100644
--- a/docs/html/training/displaying-bitmaps/index.jd
+++ b/docs/html/training/displaying-bitmaps/index.jd
@@ -18,8 +18,8 @@ startpage=true
<h2>Try it out</h2>
<div class="download-box">
- <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
- <p class="filename">BitmapFun.zip</p>
+ <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+ <p class="filename">DisplayingBitmaps.zip</p>
</div>
</div>
diff --git a/docs/html/training/displaying-bitmaps/load-bitmap.jd b/docs/html/training/displaying-bitmaps/load-bitmap.jd
index 938901f..f963baa 100644
--- a/docs/html/training/displaying-bitmaps/load-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/load-bitmap.jd
@@ -18,8 +18,8 @@ trainingnavtop=true
<h2>Try it out</h2>
<div class="download-box">
- <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
- <p class="filename">BitmapFun.zip</p>
+ <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+ <p class="filename">DisplayingBitmaps.zip</p>
</div>
</div>
diff --git a/docs/html/training/displaying-bitmaps/manage-memory.jd b/docs/html/training/displaying-bitmaps/manage-memory.jd
index 7f2b4c5..5a5c2cd 100644
--- a/docs/html/training/displaying-bitmaps/manage-memory.jd
+++ b/docs/html/training/displaying-bitmaps/manage-memory.jd
@@ -26,8 +26,8 @@ trainingnavtop=true
<h2>Try it out</h2>
<div class="download-box">
- <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
- <p class="filename">BitmapFun.zip</p>
+ <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+ <p class="filename">DisplayingBitmaps.zip</p>
</div>
</div>
diff --git a/docs/html/training/displaying-bitmaps/process-bitmap.jd b/docs/html/training/displaying-bitmaps/process-bitmap.jd
index ed0b368..a557619 100644
--- a/docs/html/training/displaying-bitmaps/process-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/process-bitmap.jd
@@ -26,8 +26,8 @@ trainingnavtop=true
<h2>Try it out</h2>
<div class="download-box">
- <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
- <p class="filename">BitmapFun.zip</p>
+ <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+ <p class="filename">DisplayingBitmaps.zip</p>
</div>
</div>
diff --git a/docs/html/wear/css/wear.css b/docs/html/wear/css/wear.css
index 9d9d7a7..40afeaa 100644
--- a/docs/html/wear/css/wear.css
+++ b/docs/html/wear/css/wear.css
@@ -2,6 +2,7 @@
* UTILITIES
*/
+
.border-box {
box-sizing: border-box;
}
@@ -92,6 +93,10 @@
* LAYOUT
*/
+#body-content,
+.fullpage,
+#jd-content,
+.jd-descr,
.wear-body-content {
height: 100%;
}
@@ -134,13 +139,11 @@
}
.wear-hero-container {
- height: 800px;
- height: 100vh;
+ height: 100%;
}
.wear-hero {
- height: 100%;
- height: calc(100% - 72px);
+ height: calc(100% - 70px);
min-height: 504px;
margin-top: -4px;
padding-top: 0;
@@ -184,6 +187,7 @@
}
.wear-button {
+ white-space: nowrap;
display: inline-block;
padding: 16px 32px;
font-size: 18px;
@@ -233,6 +237,7 @@ a.wear-button:visited {
}
.wear-video-link {
+ white-space: nowrap;
display: inline-block;
padding: 16px 32px 16px 82px;
font-size: 18px;
@@ -436,7 +441,7 @@ pointer-events: all;
#icon-video-close {
background-image: url("../images/close.png");
background-position: 0 0;
-height: 48px;
-width: 48px;
+height: 36px;
+width: 36px;
display:block;
-} \ No newline at end of file
+}
diff --git a/docs/html/wear/design/index.html b/docs/html/wear/design/index.html
index 4bbbf29..9952490 100644
--- a/docs/html/wear/design/index.html
+++ b/docs/html/wear/design/index.html
@@ -379,11 +379,15 @@ h3 {
}
</style>
<p>
-Android Wear devices provide just the right information at just the right time, allowing you to be connected to the virtual world and present in the real world.</p>
+Android wearables provide just the right information at just the right time, allowing you to be connected to the virtual world and present in the real world.</p>
<img src="/wear/images/05_images.png" height="200" width="169" style="float:right;clear:right;margin:0 0 60px 60px" />
-<p>Here you’ll find some guidelines for designing great user experiences on the Android Wear platform. Designing for Android Wear is substantially different than designing for phones or tablets, so we’ll start by describing how your content can work in tandem with the overall Android Wear vision.</p>
+<p>Here you’ll find some guidelines for designing great user experiences on the Android Wear
+platform. Designing for Android Wear is substantially different than designing for phones or
+tablets, so we’ll start by describing how your content can work in tandem with the overall
+Android Wear vision. To better understand the user experience on Android Wear, also be sure
+to read the <a href="/wear/design/user-interface.html">UI Overview</a>.</p>
<img src="/wear/images/02_notifications.png" height="200" width="169" style="float:right;clear:right;margin:0 0 20px 60px" />
@@ -393,7 +397,7 @@ Android Wear devices provide just the right information at just the right time,
<p>Android Wear experiences are:</p>
<ul>
- <li><strong>Contextually aware and smart.</strong> These devices bring a new level of awareness to computing. Rather than requiring attention and input from users, Android Wear devices are aware of their situation and state, and helpfully display the right information at the right time. <em>Timely, relevant, specific</em>.</li>
+ <li><strong>Contextually aware and smart.</strong> These devices bring a new level of awareness to computing. Rather than requiring attention and input from users, Android wearables are aware of their situation and state, and helpfully display the right information at the right time. <em>Timely, relevant, specific</em>.</li>
<li><strong>Glanceable.</strong> Wearable devices are used all throughout the day, even when they sit in our peripheral vision. Effective apps provide the maximum payload of information with a minimum of fuss, optimized to provide tiny snippets of relevant information throughout the day. <em>Short, sharp, immediate.</em></li>
@@ -462,7 +466,8 @@ Notifications for Android Wear</a>.</p>
<img src="/wear/images/07_appicons.png" height="200" style="float:right;margin:0 0 20px 60px" />
-<p>Your application’s launcher icon will be automatically placed on the card, identifying your notification. Do not use the notification title or background image to identify or brand your application. Instead, allow your icon to identify itself and focus on delivering a clear, succinct message in the card and image. You can choose not to display this icon.
+<p>Your application’s launcher icon will be automatically placed on the card, identifying your notification. Do not use the notification title or background image to identify or brand your application. Instead, allow your icon to identify itself and focus on delivering a clear, succinct message in the card and image. You can choose not to display this icon using
+ <a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setHintHideIcon(boolean)"><code>setHintHideIcon()</code></a>.
</p>
diff --git a/docs/html/wear/design/user-interface.html b/docs/html/wear/design/user-interface.html
index f87b9da..c23d79c 100644
--- a/docs/html/wear/design/user-interface.html
+++ b/docs/html/wear/design/user-interface.html
@@ -404,9 +404,9 @@ likely to be useful.</p>
<p>Cards in the stream are more than simple notifications. They can be swiped horizontally to
reveal additional pages. Further horizontal swiping may reveal tappable buttons, allowing the user
to take action on the notification. Cards can also be dismissed by swiping left to right, removing
-them from the stream until the next time they have useful information to display. In the emulator,
-hovering the mouse over the screen illuminates a blue bar at the top of the device
-that takes you home when clicked.</p>
+them from the stream until the next time they have useful information to display.
+In the emulator, hovering the mouse over the top of the screen illuminates a blue bar at
+the top of the device that takes you home when clicked.</p>
diff --git a/docs/html/wear/images/close.png b/docs/html/wear/images/close.png
index bd473d2..7e45fb7 100644
--- a/docs/html/wear/images/close.png
+++ b/docs/html/wear/images/close.png
Binary files differ
diff --git a/docs/html/wear/index.html b/docs/html/wear/index.html
index 5ea793b..712e1af 100644
--- a/docs/html/wear/index.html
+++ b/docs/html/wear/index.html
@@ -280,7 +280,17 @@ onkeyup="return search_changed(event, false, '/')" />
<div class="jd-descr" itemprop="articleBody">
- <div id="video-container">
+ <style>
+.fullpage>#footer,
+#jd-content>.content-footer.wrap {
+ display:none;
+}
+</style>
+
+
+
+
+<div id="video-container">
<div id="video-frame">
<div class="video-close">
<span id="icon-video-close">&nbsp;</span>
@@ -294,6 +304,7 @@ onkeyup="return search_changed(event, false, '/')" />
</div>
+
<div class="wear-body-content">
<div class="wear-hero-container">
<div class="wear-section wear-hero">
@@ -319,7 +330,7 @@ onkeyup="return search_changed(event, false, '/')" />
<a href="/wear/preview/start.html" class="wear-button wear-primary" style="margin-top: 40px;">
Get the Developer Preview
</a>
- <a id="watchVideo" href="https://youtube.googleapis.com/v/i2uvYI6blEE">
+ <a id="watchVideo" href="https://youtube.googleapis.com/v/0xQ3y902DEQ">
<div class="wear-video-link">Watch the video</div>
</a>
<script>
@@ -328,7 +339,7 @@ $("#watchVideo").on("click", function(e) {
var params = { allowScriptAccess: "always"};
var atts = { id: "ytapiplayer" };
- swfobject.embedSWF("//www.youtube.com/v/i2uvYI6blEE?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1;autoplay=1",
+ swfobject.embedSWF("//www.youtube.com/v/0xQ3y902DEQ?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1;autoplay=1",
"ytapiplayer", "940", "526.4", "8", null, null, params, atts);
e.preventDefault();
@@ -491,7 +502,7 @@ $("#icon-video-close").on("click", function() {
<img src="/wear/images/features/ts2.png" alt="">
<p>Send Data</p>
<p class="wear-small">
- Send data and actions between a phone and a wearable with a data replication APIs and RPCs.
+ Send data and actions between a phone and a wearable with data replication APIs and RPCs.
</p>
</div>
<div class="col-4">
@@ -505,7 +516,7 @@ $("#icon-video-close").on("click", function() {
<img src="/wear/images/features/ts4.png" alt="">
<p>Voice Actions</p>
<p class="wear-small">
- Register your app to handle voice actions, like "Ok Google, take a note.""
+ Register your app to handle voice actions, like "Ok Google, take a note."
</p>
</div>
</div>
@@ -590,30 +601,30 @@ $("#icon-video-close").on("click", function() {
<div class="cols">
<div class="wear-body">
<div class="col-3-wide">
- <a href="/TODO">
+ <a target="_blank" href="https://www.youtube.com/playlist?list=PLWz5rJ2EKKc-kIrPiq098QH9dOle-fLef">
<img class="wear-social-image" src="//www.google.com/images/icons/product/youtube-128.png" alt="">
</a>
<div class="wear-social-copy">
<p>DevBytes</p>
<p class="wear-small">
- Learn how to optimize your app notifications for wearable devices in this <a href="/TODO">DevBytes video</a> using the Android Wear Developer Preview.
+ Learn how to optimize your app notifications for wearable devices in this <a target="_blank" href="https://www.youtube.com/playlist?list=PLWz5rJ2EKKc-kIrPiq098QH9dOle-fLef">DevBytes video</a> using the Android Wear Developer Preview.
</p>
</div>
</div>
<div class="col-3-wide">
- <a href="http://android-developers.blogspot.com/">
+ <a target="_blank" href="http://android-developers.blogspot.com/2014/03/android-wear-developer-preview.html">
<img class="wear-social-image" src="/wear/images/blogger.png" alt="">
</a>
<div class="wear-social-copy">
<p>Blog Post</p>
<p class="wear-small">
Read more about the Android Wear Developer Preview announcement
- at the <a href="http://android-developers.blogspot.com/">Android Developers Blog</a>.
+ at the <a target="_blank" href="http://android-developers.blogspot.com/2014/03/android-wear-developer-preview.html">Android Developers Blog</a>.
</p>
</div>
</div>
<div class="col-3-wide">
- <a href="http://g.co/androidweardev">
+ <a target="_blank" href="http://g.co/androidweardev">
<img class="wear-social-image" src="//www.google.com/images/icons/product/gplus-128.png" alt="+Android Wear Developers">
</a>
<div class="wear-social-copy">
@@ -622,7 +633,7 @@ $("#icon-video-close").on("click", function() {
Follow us on Google+ to stay up-to-date on Android Wear development and join the discussion!
</p>
<p class="wear-small">
- <a href="http://g.co/androidweardev">+Android Wear Developers</a>
+ <a target="_blank" href="http://g.co/androidweardev">+Android Wear Developers</a>
</p>
</div>
</div>
@@ -631,6 +642,25 @@ $("#icon-video-close").on("click", function() {
</div> <!-- end .wrap -->
</div> <!-- end .wear-section -->
</div> <!-- end .wear-rest-of-page -->
+
+
+ <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
+ <div class="layout-content-col col-16" style="padding-top:4px">
+ <style>#___plusone_0 {float:right !important;}</style>
+ <div class="g-plusone" data-size="medium"></div>
+ </div>
+ </div>
+ <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px">
+ <div id="copyright">
+ Except as noted, this content is
+ licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
+ Creative Commons Attribution 2.5</a>. For details and
+ restrictions, see the <a href="/license.html">Content
+ License</a>.
+ </div>
+ </div>
+
+
</div> <!-- end wear-body-content -->
<script>
@@ -641,6 +671,8 @@ $("#icon-video-close").on("click", function() {
e.preventDefault();
});
</script>
+
+
</div>
<div class="content-footer wrap"
diff --git a/docs/html/wear/notifications/pages.html b/docs/html/wear/notifications/pages.html
index abff8fa..ce568eb 100644
--- a/docs/html/wear/notifications/pages.html
+++ b/docs/html/wear/notifications/pages.html
@@ -377,7 +377,7 @@ onkeyup="return search_changed(event, false, '/')" />
<img src="/wear/images/08_pages.png" height="200" style="float:right;margin:0 0 20px 40px" />
<p>When you'd like to provide more information without requiring users
-to open your app on their phones, you can
+to open your app on their handheld device, you can
add one or more pages to the notification on Android Wear. The additional pages
appear immediately to the right of the main notification card.
For information about when to use and how to design
diff --git a/docs/html/wear/notifications/remote-input.html b/docs/html/wear/notifications/remote-input.html
index 6500233..c8f6621 100644
--- a/docs/html/wear/notifications/remote-input.html
+++ b/docs/html/wear/notifications/remote-input.html
@@ -373,20 +373,24 @@ onkeyup="return search_changed(event, false, '/')" />
<div class="jd-descr" itemprop="articleBody">
- <img src="/wear/images/13_voicereply.png" height="200" width="169" style="float:right;margin:0 0 20px 60px" />
+ <img src="/wear/images/13_voicereply.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
<img src="/wear/images/03_actions.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
<p>If your notification includes an action to respond with text,
such as to reply to an email, it should normally launch an activity
- on the handheld device. However, when your notification appears on an Android Wear device, you can
+ on the handheld device. However, when your notification appears on an Android wearable, you can
allow users to dictate a reply with voice input. You can also provide pre-defined text
- replies for the user to select.</p>
+ messages for the user to select.</p>
<p>When the user replies with voice or selects one of the available
-responses, the system delivers your app on the handheld the
-message as a string extra in the <code><a href="/reference/android/content/Intent.html">Intent</a></code> you specified
-to be used for the action.</p>
+messages, the system sends the message to your app on the connected handheld device.
+The message is attached as an extra in the <code><a href="/reference/android/content/Intent.html">Intent</a></code> you specified
+to be used for the notification action.</p>
+
+<p class="note"><strong>Note:</strong> When developing with the Android emulator,
+you must type text replies into the voice input field, so be sure you have enabled
+<strong>Hardware keyboard present</strong> in the AVD settings.</p>
<h2 id="RemoteInput">Define the Remote Input</h2>
@@ -397,10 +401,10 @@ to be used for the action.</p>
<a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html"><code>RemoteInput.Builder</code></a> APIs.
The
<a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html"><code>RemoteInput.Builder</code></a> constructor takes a string that the system
- will use as a key for the <code><a href="/reference/android/content/Intent.html">Intent</a></code> extra that caries the reply message
+ will use as a key for the <code><a href="/reference/android/content/Intent.html">Intent</a></code> extra that carries the reply message
to your app on the handheld.</p>
-<p>For example, here's a new
+<p>For example, here's how to create a new
<a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">
<code>RemoteInput</code></a> object that provides a custom
label for the voice input prompt:</p>
@@ -422,7 +426,7 @@ RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
<img src="/wear/images/12_voicereply.png" height="200" style="float:right;margin:0 0 20px 40px" />
<p>In addition to allowing voice input, you can
- provide up to five text responses the user can select for quick replies. Call
+ provide up to five text responses that the user can select for quick replies. Call
<a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html#setChoices(java.lang.String[])"><code>setChoices()</code></a> and pass it a string array.</p>
<p>For example, you may define some responses in a resource array:</p>
@@ -465,9 +469,9 @@ method), then you should attach the
<pre>
// Create intent for reply action
-Intent replyIntent = new Intent(this, ReplyService.class);
+Intent replyIntent = new Intent(this, ReplyActivity.class);
PendingIntent replyPendingIntent =
- PendingIntent.getService(this, 0, replyIntent, 0);
+ PendingIntent.getActivity(this, 0, replyIntent, 0);
// Build the notification
NotificationCompat.Builder replyNotificationBuilder =
@@ -563,8 +567,8 @@ Notification replyNotification =
.build();
</pre>
-<p>Now, when the user selects "Reply" from an Android wearable,
- the system prompts for voice input (and provides the list of pre-defined replies, if provided).
+<p>Now, when the user selects "Reply" from an Android wearable, the system prompts the user
+ for voice input (and shows the list of pre-defined replies, if provided).
Once the user completes a response, the system invokes
the <code><a href="/reference/android/content/Intent.html">Intent</a></code> attached to the action and adds the
<code>EXTRA_VOICE_REPLY</code> extra (the string
diff --git a/docs/html/wear/notifications/stacks.html b/docs/html/wear/notifications/stacks.html
index 5d10165..e4f74a0 100644
--- a/docs/html/wear/notifications/stacks.html
+++ b/docs/html/wear/notifications/stacks.html
@@ -376,15 +376,20 @@ onkeyup="return search_changed(event, false, '/')" />
<img src="/wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
<img src="/wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
-<p>When your app creates more than one notification about the same type, you should traditionally
-update the existing notification with a summary of all the notifications instead of creating multiple notifications. For instance, instead
-of three notifications for each received email, you should create one with a summary such as "3 new
-messages." To view the contents of each message, the user must then touch the notification to open
-your app.</p>
-
-<p>However, when a user is viewing your notifications on a wearable device, you can create
-a stack that collects all the notifications for immediate access without creating multiple
-cards in the card stream.</p>
+<p>When creating notifications for a handheld device, you should always aggregate similar
+notifications into a single summary notification. For example, if your app creates notifications
+for received messages, you should not show more than one notification
+on a handheld device&mdash;when more than one is message is received, use a single notification
+to provide a summary such as "2 new messages."</p>
+
+<p>However, a summary notification is less useful on an Android wearable because users
+are not able to read details from each message on the wearable (they must open your app on the
+handheld to view more information). So for the wearable device, you should
+group all the notifications together in a stack. The stack of notifications appears as a single
+card, which users can expand to view the details from each notification separately. The new
+<a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setGroup(java.lang.String, int)">
+<code>setGroup()</code></a> method makes this possible while allowing you to still provide
+only one summary notification on the handheld device.</p>
<p>For details about designing notification stacks, see the
<a href="/wear/design/index.html#NotificationStacks">Design Principles of Android
@@ -420,12 +425,10 @@ href="/reference/android/preview/support/wearable/notifications/WearableNotifica
<h2 id="AddSummary">Add a Summary Notification</h2>
-<p>It's important that you still provide a summary notification for handheld devices. So in
-addition to adding each unique notification to the same stack group, also add the summary
-notification but set its order position to be <a
-href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY"><code>GROUP_ORDER_SUMMARY</code></a>.
-The notification in this position does not appear in the stack on the wearable but
-appears as the only notification on the handheld.</p>
+<p>It's important that you still provide a summary notification that appears on handheld devices.
+So in addition to adding each unique notification to the same stack group, also add a summary
+notification, but set its order position to be <a
+href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY"><code>GROUP_ORDER_SUMMARY</code></a>.</p>
<pre>
Notification summaryNotification = new WearableNotifications.Builder(builder)
@@ -433,6 +436,8 @@ Notification summaryNotification = new WearableNotifications.Builder(builder)
.build();
</pre>
+<p>This notification will not appear in your stack of notifications on the wearable, but
+appears as the only notification on the handheld device.
</body>
</html>
diff --git a/docs/html/wear/preview/start.html b/docs/html/wear/preview/start.html
index ce51c66..4aec648 100644
--- a/docs/html/wear/preview/start.html
+++ b/docs/html/wear/preview/start.html
@@ -376,12 +376,11 @@ onkeyup="return search_changed(event, false, '/')" />
<div class="cols">
<div class="col-5">
-<p>The Android Wear Developer Preview provides tools and APIs that allow you to
+<p>The Android Wear Developer Preview includes tools and APIs that allow you to
enhance your app notifications
-to provide an optimized user experience on Android Wear.</p>
+to provide an optimized user experience on Android wearables.</p>
-<p>With the Android Wear
-Developer Preview, you can:</p>
+<p>With the Android Wear Developer Preview, you can:</p>
<ul>
<li>Run the Android Wear platform in the Android emulator.</li>
@@ -438,12 +437,12 @@ preview support library and receive access to the
</ol>
<p class="note"><strong>Note:</strong>
-If you're already using the ADT plugin for Eclipse, you must update to version 22.6.1 or higher.
-To check for updates, select <strong>Help &gt; Check for updates</strong> in the Eclipse toolbar. </p>
+If you're using the ADT plugin for Eclipse, you must update to version 22.6.1 or higher.
+If you're using Android Studio, you must update to version 0.5.1 or higher</p>
-<h2 id="Install">1. Install the Android Wear system image</h2>
+<h2 id="Install">1. Install the Android Wear System Image</h2>
<ol>
@@ -464,7 +463,9 @@ To check for updates, select <strong>Help &gt; Check for updates</strong> in the
</ol>
</li>
- <li>Below Android 4.4.2, select <strong>Android Wear ARM EABI v7a System Image</strong>.</li>
+ <li>Below Android 4.4.2, select <strong>Android Wear ARM EABI v7a System Image</strong>.
+<p class="note"><strong>Note:</strong> Android Wear is designed to support multiple processor architectures.
+</p></li>
<li>Below Extras, ensure that you have the latest version of the
<a href="/tools/support-library/index.html">Android Support Library</a>.
If an update is available, select <strong>Android Support Library</strong>. If you're using Android Studio, also select <strong>Android Support Repository</strong>.</li>
@@ -490,11 +491,13 @@ you want to create an emulator with a square or round display.</li>
<li>For the Device, select <strong>Android Wear Square</strong> or
<strong>Android Wear Round</strong>.</li>
<li>For the Target, select <strong>Android 4.4.2 - API Level 19</strong> (or higher).</li>
-<li>For the CPU/ABI, select <strong>Android Wear ARM (armeabi-v7a)</strong>.</li>
+<li>For the CPU/ABI, select <strong>Android Wear ARM (armeabi-v7a)</strong>.
+<p class="note"><strong>Note:</strong> Android Wear is designed to support multiple processor architectures.
+</p></li>
<li>For the Skin, select <strong>AndroidWearSquare</strong> or
<strong>AndroidWearRound</strong>.</li>
<li>Leave all other options set to their defaults and click <strong>OK</strong>.
- <p>Although real Android Wear devices do not provide a keyboard as an input method,
+ <p>Although real Android wearables do not provide a keyboard as an input method,
you should keep <strong>Hardware keyboard present</strong> selected so you can
provide text input on screens where users will instead provide voice input.</p>
</li>
@@ -526,8 +529,8 @@ unpredictable.</p>
<h2 id="SetupApp">3. Set Up the Android Wear Preview App</h2>
-<p>The <em>Android Wear Preview</em> app is an app you must have installed on your Android
-device (a phone or tablet) in order to deliver app notifications to the Android Wear emulator.</p>
+<p>To view your app's notifications on the Android Wear emulator, you must have the
+<em>Android Wear Preview</em> app installed on your Android device (a phone or tablet).</p>
<p>To receive the Android Wear Preview app, you must <a
href="/wear/preview/signup.html">sign up for the Developer Preview</a> using the same
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index ce6cc38..589211f 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -72,7 +72,7 @@ String8 Asset::getAssetAllocations()
}
cur = cur->mNext;
}
-
+
return res;
}
@@ -84,18 +84,18 @@ Asset::Asset(void)
mNext = mPrev = NULL;
if (gTail == NULL) {
gHead = gTail = this;
- } else {
- mPrev = gTail;
- gTail->mNext = this;
- gTail = this;
- }
+ } else {
+ mPrev = gTail;
+ gTail->mNext = this;
+ gTail = this;
+ }
//ALOGI("Creating Asset %p #%d\n", this, gCount);
}
Asset::~Asset(void)
{
AutoMutex _l(gAssetLock);
- gCount--;
+ gCount--;
if (gHead == this) {
gHead = mNext;
}
@@ -409,7 +409,7 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, siz
}
mFileName = fileName != NULL ? strdup(fileName) : NULL;
-
+
return NO_ERROR;
}
@@ -538,7 +538,7 @@ void _FileAsset::close(void)
free(mFileName);
mFileName = NULL;
}
-
+
if (mFp != NULL) {
// can only be NULL when called from destructor
// (otherwise we would never return this object)
diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp
index 1a5c55c..a5b9416 100644
--- a/libs/androidfw/BackupData.cpp
+++ b/libs/androidfw/BackupData.cpp
@@ -291,7 +291,7 @@ BackupDataReader::ReadNextHeader(bool* done, int* type)
(int)(m_pos - sizeof(m_header)), (int)m_header.type);
m_status = EINVAL;
}
-
+
return m_status;
}
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index 302fbf6..ab837ad 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -568,8 +568,8 @@ int write_tarfile(const String8& packageName, const String8& domain,
// [ 108 : 8 ] uid -- ignored in Android format; uids are remapped at restore time
// [ 116 : 8 ] gid -- ignored in Android format
- snprintf(buf + 108, 8, "0%lo", s.st_uid);
- snprintf(buf + 116, 8, "0%lo", s.st_gid);
+ snprintf(buf + 108, 8, "0%lo", (unsigned long)s.st_uid);
+ snprintf(buf + 116, 8, "0%lo", (unsigned long)s.st_gid);
// [ 124 : 12 ] file size in bytes
if (s.st_size > 077777777777LL) {
@@ -778,7 +778,7 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in)
ALOGW("Could not open file %s -- %s", filename.string(), strerror(errno));
return errno;
}
-
+
while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) {
err = write(fd, buf, amt);
if (err != amt) {
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 0f54edb..2b74a33 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -1,16 +1,16 @@
/*
* Copyright (C) 2006-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
+ * 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
+ * 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.
*/
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 98849e3..652cd4a 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1266,7 +1266,7 @@ ResXMLParser::event_code_t ResXMLParser::nextNode()
const ResXMLTree_node* next = (const ResXMLTree_node*)
(((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size));
//ALOGW("Next node: prev=%p, next=%p\n", mCurNode, next);
-
+
if (((const uint8_t*)next) >= mTree.mDataEnd) {
mCurNode = NULL;
return (mEventCode=END_DOCUMENT);
@@ -1303,7 +1303,7 @@ ResXMLParser::event_code_t ResXMLParser::nextNode()
(int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)));
continue;
}
-
+
if ((totalSize-headerSize) < minExtSize) {
ALOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n",
(int)dtohs(next->header.type),
@@ -1311,10 +1311,10 @@ ResXMLParser::event_code_t ResXMLParser::nextNode()
(int)(totalSize-headerSize), (int)minExtSize);
return (mEventCode=BAD_DOCUMENT);
}
-
+
//printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n",
// mCurNode, mCurExt, headerSize, minExtSize);
-
+
return eventCode;
} while (true);
}
@@ -2717,7 +2717,7 @@ struct ResTable::Package
delete types[i];
}
}
-
+
ResTable* const owner;
const Header* const header;
const ResTable_package* const package;
@@ -2725,7 +2725,7 @@ struct ResTable::Package
ResStringPool typeStrings;
ResStringPool keyStrings;
-
+
const Type* getType(size_t idx) const {
return idx < types.size() ? types[idx] : NULL;
}
@@ -2775,18 +2775,18 @@ struct ResTable::PackageGroup
bags = NULL;
}
}
-
+
ResTable* const owner;
String16 const name;
uint32_t const id;
Vector<Package*> packages;
-
+
// This is for finding typeStrings and other common package stuff.
Package* basePackage;
// For quick access.
size_t typeCount;
-
+
// Computed attribute bags, first indexed by the type and second
// by the entry in that type.
bag_set*** bags;
@@ -2935,7 +2935,7 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
//ALOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this);
//dumpToLog();
-
+
return NO_ERROR;
}
@@ -2944,7 +2944,7 @@ status_t ResTable::Theme::setTo(const Theme& other)
//ALOGI("Setting theme %p from theme %p...\n", this, &other);
//dumpToLog();
//other.dumpToLog();
-
+
if (&mTable == &other.mTable) {
for (size_t i=0; i<Res_MAXPACKAGE; i++) {
if (mPackages[i] != NULL) {
@@ -2974,7 +2974,7 @@ status_t ResTable::Theme::setTo(const Theme& other)
//ALOGI("Final theme:");
//dumpToLog();
-
+
return NO_ERROR;
}
@@ -2984,7 +2984,7 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
int cnt = 20;
if (outTypeSpecFlags != NULL) *outTypeSpecFlags = 0;
-
+
do {
const ssize_t p = mTable.getResourcePackageIndex(resID);
const uint32_t t = Res_GETTYPE(resID);
@@ -3058,12 +3058,12 @@ void ResTable::Theme::dumpToLog() const
for (size_t i=0; i<Res_MAXPACKAGE; i++) {
package_info* pi = mPackages[i];
if (pi == NULL) continue;
-
+
ALOGI(" Package #0x%02x:\n", (int)(i+1));
for (size_t j=0; j<pi->numTypes; j++) {
type_info& ti = pi->types[j];
if (ti.numEntries == 0) continue;
-
+
ALOGI(" Type #0x%02x:\n", (int)(j+1));
for (size_t k=0; k<ti.numEntries; k++) {
theme_entry& te = ti.entries[k];
@@ -3125,11 +3125,11 @@ status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData, const
status_t ResTable::add(ResTable* src)
{
mError = src->mError;
-
+
for (size_t i=0; i<src->mHeaders.size(); i++) {
mHeaders.add(src->mHeaders[i]);
}
-
+
for (size_t i=0; i<src->mPackageGroups.size(); i++) {
PackageGroup* srcPg = src->mPackageGroups[i];
PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id);
@@ -3140,14 +3140,14 @@ status_t ResTable::add(ResTable* src)
pg->typeCount = srcPg->typeCount;
mPackageGroups.add(pg);
}
-
+
memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap));
-
+
return mError;
}
status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie,
- Asset* asset, bool copyData, const Asset* idmap)
+ Asset* /*asset*/, bool copyData, const Asset* idmap)
{
if (!data) return NO_ERROR;
Header* header = new Header(this);
@@ -3171,7 +3171,7 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook
LOAD_TABLE_NOISY(
ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, asset=%p, copy=%d "
"idmap=%p\n", data, size, cookie, asset, copyData, idmap));
-
+
if (copyData || notDeviceEndian) {
header->ownedData = malloc(size);
if (header->ownedData == NULL) {
@@ -3503,7 +3503,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag
// are identical (diff == 0), or overlay packages will not take effect.
continue;
}
-
+
bestItem = thisConfig;
bestValue = item;
bestPackage = package;
@@ -3573,7 +3573,7 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
const char16_t* ResTable::valueToString(
const Res_value* value, size_t stringBlock,
- char16_t tmpBuffer[TMP_BUFFER_SIZE], size_t* outLen)
+ char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen)
{
if (!value) {
return NULL;
@@ -3596,7 +3596,7 @@ ssize_t ResTable::lockBag(uint32_t resID, const bag_entry** outBag) const
return err;
}
-void ResTable::unlockBag(const bag_entry* bag) const
+void ResTable::unlockBag(const bag_entry* /*bag*/) const
{
//printf("<<< unlockBag %p\n", this);
mLock.unlock();
@@ -3697,7 +3697,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
bag_set* set = NULL;
TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
-
+
ResTable_config bestConfig;
memset(&bestConfig, 0, sizeof(bestConfig));
@@ -3763,7 +3763,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0;
const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
? dtohl(((const ResTable_map_entry*)entry)->count) : 0;
-
+
size_t N = count;
TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n",
@@ -3807,7 +3807,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
} else {
set->typeSpecFlags = -1;
}
-
+
// Now merge in the new attributes...
ssize_t curOff = offset;
const ResTable_map* map;
@@ -4070,7 +4070,7 @@ nope:
TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n",
String8(group->name).string(), ti));
}
-
+
size_t NTC = typeConfigs->configs.size();
for (size_t tci=0; tci<NTC; tci++) {
const ResTable_type* const ty = typeConfigs->configs[tci];
@@ -4086,9 +4086,9 @@ nope:
if (offset == ResTable_type::NO_ENTRY) {
continue;
}
-
+
offset += typeOffset;
-
+
if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) {
ALOGW("ResTable_entry at %d is beyond type chunk data %d",
offset, dtohl(ty->header.size));
@@ -4102,7 +4102,7 @@ nope:
String8(name, nameLen).string());
return 0;
}
-
+
const ResTable_entry* const entry = (const ResTable_entry*)
(((const uint8_t*)ty) + offset);
if (dtohs(entry->size) < sizeof(*entry)) {
@@ -4259,7 +4259,7 @@ static bool parse_unit(const char* str, Res_value* outValue,
if (*realEnd != 0) {
return false;
}
-
+
const unit_entry* cur = unitNames;
while (cur->name) {
if (len == cur->len && strncmp(cur->name, str, len) == 0) {
@@ -4410,7 +4410,7 @@ bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue)
if (neg) {
mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK;
}
- outValue->data |=
+ outValue->data |=
(radix<<Res_value::COMPLEX_RADIX_SHIFT)
| (mantissa<<Res_value::COMPLEX_MANTISSA_SHIFT);
//printf("Input value: %f 0x%016Lx, mult: %f, radix: %d, shift: %d, final: 0x%08x\n",
@@ -4523,7 +4523,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString,
// Note: we don't check attrType here because the reference can
// be to any other type; we just need to count on the client making
// sure the referenced type is correct.
-
+
//printf("Looking up ref: %s\n", String8(s, len).string());
// It's a reference!
@@ -4610,7 +4610,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString,
}
}
}
-
+
if (*s == '#') {
// It's a color! Convert to an integer of the form 0xaarrggbb.
uint32_t color = 0;
@@ -4710,7 +4710,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString,
// String8(package).string(), String8(type).string(),
// String8(name).string());
uint32_t specFlags = 0;
- uint32_t rid =
+ uint32_t rid =
identifierForName(name.string(), name.size(),
type.string(), type.size(),
package.string(), package.size(), &specFlags);
@@ -4875,7 +4875,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString,
return true;
}
}
-
+
}
bag++;
cnt--;
@@ -5240,43 +5240,43 @@ ssize_t ResTable::getEntry(
entryIndex, (int)allTypes->entryCount);
return BAD_TYPE;
}
-
+
const ResTable_type* type = NULL;
uint32_t offset = ResTable_type::NO_ENTRY;
ResTable_config bestConfig;
memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up
-
+
const size_t NT = allTypes->configs.size();
for (size_t i=0; i<NT; i++) {
const ResTable_type* const thisType = allTypes->configs[i];
if (thisType == NULL) continue;
-
+
ResTable_config thisConfig;
thisConfig.copyFromDtoH(thisType->config);
TABLE_GETENTRY(ALOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n",
entryIndex, typeIndex+1, dtohl(thisType->config.size),
thisConfig.toString().string()));
-
+
// Check to make sure this one is valid for the current parameters.
if (config && !thisConfig.match(*config)) {
TABLE_GETENTRY(ALOGI("Does not match config!\n"));
continue;
}
-
+
// Check if there is the desired entry in this type.
-
+
const uint8_t* const end = ((const uint8_t*)thisType)
+ dtohl(thisType->header.size);
const uint32_t* const eindex = (const uint32_t*)
(((const uint8_t*)thisType) + dtohs(thisType->header.headerSize));
-
+
uint32_t thisOffset = dtohl(eindex[entryIndex]);
if (thisOffset == ResTable_type::NO_ENTRY) {
TABLE_GETENTRY(ALOGI("Skipping because it is not defined!\n"));
continue;
}
-
+
if (type != NULL) {
// Check if this one is less specific than the last found. If so,
// we will skip it. We check starting with things we most care
@@ -5286,19 +5286,19 @@ ssize_t ResTable::getEntry(
continue;
}
}
-
+
type = thisType;
offset = thisOffset;
bestConfig = thisConfig;
TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n"));
if (!config) break;
}
-
+
if (type == NULL) {
TABLE_GETENTRY(ALOGI("No value found for requested entry!\n"));
return BAD_INDEX;
}
-
+
offset += dtohl(type->entriesStart);
TABLE_NOISY(aout << "Looking in resource table " << package->header->header
<< ", typeOff="
@@ -5363,7 +5363,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
dtohl(pkg->keyStrings));
return (mError=BAD_TYPE);
}
-
+
Package* package = NULL;
PackageGroup* group = NULL;
uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id);
@@ -5372,12 +5372,12 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
// always loaded alongside their idmaps, but during idmap creation
// the package is temporarily loaded by itself.
if (id < 256) {
-
+
package = new Package(this, header, pkg);
if (package == NULL) {
return (mError=NO_MEMORY);
}
-
+
size_t idx = mPackageMap[id];
if (idx == 0) {
idx = mPackageGroups.size()+1;
@@ -5411,7 +5411,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
return (mError=err);
}
group->basePackage = package;
-
+
mPackageMap[id] = (uint8_t)idx;
} else {
group = mPackageGroups.itemAt(idx-1);
@@ -5428,10 +5428,10 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
return NO_ERROR;
}
-
+
// Iterate through all chunks.
size_t curPackage = 0;
-
+
const ResChunk_header* chunk =
(const ResChunk_header*)(((const uint8_t*)pkg)
+ dtohs(pkg->header.headerSize));
@@ -5450,9 +5450,9 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
if (err != NO_ERROR) {
return (mError=err);
}
-
+
const size_t typeSpecSize = dtohl(typeSpec->header.size);
-
+
LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
(void*)(base-(const uint8_t*)chunk),
dtohs(typeSpec->header.type),
@@ -5468,12 +5468,12 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
(void*)typeSpecSize);
return (mError=BAD_TYPE);
}
-
+
if (typeSpec->id == 0) {
ALOGW("ResTable_type has an id of 0.");
return (mError=BAD_TYPE);
}
-
+
while (package->types.size() < typeSpec->id) {
package->types.add(NULL);
}
@@ -5489,7 +5489,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
t->typeSpecFlags = (const uint32_t*)(
((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
t->typeSpec = typeSpec;
-
+
} else if (ctype == RES_TABLE_TYPE_TYPE) {
const ResTable_type* type = (const ResTable_type*)(chunk);
err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4,
@@ -5497,9 +5497,9 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
if (err != NO_ERROR) {
return (mError=err);
}
-
+
const uint32_t typeSize = dtohl(type->header.size);
-
+
LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n",
(void*)(base-(const uint8_t*)chunk),
dtohs(type->header.type),
@@ -5523,7 +5523,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
ALOGW("ResTable_type has an id of 0.");
return (mError=BAD_TYPE);
}
-
+
while (package->types.size() < type->id) {
package->types.add(NULL);
}
@@ -5536,7 +5536,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
(int)dtohl(type->entryCount), (int)t->entryCount);
return (mError=BAD_TYPE);
}
-
+
TABLE_GETENTRY(
ResTable_config thisConfig;
thisConfig.copyFromDtoH(type->config);
@@ -5557,7 +5557,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
if (group->typeCount == 0) {
group->typeCount = package->types.size();
}
-
+
return NO_ERROR;
}
@@ -5757,7 +5757,7 @@ static void print_complex(uint32_t complex, bool isFraction)
* RADIX_MULTS[(complex>>Res_value::COMPLEX_RADIX_SHIFT)
& Res_value::COMPLEX_RADIX_MASK];
printf("%f", value);
-
+
if (!isFraction) {
switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
case Res_value::COMPLEX_UNIT_PX: printf("px"); break;
@@ -5832,7 +5832,7 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const
} else {
printf("(string) null\n");
}
- }
+ }
} else if (value.dataType == Res_value::TYPE_FLOAT) {
printf("(float) %g\n", *(const float*)&value.data);
} else if (value.dataType == Res_value::TYPE_DIMENSION) {
@@ -5875,7 +5875,7 @@ void ResTable::print(bool inclValues) const
printf("Package Group %d id=%d packageCount=%d name=%s\n",
(int)pgIndex, pg->id, (int)pg->packages.size(),
String8(pg->name).string());
-
+
size_t pkgCount = pg->packages.size();
for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
const Package* pkg = pg->packages[pkgIndex];
@@ -5942,17 +5942,17 @@ void ResTable::print(bool inclValues) const
continue;
}
for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
-
+
const uint8_t* const end = ((const uint8_t*)type)
+ dtohl(type->header.size);
const uint32_t* const eindex = (const uint32_t*)
(((const uint8_t*)type) + dtohs(type->header.headerSize));
-
+
uint32_t thisOffset = dtohl(eindex[entryIndex]);
if (thisOffset == ResTable_type::NO_ENTRY) {
continue;
}
-
+
uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
| (0x00ff0000 & ((typeIndex+1)<<16))
| (0x0000ffff & (entryIndex));
@@ -5985,7 +5985,7 @@ void ResTable::print(bool inclValues) const
entriesStart, thisOffset, typeSize);
continue;
}
-
+
const ResTable_entry* ent = (const ResTable_entry*)
(((const uint8_t*)type) + entriesStart + thisOffset);
if (((entriesStart + thisOffset)&0x3) != 0) {
@@ -5993,7 +5993,7 @@ void ResTable::print(bool inclValues) const
(entriesStart + thisOffset));
continue;
}
-
+
uintptr_t esize = dtohs(ent->size);
if ((esize&0x3) != 0) {
printf("NON-INTEGER ResTable_entry SIZE: 0x%x\n", esize);
@@ -6004,7 +6004,7 @@ void ResTable::print(bool inclValues) const
entriesStart, thisOffset, esize, typeSize);
continue;
}
-
+
const Res_value* valuePtr = NULL;
const ResTable_map_entry* bagPtr = NULL;
Res_value value;
@@ -6019,12 +6019,12 @@ void ResTable::print(bool inclValues) const
(int)value.dataType, (int)value.data,
(int)value.size, (int)value.res0);
}
-
+
if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
printf(" (PUBLIC)");
}
printf("\n");
-
+
if (inclValues) {
if (valuePtr != NULL) {
printf(" ");
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index e9ac2fe..6fa0f14 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -127,7 +127,7 @@ static const unsigned long kReadBufSize = 32768;
goto z_bail;
}
- /* output buffer holds all, so no need to write the output */
+ /* output buffer holds all, so no need to write the output */
} while (zerr == Z_OK);
assert(zerr == Z_STREAM_END); /* other errors should've been caught */
@@ -197,7 +197,7 @@ public:
{
}
- long read(unsigned char** nextBuffer, long readSize) {
+ long read(unsigned char** nextBuffer, long /*readSize*/) {
if (!mBufferReturned) {
mBufferReturned = true;
*nextBuffer = mInput;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index eeff4c0..4de755d 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -38,6 +38,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
Program.cpp \
ProgramCache.cpp \
RenderBufferCache.cpp \
+ RenderNode.cpp \
RenderProperties.cpp \
ResourceCache.cpp \
ShadowTessellator.cpp \
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index d124cde..cf745ee 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -22,10 +22,10 @@
#include <SkMatrix.h>
#include <utils/StrongPointer.h>
-#include "DisplayList.h"
#include "Layer.h"
#include "OpenGLRenderer.h"
#include "Rect.h"
+#include "RenderNode.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index f4de8ec..9e6a96d 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -29,543 +29,6 @@
namespace android {
namespace uirenderer {
-void RenderNode::outputLogBuffer(int fd) {
- DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
- if (logBuffer.isEmpty()) {
- return;
- }
-
- FILE *file = fdopen(fd, "a");
-
- fprintf(file, "\nRecent DisplayList operations\n");
- logBuffer.outputCommands(file);
-
- String8 cachesLog;
- Caches::getInstance().dumpMemoryUsage(cachesLog);
- fprintf(file, "\nCaches:\n%s", cachesLog.string());
- fprintf(file, "\n");
-
- fflush(file);
-}
-
-RenderNode::RenderNode() : mDestroyed(false), mDisplayListData(0) {
-}
-
-RenderNode::~RenderNode() {
- LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this);
-
- mDestroyed = true;
- delete mDisplayListData;
-}
-
-void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) {
- if (displayList) {
- DISPLAY_LIST_LOGD("Deferring display list destruction");
- Caches::getInstance().deleteDisplayListDeferred(displayList);
- }
-}
-
-void RenderNode::setData(DisplayListData* data) {
- delete mDisplayListData;
- mDisplayListData = data;
- if (mDisplayListData) {
- Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
- }
-}
-
-/**
- * This function is a simplified version of replay(), where we simply retrieve and log the
- * display list. This function should remain in sync with the replay() function.
- */
-void RenderNode::output(uint32_t level) {
- ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
- mName.string(), isRenderable());
- ALOGD("%*s%s %d", level * 2, "", "Save",
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
-
- outputViewProperties(level);
- int flags = DisplayListOp::kOpLogFlag_Recurse;
- for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
- mDisplayListData->displayListOps[i]->output(level, flags);
- }
-
- ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
-}
-
-void RenderNode::outputViewProperties(const int level) {
- properties().updateMatrix();
- if (properties().mLeft != 0 || properties().mTop != 0) {
- ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", properties().mLeft, properties().mTop);
- }
- if (properties().mStaticMatrix) {
- ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING,
- level * 2, "", properties().mStaticMatrix, SK_MATRIX_ARGS(properties().mStaticMatrix));
- }
- if (properties().mAnimationMatrix) {
- ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING,
- level * 2, "", properties().mAnimationMatrix, SK_MATRIX_ARGS(properties().mAnimationMatrix));
- }
- if (properties().mMatrixFlags != 0) {
- if (properties().mMatrixFlags == TRANSLATION) {
- ALOGD("%*sTranslate %.2f, %.2f, %.2f",
- level * 2, "", properties().mTranslationX, properties().mTranslationY, properties().mTranslationZ);
- } else {
- ALOGD("%*sConcatMatrix %p: " MATRIX_4_STRING,
- level * 2, "", properties().mTransformMatrix, MATRIX_4_ARGS(properties().mTransformMatrix));
- }
- }
-
- bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
- if (properties().mAlpha < 1) {
- if (properties().mCaching) {
- ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", properties().mAlpha);
- } else if (!properties().mHasOverlappingRendering) {
- ALOGD("%*sScaleAlpha %.2f", level * 2, "", properties().mAlpha);
- } else {
- int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
- if (clipToBoundsNeeded) {
- flags |= SkCanvas::kClipToLayer_SaveFlag;
- clipToBoundsNeeded = false; // clipping done by save layer
- }
- ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
- (float) 0, (float) 0, (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop,
- (int)(properties().mAlpha * 255), flags);
- }
- }
- if (clipToBoundsNeeded) {
- ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
- (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop);
- }
-}
-
-/*
- * For property operations, we pass a savecount of 0, since the operations aren't part of the
- * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
- * base saveCount (i.e., how RestoreToCount uses saveCount + properties().mCount)
- */
-#define PROPERTY_SAVECOUNT 0
-
-template <class T>
-void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler,
- const int level) {
-#if DEBUG_DISPLAY_LIST
- outputViewProperties(level);
-#endif
- properties().updateMatrix();
- if (properties().mLeft != 0 || properties().mTop != 0) {
- renderer.translate(properties().mLeft, properties().mTop);
- }
- if (properties().mStaticMatrix) {
- renderer.concatMatrix(properties().mStaticMatrix);
- } else if (properties().mAnimationMatrix) {
- renderer.concatMatrix(properties().mAnimationMatrix);
- }
- if (properties().mMatrixFlags != 0) {
- if (properties().mMatrixFlags == TRANSLATION) {
- renderer.translate(properties().mTranslationX, properties().mTranslationY);
- } else {
- renderer.concatMatrix(*properties().mTransformMatrix);
- }
- }
- bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
- if (properties().mAlpha < 1) {
- if (properties().mCaching) {
- renderer.setOverrideLayerAlpha(properties().mAlpha);
- } else if (!properties().mHasOverlappingRendering) {
- renderer.scaleAlpha(properties().mAlpha);
- } else {
- // TODO: should be able to store the size of a DL at record time and not
- // have to pass it into this call. In fact, this information might be in the
- // location/size info that we store with the new native transform data.
- int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
- if (clipToBoundsNeeded) {
- saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
- clipToBoundsNeeded = false; // clipping done by saveLayer
- }
-
- SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
- 0, 0, properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, properties().mAlpha * 255, saveFlags);
- handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
- }
- }
- if (clipToBoundsNeeded) {
- ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
- properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, SkRegion::kIntersect_Op);
- handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
- }
- if (CC_UNLIKELY(properties().mClipToOutline && !properties().mOutline.isEmpty())) {
- ClipPathOp* op = new (handler.allocator()) ClipPathOp(&properties().mOutline, SkRegion::kIntersect_Op);
- handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
- }
-}
-
-/**
- * Apply property-based transformations to input matrix
- *
- * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
- * matrix computation instead of the Skia 3x3 matrix + camera hackery.
- */
-void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
- if (properties().mLeft != 0 || properties().mTop != 0) {
- matrix.translate(properties().mLeft, properties().mTop);
- }
- if (properties().mStaticMatrix) {
- mat4 stat(*properties().mStaticMatrix);
- matrix.multiply(stat);
- } else if (properties().mAnimationMatrix) {
- mat4 anim(*properties().mAnimationMatrix);
- matrix.multiply(anim);
- }
- if (properties().mMatrixFlags != 0) {
- properties().updateMatrix();
- if (properties().mMatrixFlags == TRANSLATION) {
- matrix.translate(properties().mTranslationX, properties().mTranslationY,
- true3dTransform ? properties().mTranslationZ : 0.0f);
- } else {
- if (!true3dTransform) {
- matrix.multiply(*properties().mTransformMatrix);
- } else {
- mat4 true3dMat;
- true3dMat.loadTranslate(
- properties().mPivotX + properties().mTranslationX,
- properties().mPivotY + properties().mTranslationY,
- properties().mTranslationZ);
- true3dMat.rotate(properties().mRotationX, 1, 0, 0);
- true3dMat.rotate(properties().mRotationY, 0, 1, 0);
- true3dMat.rotate(properties().mRotation, 0, 0, 1);
- true3dMat.scale(properties().mScaleX, properties().mScaleY, 1);
- true3dMat.translate(-properties().mPivotX, -properties().mPivotY);
-
- matrix.multiply(true3dMat);
- }
- }
- }
-}
-
-/**
- * Organizes the DisplayList hierarchy to prepare for background projection reordering.
- *
- * This should be called before a call to defer() or drawDisplayList()
- *
- * Each DisplayList that serves as a 3d root builds its list of composited children,
- * which are flagged to not draw in the standard draw loop.
- */
-void RenderNode::computeOrdering() {
- ATRACE_CALL();
- mProjectedNodes.clear();
-
- // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
- // transform properties are applied correctly to top level children
- if (mDisplayListData == NULL) return;
- for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
- DrawDisplayListOp* childOp = mDisplayListData->children[i];
- childOp->mDisplayList->computeOrderingImpl(childOp,
- &mProjectedNodes, &mat4::identity());
- }
-}
-
-void RenderNode::computeOrderingImpl(
- DrawDisplayListOp* opState,
- Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
- const mat4* transformFromProjectionSurface) {
- mProjectedNodes.clear();
- if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
-
- // TODO: should avoid this calculation in most cases
- // TODO: just calculate single matrix, down to all leaf composited elements
- Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
- localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
-
- if (properties().mProjectBackwards) {
- // composited projectee, flag for out of order draw, save matrix, and store in proj surface
- opState->mSkipInOrderDraw = true;
- opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
- compositedChildrenOfProjectionSurface->add(opState);
- } else {
- // standard in order draw
- opState->mSkipInOrderDraw = false;
- }
-
- if (mDisplayListData->children.size() > 0) {
- const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
- bool haveAppliedPropertiesToProjection = false;
- for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
- DrawDisplayListOp* childOp = mDisplayListData->children[i];
- RenderNode* child = childOp->mDisplayList;
-
- Vector<DrawDisplayListOp*>* projectionChildren = NULL;
- const mat4* projectionTransform = NULL;
- if (isProjectionReceiver && !child->properties().mProjectBackwards) {
- // if receiving projections, collect projecting descendent
-
- // Note that if a direct descendent is projecting backwards, we pass it's
- // grandparent projection collection, since it shouldn't project onto it's
- // parent, where it will already be drawing.
- projectionChildren = &mProjectedNodes;
- projectionTransform = &mat4::identity();
- } else {
- if (!haveAppliedPropertiesToProjection) {
- applyViewPropertyTransforms(localTransformFromProjectionSurface);
- haveAppliedPropertiesToProjection = true;
- }
- projectionChildren = compositedChildrenOfProjectionSurface;
- projectionTransform = &localTransformFromProjectionSurface;
- }
- child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
- }
- }
-
-}
-
-class DeferOperationHandler {
-public:
- DeferOperationHandler(DeferStateStruct& deferStruct, int level)
- : mDeferStruct(deferStruct), mLevel(level) {}
- inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
- operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
- }
- inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
-
-private:
- DeferStateStruct& mDeferStruct;
- const int mLevel;
-};
-
-void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
- DeferOperationHandler handler(deferStruct, level);
- iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
-}
-
-class ReplayOperationHandler {
-public:
- ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
- : mReplayStruct(replayStruct), mLevel(level) {}
- inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
-#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
- properties().mReplayStruct.mRenderer.eventMark(operation->name());
-#endif
- operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
- }
- inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
-
-private:
- ReplayStateStruct& mReplayStruct;
- const int mLevel;
-};
-
-void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
- ReplayOperationHandler handler(replayStruct, level);
-
- replayStruct.mRenderer.startMark(mName.string());
- iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
- replayStruct.mRenderer.endMark();
-
- DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
- replayStruct.mDrawGlStatus);
-}
-
-void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
- if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
-
- for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
- DrawDisplayListOp* childOp = mDisplayListData->children[i];
- RenderNode* child = childOp->mDisplayList;
- float childZ = child->properties().mTranslationZ;
-
- if (childZ != 0.0f) {
- zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
- childOp->mSkipInOrderDraw = true;
- } else if (!child->properties().mProjectBackwards) {
- // regular, in order drawing DisplayList
- childOp->mSkipInOrderDraw = false;
- }
- }
-
- // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
- std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
-}
-
-#define SHADOW_DELTA 0.1f
-
-template <class T>
-void RenderNode::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
- ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
- const int size = zTranslatedNodes.size();
- if (size == 0
- || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
- || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
- // no 3d children to draw
- return;
- }
-
- int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- LinearAllocator& alloc = handler.allocator();
- ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
- SkRegion::kIntersect_Op); // clip to 3d root bounds
- handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-
- /**
- * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
- * with very similar Z heights to draw together.
- *
- * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
- * underneath both, and neither's shadow is drawn on top of the other.
- */
- const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
- size_t drawIndex, shadowIndex, endIndex;
- if (mode == kNegativeZChildren) {
- drawIndex = 0;
- endIndex = nonNegativeIndex;
- shadowIndex = endIndex; // draw no shadows
- } else {
- drawIndex = nonNegativeIndex;
- endIndex = size;
- shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
- }
- float lastCasterZ = 0.0f;
- while (shadowIndex < endIndex || drawIndex < endIndex) {
- if (shadowIndex < endIndex) {
- DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
- RenderNode* caster = casterOp->mDisplayList;
- const float casterZ = zTranslatedNodes[shadowIndex].key;
- // attempt to render the shadow if the caster about to be drawn is its caster,
- // OR if its caster's Z value is similar to the previous potential caster
- if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
-
- if (caster->properties().mAlpha > 0.0f) {
- mat4 shadowMatrixXY(casterOp->mTransformFromParent);
- caster->applyViewPropertyTransforms(shadowMatrixXY);
-
- // Z matrix needs actual 3d transformation, so mapped z values will be correct
- mat4 shadowMatrixZ(casterOp->mTransformFromParent);
- caster->applyViewPropertyTransforms(shadowMatrixZ, true);
-
- DisplayListOp* shadowOp = new (alloc) DrawShadowOp(
- shadowMatrixXY, shadowMatrixZ,
- caster->properties().mAlpha, &(caster->properties().mOutline),
- caster->properties().mWidth, caster->properties().mHeight);
- handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
- }
-
- lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
- shadowIndex++;
- continue;
- }
- }
-
- // only the actual child DL draw needs to be in save/restore,
- // since it modifies the renderer's matrix
- int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
-
- DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
- RenderNode* child = childOp->mDisplayList;
-
- renderer.concatMatrix(childOp->mTransformFromParent);
- childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
- handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
- childOp->mSkipInOrderDraw = true;
-
- renderer.restoreToCount(restoreTo);
- drawIndex++;
- }
- handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
-}
-
-template <class T>
-void RenderNode::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) {
- int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- LinearAllocator& alloc = handler.allocator();
- ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
- SkRegion::kReplace_Op); // clip to projection surface root bounds
- handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-
- for (size_t i = 0; i < mProjectedNodes.size(); i++) {
- DrawDisplayListOp* childOp = mProjectedNodes[i];
-
- // matrix save, concat, and restore can be done safely without allocating operations
- int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
- renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
- childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
- handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
- childOp->mSkipInOrderDraw = true;
- renderer.restoreToCount(restoreTo);
- }
- handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
-}
-
-/**
- * This function serves both defer and replay modes, and will organize the displayList's component
- * operations for a single frame:
- *
- * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
- * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
- * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
- * defer vs replay logic, per operation
- */
-template <class T>
-void RenderNode::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
- if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
- ALOGW("Error: %s is drawing after destruction", mName.string());
- CRASH();
- }
- if (mDisplayListData->isEmpty() || properties().mAlpha <= 0) {
- DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
- return;
- }
-
-#if DEBUG_DISPLAY_LIST
- Rect* clipRect = renderer.getClipRect();
- DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
- level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
- clipRect->right, clipRect->bottom);
-#endif
-
- LinearAllocator& alloc = handler.allocator();
- int restoreTo = renderer.getSaveCount();
- handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
- PROPERTY_SAVECOUNT, properties().mClipToBounds);
-
- DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
-
- setViewProperties<T>(renderer, handler, level + 1);
-
- bool quickRejected = properties().mClipToBounds && renderer.quickRejectConservative(0, 0, properties().mWidth, properties().mHeight);
- if (!quickRejected) {
- Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
- buildZSortedChildList(zTranslatedNodes);
-
- // for 3d root, draw children with negative z values
- iterate3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
-
- DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
- const int saveCountOffset = renderer.getSaveCount() - 1;
- const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
- for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
- DisplayListOp *op = mDisplayListData->displayListOps[i];
-
-#if DEBUG_DISPLAY_LIST
- op->output(level + 1);
-#endif
-
- logBuffer.writeCommand(level, op->name());
- handler(op, saveCountOffset, properties().mClipToBounds);
-
- if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
- iterateProjectedChildren(renderer, handler, level);
- }
- }
-
- // for 3d root, draw children with positive z values
- iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
- }
-
- DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
- handler(new (alloc) RestoreToCountOp(restoreTo),
- PROPERTY_SAVECOUNT, properties().mClipToBounds);
- renderer.setOverrideLayerAlpha(1.0f);
-}
-
void DisplayListData::cleanupResources() {
Caches& caches = Caches::getInstance();
caches.unregisterFunctors(functorCount);
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index b80c118..df5cba6 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -143,136 +143,6 @@ private:
void cleanupResources();
};
-/**
- * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
- *
- * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
- * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData
- * (which holds the actual data), and DisplayList (which holds properties and performs playback onto
- * a renderer).
- *
- * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's
- * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay
- * attached.
- */
-class RenderNode {
-public:
- ANDROID_API RenderNode();
- ANDROID_API ~RenderNode();
-
- // See flags defined in DisplayList.java
- enum ReplayFlag {
- kReplayFlag_ClipChildren = 0x1
- };
-
- ANDROID_API static void destroyDisplayListDeferred(RenderNode* displayList);
- ANDROID_API static void outputLogBuffer(int fd);
-
- ANDROID_API void setData(DisplayListData* newData);
-
- void computeOrdering();
- void defer(DeferStateStruct& deferStruct, const int level);
- void replay(ReplayStateStruct& replayStruct, const int level);
-
- ANDROID_API void output(uint32_t level = 1);
-
- bool isRenderable() const {
- return mDisplayListData && mDisplayListData->hasDrawOps;
- }
-
- void setName(const char* name) {
- if (name) {
- char* lastPeriod = strrchr(name, '.');
- if (lastPeriod) {
- mName.setTo(lastPeriod + 1);
- } else {
- mName.setTo(name);
- }
- }
- }
-
- RenderProperties& properties() {
- return mProperties;
- }
-
- bool isProjectionReceiver() {
- return properties().isProjectionReceiver();
- }
-
- int getWidth() {
- return properties().getWidth();
- }
-
- int getHeight() {
- return properties().getHeight();
- }
-
-private:
- typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
-
- static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) {
- for (size_t i = 0; i < nodes.size(); i++) {
- if (nodes[i].key >= 0.0f) return i;
- }
- return nodes.size();
- }
-
- enum ChildrenSelectMode {
- kNegativeZChildren,
- kPositiveZChildren
- };
-
- void outputViewProperties(const int level);
-
- void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
-
- void computeOrderingImpl(DrawDisplayListOp* opState,
- Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
- const mat4* transformFromProjectionSurface);
-
- template <class T>
- inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level);
-
- void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
-
- template <class T>
- inline void iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
- ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
-
- template <class T>
- inline void iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level);
-
- template <class T>
- inline void iterate(OpenGLRenderer& renderer, T& handler, const int level);
-
- class TextContainer {
- public:
- size_t length() const {
- return mByteLength;
- }
-
- const char* text() const {
- return (const char*) mText;
- }
-
- size_t mByteLength;
- const char* mText;
- };
-
- String8 mName;
- bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed
-
- RenderProperties mProperties;
- DisplayListData* mDisplayListData;
-
- /**
- * Draw time state - these properties are only set and used during rendering
- */
-
- // for projection surfaces, contains a list of all children items
- Vector<DrawDisplayListOp*> mProjectedNodes;
-}; // class DisplayList
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index e69e08e..78c97e1 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -21,12 +21,12 @@
#include <private/hwui/DrawGlInfo.h>
-#include "DisplayList.h"
+#include "Caches.h"
#include "DeferredDisplayList.h"
#include "DisplayListLogBuffer.h"
#include "DisplayListOp.h"
#include "DisplayListRenderer.h"
-#include "Caches.h"
+#include "RenderNode.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 65498a5..04c5a73 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -22,9 +22,9 @@
#include <SkPath.h>
#include <cutils/compiler.h>
-#include "DisplayList.h"
#include "DisplayListLogBuffer.h"
#include "OpenGLRenderer.h"
+#include "RenderNode.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 52176d4..bd9bfe9 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -18,12 +18,12 @@
#include <utils/Log.h>
-#include "DisplayList.h"
+#include "Caches.h"
#include "DeferredDisplayList.h"
#include "Layer.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
-#include "Caches.h"
+#include "RenderNode.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index cb8155b..cdd789d 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -3226,11 +3226,13 @@ status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& c
const int casterVertexCount = casterVertices2d.size();
Vector3 casterPolygon[casterVertexCount];
float minZ = FLT_MAX;
+ float maxZ = -FLT_MAX;
for (int i = 0; i < casterVertexCount; i++) {
const Vertex& point2d = casterVertices2d[i];
casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
minZ = fmin(minZ, casterPolygon[i].z);
+ maxZ = fmax(maxZ, casterPolygon[i].z);
}
// map the centroid of the caster into 3d
@@ -3248,6 +3250,15 @@ status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& c
}
centroid3d.z += casterLift;
}
+
+ // Check whether we want to draw the shadow at all by checking the caster's
+ // bounds against clip.
+ // We only have ortho projection, so we can just ignore the Z in caster for
+ // simple rejection calculation.
+ Rect localClip = mSnapshot->getLocalClip();
+ Rect casterBounds(casterOutline->getBounds());
+ casterTransformXY.mapRect(casterBounds);
+
bool isCasterOpaque = (casterAlpha == 1.0f);
// draw caster's shadows
if (mCaches.propertyAmbientShadowStrength > 0) {
@@ -3255,7 +3266,7 @@ status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& c
VertexBuffer ambientShadowVertexBuffer;
VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateAmbientShadow(
isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
- ambientShadowVertexBuffer);
+ casterBounds, localClip, maxZ, ambientShadowVertexBuffer);
drawVertexBuffer(vertexBufferMode, ambientShadowVertexBuffer, &paint);
}
@@ -3266,7 +3277,8 @@ status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& c
mCaches.propertyLightPosYScale, mCaches.propertyLightPosZScale);
VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateSpotShadow(
isCasterOpaque, casterPolygon, casterVertexCount, lightPosScale,
- *currentTransform(), getWidth(), getHeight(), spotShadowVertexBuffer);
+ *currentTransform(), getWidth(), getHeight(), casterBounds, localClip,
+ spotShadowVertexBuffer);
drawVertexBuffer(vertexBufferMode, spotShadowVertexBuffer, &paint);
}
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index c230149..0083b77 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -18,6 +18,7 @@
#define ANDROID_HWUI_RECT_H
#include <cmath>
+#include <SkRect.h>
#include <utils/Log.h>
@@ -68,6 +69,13 @@ public:
bottom(height) {
}
+ inline Rect(const SkRect& rect):
+ left(rect.fLeft),
+ top(rect.fTop),
+ right(rect.fRight),
+ bottom(rect.fBottom) {
+ }
+
friend int operator==(const Rect& a, const Rect& b) {
return !memcmp(&a, &b, sizeof(a));
}
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
new file mode 100644
index 0000000..e371590
--- /dev/null
+++ b/libs/hwui/RenderNode.cpp
@@ -0,0 +1,571 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include "RenderNode.h"
+
+#include <SkCanvas.h>
+#include <algorithm>
+
+#include <utils/Trace.h>
+
+#include "Debug.h"
+#include "DisplayListOp.h"
+#include "DisplayListLogBuffer.h"
+
+namespace android {
+namespace uirenderer {
+
+void RenderNode::outputLogBuffer(int fd) {
+ DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+ if (logBuffer.isEmpty()) {
+ return;
+ }
+
+ FILE *file = fdopen(fd, "a");
+
+ fprintf(file, "\nRecent DisplayList operations\n");
+ logBuffer.outputCommands(file);
+
+ String8 cachesLog;
+ Caches::getInstance().dumpMemoryUsage(cachesLog);
+ fprintf(file, "\nCaches:\n%s", cachesLog.string());
+ fprintf(file, "\n");
+
+ fflush(file);
+}
+
+RenderNode::RenderNode() : mDestroyed(false), mDisplayListData(0) {
+}
+
+RenderNode::~RenderNode() {
+ LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this);
+
+ mDestroyed = true;
+ delete mDisplayListData;
+}
+
+void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) {
+ if (displayList) {
+ DISPLAY_LIST_LOGD("Deferring display list destruction");
+ Caches::getInstance().deleteDisplayListDeferred(displayList);
+ }
+}
+
+void RenderNode::setData(DisplayListData* data) {
+ delete mDisplayListData;
+ mDisplayListData = data;
+ if (mDisplayListData) {
+ Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
+ }
+}
+
+/**
+ * This function is a simplified version of replay(), where we simply retrieve and log the
+ * display list. This function should remain in sync with the replay() function.
+ */
+void RenderNode::output(uint32_t level) {
+ ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
+ mName.string(), isRenderable());
+ ALOGD("%*s%s %d", level * 2, "", "Save",
+ SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+
+ outputViewProperties(level);
+ int flags = DisplayListOp::kOpLogFlag_Recurse;
+ for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+ mDisplayListData->displayListOps[i]->output(level, flags);
+ }
+
+ ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
+}
+
+void RenderNode::outputViewProperties(const int level) {
+ properties().updateMatrix();
+ if (properties().mLeft != 0 || properties().mTop != 0) {
+ ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", properties().mLeft, properties().mTop);
+ }
+ if (properties().mStaticMatrix) {
+ ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING,
+ level * 2, "", properties().mStaticMatrix, SK_MATRIX_ARGS(properties().mStaticMatrix));
+ }
+ if (properties().mAnimationMatrix) {
+ ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING,
+ level * 2, "", properties().mAnimationMatrix, SK_MATRIX_ARGS(properties().mAnimationMatrix));
+ }
+ if (properties().mMatrixFlags != 0) {
+ if (properties().mMatrixFlags == TRANSLATION) {
+ ALOGD("%*sTranslate %.2f, %.2f, %.2f",
+ level * 2, "", properties().mTranslationX, properties().mTranslationY, properties().mTranslationZ);
+ } else {
+ ALOGD("%*sConcatMatrix %p: " MATRIX_4_STRING,
+ level * 2, "", properties().mTransformMatrix, MATRIX_4_ARGS(properties().mTransformMatrix));
+ }
+ }
+
+ bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
+ if (properties().mAlpha < 1) {
+ if (properties().mCaching) {
+ ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", properties().mAlpha);
+ } else if (!properties().mHasOverlappingRendering) {
+ ALOGD("%*sScaleAlpha %.2f", level * 2, "", properties().mAlpha);
+ } else {
+ int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
+ if (clipToBoundsNeeded) {
+ flags |= SkCanvas::kClipToLayer_SaveFlag;
+ clipToBoundsNeeded = false; // clipping done by save layer
+ }
+ ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
+ (float) 0, (float) 0, (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop,
+ (int)(properties().mAlpha * 255), flags);
+ }
+ }
+ if (clipToBoundsNeeded) {
+ ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
+ (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop);
+ }
+}
+
+/*
+ * For property operations, we pass a savecount of 0, since the operations aren't part of the
+ * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
+ * base saveCount (i.e., how RestoreToCount uses saveCount + properties().mCount)
+ */
+#define PROPERTY_SAVECOUNT 0
+
+template <class T>
+void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler,
+ const int level) {
+#if DEBUG_DISPLAY_LIST
+ outputViewProperties(level);
+#endif
+ properties().updateMatrix();
+ if (properties().mLeft != 0 || properties().mTop != 0) {
+ renderer.translate(properties().mLeft, properties().mTop);
+ }
+ if (properties().mStaticMatrix) {
+ renderer.concatMatrix(properties().mStaticMatrix);
+ } else if (properties().mAnimationMatrix) {
+ renderer.concatMatrix(properties().mAnimationMatrix);
+ }
+ if (properties().mMatrixFlags != 0) {
+ if (properties().mMatrixFlags == TRANSLATION) {
+ renderer.translate(properties().mTranslationX, properties().mTranslationY);
+ } else {
+ renderer.concatMatrix(*properties().mTransformMatrix);
+ }
+ }
+ bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
+ if (properties().mAlpha < 1) {
+ if (properties().mCaching) {
+ renderer.setOverrideLayerAlpha(properties().mAlpha);
+ } else if (!properties().mHasOverlappingRendering) {
+ renderer.scaleAlpha(properties().mAlpha);
+ } else {
+ // TODO: should be able to store the size of a DL at record time and not
+ // have to pass it into this call. In fact, this information might be in the
+ // location/size info that we store with the new native transform data.
+ int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
+ if (clipToBoundsNeeded) {
+ saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
+ clipToBoundsNeeded = false; // clipping done by saveLayer
+ }
+
+ SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
+ 0, 0, properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, properties().mAlpha * 255, saveFlags);
+ handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+ }
+ }
+ if (clipToBoundsNeeded) {
+ ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
+ properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, SkRegion::kIntersect_Op);
+ handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+ }
+ if (CC_UNLIKELY(properties().mClipToOutline && !properties().mOutline.isEmpty())) {
+ ClipPathOp* op = new (handler.allocator()) ClipPathOp(&properties().mOutline, SkRegion::kIntersect_Op);
+ handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+ }
+}
+
+/**
+ * Apply property-based transformations to input matrix
+ *
+ * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
+ * matrix computation instead of the Skia 3x3 matrix + camera hackery.
+ */
+void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
+ if (properties().mLeft != 0 || properties().mTop != 0) {
+ matrix.translate(properties().mLeft, properties().mTop);
+ }
+ if (properties().mStaticMatrix) {
+ mat4 stat(*properties().mStaticMatrix);
+ matrix.multiply(stat);
+ } else if (properties().mAnimationMatrix) {
+ mat4 anim(*properties().mAnimationMatrix);
+ matrix.multiply(anim);
+ }
+ if (properties().mMatrixFlags != 0) {
+ properties().updateMatrix();
+ if (properties().mMatrixFlags == TRANSLATION) {
+ matrix.translate(properties().mTranslationX, properties().mTranslationY,
+ true3dTransform ? properties().mTranslationZ : 0.0f);
+ } else {
+ if (!true3dTransform) {
+ matrix.multiply(*properties().mTransformMatrix);
+ } else {
+ mat4 true3dMat;
+ true3dMat.loadTranslate(
+ properties().mPivotX + properties().mTranslationX,
+ properties().mPivotY + properties().mTranslationY,
+ properties().mTranslationZ);
+ true3dMat.rotate(properties().mRotationX, 1, 0, 0);
+ true3dMat.rotate(properties().mRotationY, 0, 1, 0);
+ true3dMat.rotate(properties().mRotation, 0, 0, 1);
+ true3dMat.scale(properties().mScaleX, properties().mScaleY, 1);
+ true3dMat.translate(-properties().mPivotX, -properties().mPivotY);
+
+ matrix.multiply(true3dMat);
+ }
+ }
+ }
+}
+
+/**
+ * Organizes the DisplayList hierarchy to prepare for background projection reordering.
+ *
+ * This should be called before a call to defer() or drawDisplayList()
+ *
+ * Each DisplayList that serves as a 3d root builds its list of composited children,
+ * which are flagged to not draw in the standard draw loop.
+ */
+void RenderNode::computeOrdering() {
+ ATRACE_CALL();
+ mProjectedNodes.clear();
+
+ // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
+ // transform properties are applied correctly to top level children
+ if (mDisplayListData == NULL) return;
+ for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+ DrawDisplayListOp* childOp = mDisplayListData->children[i];
+ childOp->mDisplayList->computeOrderingImpl(childOp,
+ &mProjectedNodes, &mat4::identity());
+ }
+}
+
+void RenderNode::computeOrderingImpl(
+ DrawDisplayListOp* opState,
+ Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
+ const mat4* transformFromProjectionSurface) {
+ mProjectedNodes.clear();
+ if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
+
+ // TODO: should avoid this calculation in most cases
+ // TODO: just calculate single matrix, down to all leaf composited elements
+ Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
+ localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
+
+ if (properties().mProjectBackwards) {
+ // composited projectee, flag for out of order draw, save matrix, and store in proj surface
+ opState->mSkipInOrderDraw = true;
+ opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
+ compositedChildrenOfProjectionSurface->add(opState);
+ } else {
+ // standard in order draw
+ opState->mSkipInOrderDraw = false;
+ }
+
+ if (mDisplayListData->children.size() > 0) {
+ const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
+ bool haveAppliedPropertiesToProjection = false;
+ for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+ DrawDisplayListOp* childOp = mDisplayListData->children[i];
+ RenderNode* child = childOp->mDisplayList;
+
+ Vector<DrawDisplayListOp*>* projectionChildren = NULL;
+ const mat4* projectionTransform = NULL;
+ if (isProjectionReceiver && !child->properties().mProjectBackwards) {
+ // if receiving projections, collect projecting descendent
+
+ // Note that if a direct descendent is projecting backwards, we pass it's
+ // grandparent projection collection, since it shouldn't project onto it's
+ // parent, where it will already be drawing.
+ projectionChildren = &mProjectedNodes;
+ projectionTransform = &mat4::identity();
+ } else {
+ if (!haveAppliedPropertiesToProjection) {
+ applyViewPropertyTransforms(localTransformFromProjectionSurface);
+ haveAppliedPropertiesToProjection = true;
+ }
+ projectionChildren = compositedChildrenOfProjectionSurface;
+ projectionTransform = &localTransformFromProjectionSurface;
+ }
+ child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
+ }
+ }
+
+}
+
+class DeferOperationHandler {
+public:
+ DeferOperationHandler(DeferStateStruct& deferStruct, int level)
+ : mDeferStruct(deferStruct), mLevel(level) {}
+ inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
+ operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
+ }
+ inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
+
+private:
+ DeferStateStruct& mDeferStruct;
+ const int mLevel;
+};
+
+void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
+ DeferOperationHandler handler(deferStruct, level);
+ iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
+}
+
+class ReplayOperationHandler {
+public:
+ ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
+ : mReplayStruct(replayStruct), mLevel(level) {}
+ inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
+#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
+ properties().mReplayStruct.mRenderer.eventMark(operation->name());
+#endif
+ operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
+ }
+ inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
+
+private:
+ ReplayStateStruct& mReplayStruct;
+ const int mLevel;
+};
+
+void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
+ ReplayOperationHandler handler(replayStruct, level);
+
+ replayStruct.mRenderer.startMark(mName.string());
+ iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
+ replayStruct.mRenderer.endMark();
+
+ DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
+ replayStruct.mDrawGlStatus);
+}
+
+void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
+ if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
+
+ for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+ DrawDisplayListOp* childOp = mDisplayListData->children[i];
+ RenderNode* child = childOp->mDisplayList;
+ float childZ = child->properties().mTranslationZ;
+
+ if (childZ != 0.0f) {
+ zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
+ childOp->mSkipInOrderDraw = true;
+ } else if (!child->properties().mProjectBackwards) {
+ // regular, in order drawing DisplayList
+ childOp->mSkipInOrderDraw = false;
+ }
+ }
+
+ // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
+ std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
+}
+
+#define SHADOW_DELTA 0.1f
+
+template <class T>
+void RenderNode::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+ ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
+ const int size = zTranslatedNodes.size();
+ if (size == 0
+ || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
+ || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
+ // no 3d children to draw
+ return;
+ }
+
+ int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ LinearAllocator& alloc = handler.allocator();
+ ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
+ SkRegion::kIntersect_Op); // clip to 3d root bounds
+ handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+
+ /**
+ * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
+ * with very similar Z heights to draw together.
+ *
+ * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
+ * underneath both, and neither's shadow is drawn on top of the other.
+ */
+ const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
+ size_t drawIndex, shadowIndex, endIndex;
+ if (mode == kNegativeZChildren) {
+ drawIndex = 0;
+ endIndex = nonNegativeIndex;
+ shadowIndex = endIndex; // draw no shadows
+ } else {
+ drawIndex = nonNegativeIndex;
+ endIndex = size;
+ shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
+ }
+ float lastCasterZ = 0.0f;
+ while (shadowIndex < endIndex || drawIndex < endIndex) {
+ if (shadowIndex < endIndex) {
+ DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
+ RenderNode* caster = casterOp->mDisplayList;
+ const float casterZ = zTranslatedNodes[shadowIndex].key;
+ // attempt to render the shadow if the caster about to be drawn is its caster,
+ // OR if its caster's Z value is similar to the previous potential caster
+ if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
+
+ if (caster->properties().mAlpha > 0.0f) {
+ mat4 shadowMatrixXY(casterOp->mTransformFromParent);
+ caster->applyViewPropertyTransforms(shadowMatrixXY);
+
+ // Z matrix needs actual 3d transformation, so mapped z values will be correct
+ mat4 shadowMatrixZ(casterOp->mTransformFromParent);
+ caster->applyViewPropertyTransforms(shadowMatrixZ, true);
+
+ DisplayListOp* shadowOp = new (alloc) DrawShadowOp(
+ shadowMatrixXY, shadowMatrixZ,
+ caster->properties().mAlpha, &(caster->properties().mOutline),
+ caster->properties().mWidth, caster->properties().mHeight);
+ handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+ }
+
+ lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+ shadowIndex++;
+ continue;
+ }
+ }
+
+ // only the actual child DL draw needs to be in save/restore,
+ // since it modifies the renderer's matrix
+ int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+
+ DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
+ RenderNode* child = childOp->mDisplayList;
+
+ renderer.concatMatrix(childOp->mTransformFromParent);
+ childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
+ handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
+ childOp->mSkipInOrderDraw = true;
+
+ renderer.restoreToCount(restoreTo);
+ drawIndex++;
+ }
+ handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
+}
+
+template <class T>
+void RenderNode::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) {
+ int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ LinearAllocator& alloc = handler.allocator();
+ ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
+ SkRegion::kReplace_Op); // clip to projection surface root bounds
+ handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+
+ for (size_t i = 0; i < mProjectedNodes.size(); i++) {
+ DrawDisplayListOp* childOp = mProjectedNodes[i];
+
+ // matrix save, concat, and restore can be done safely without allocating operations
+ int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+ renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
+ childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
+ handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
+ childOp->mSkipInOrderDraw = true;
+ renderer.restoreToCount(restoreTo);
+ }
+ handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
+}
+
+/**
+ * This function serves both defer and replay modes, and will organize the displayList's component
+ * operations for a single frame:
+ *
+ * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
+ * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
+ * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
+ * defer vs replay logic, per operation
+ */
+template <class T>
+void RenderNode::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
+ if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
+ ALOGW("Error: %s is drawing after destruction", mName.string());
+ CRASH();
+ }
+ if (mDisplayListData->isEmpty() || properties().mAlpha <= 0) {
+ DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
+ return;
+ }
+
+#if DEBUG_DISPLAY_LIST
+ Rect* clipRect = renderer.getClipRect();
+ DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
+ level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
+ clipRect->right, clipRect->bottom);
+#endif
+
+ LinearAllocator& alloc = handler.allocator();
+ int restoreTo = renderer.getSaveCount();
+ handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+ PROPERTY_SAVECOUNT, properties().mClipToBounds);
+
+ DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
+ SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
+
+ setViewProperties<T>(renderer, handler, level + 1);
+
+ bool quickRejected = properties().mClipToBounds && renderer.quickRejectConservative(0, 0, properties().mWidth, properties().mHeight);
+ if (!quickRejected) {
+ Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
+ buildZSortedChildList(zTranslatedNodes);
+
+ // for 3d root, draw children with negative z values
+ iterate3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
+
+ DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+ const int saveCountOffset = renderer.getSaveCount() - 1;
+ const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
+ for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+ DisplayListOp *op = mDisplayListData->displayListOps[i];
+
+#if DEBUG_DISPLAY_LIST
+ op->output(level + 1);
+#endif
+
+ logBuffer.writeCommand(level, op->name());
+ handler(op, saveCountOffset, properties().mClipToBounds);
+
+ if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
+ iterateProjectedChildren(renderer, handler, level);
+ }
+ }
+
+ // for 3d root, draw children with positive z values
+ iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+ }
+
+ DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
+ handler(new (alloc) RestoreToCountOp(restoreTo),
+ PROPERTY_SAVECOUNT, properties().mClipToBounds);
+ renderer.setOverrideLayerAlpha(1.0f);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
new file mode 100644
index 0000000..177f33e
--- /dev/null
+++ b/libs/hwui/RenderNode.h
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+#ifndef RENDERNODE_H
+#define RENDERNODE_H
+
+#ifndef LOG_TAG
+ #define LOG_TAG "OpenGLRenderer"
+#endif
+
+#include <SkCamera.h>
+#include <SkMatrix.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
+#include <utils/KeyedVector.h>
+#include <utils/LinearAllocator.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <cutils/compiler.h>
+
+#include <androidfw/ResourceTypes.h>
+
+#include "Debug.h"
+#include "Matrix.h"
+#include "DeferredDisplayList.h"
+#include "DisplayList.h"
+#include "RenderProperties.h"
+
+class SkBitmap;
+class SkPaint;
+class SkPath;
+class SkRegion;
+
+namespace android {
+namespace uirenderer {
+
+class DeferredDisplayList;
+class DisplayListOp;
+class DisplayListRenderer;
+class OpenGLRenderer;
+class Rect;
+class Layer;
+class SkiaShader;
+
+class ClipRectOp;
+class SaveLayerOp;
+class SaveOp;
+class RestoreToCountOp;
+class DrawDisplayListOp;
+
+/**
+ * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
+ *
+ * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
+ * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData
+ * (which holds the actual data), and DisplayList (which holds properties and performs playback onto
+ * a renderer).
+ *
+ * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's
+ * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay
+ * attached.
+ */
+class RenderNode {
+public:
+ ANDROID_API RenderNode();
+ ANDROID_API ~RenderNode();
+
+ // See flags defined in DisplayList.java
+ enum ReplayFlag {
+ kReplayFlag_ClipChildren = 0x1
+ };
+
+ ANDROID_API static void destroyDisplayListDeferred(RenderNode* displayList);
+ ANDROID_API static void outputLogBuffer(int fd);
+
+ ANDROID_API void setData(DisplayListData* newData);
+
+ void computeOrdering();
+ void defer(DeferStateStruct& deferStruct, const int level);
+ void replay(ReplayStateStruct& replayStruct, const int level);
+
+ ANDROID_API void output(uint32_t level = 1);
+
+ bool isRenderable() const {
+ return mDisplayListData && mDisplayListData->hasDrawOps;
+ }
+
+ void setName(const char* name) {
+ if (name) {
+ char* lastPeriod = strrchr(name, '.');
+ if (lastPeriod) {
+ mName.setTo(lastPeriod + 1);
+ } else {
+ mName.setTo(name);
+ }
+ }
+ }
+
+ RenderProperties& properties() {
+ return mProperties;
+ }
+
+ bool isProjectionReceiver() {
+ return properties().isProjectionReceiver();
+ }
+
+ int getWidth() {
+ return properties().getWidth();
+ }
+
+ int getHeight() {
+ return properties().getHeight();
+ }
+
+private:
+ typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
+
+ static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) {
+ for (size_t i = 0; i < nodes.size(); i++) {
+ if (nodes[i].key >= 0.0f) return i;
+ }
+ return nodes.size();
+ }
+
+ enum ChildrenSelectMode {
+ kNegativeZChildren,
+ kPositiveZChildren
+ };
+
+ void outputViewProperties(const int level);
+
+ void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
+
+ void computeOrderingImpl(DrawDisplayListOp* opState,
+ Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
+ const mat4* transformFromProjectionSurface);
+
+ template <class T>
+ inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level);
+
+ void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
+
+ template <class T>
+ inline void iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+ ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
+
+ template <class T>
+ inline void iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level);
+
+ template <class T>
+ inline void iterate(OpenGLRenderer& renderer, T& handler, const int level);
+
+ class TextContainer {
+ public:
+ size_t length() const {
+ return mByteLength;
+ }
+
+ const char* text() const {
+ return (const char*) mText;
+ }
+
+ size_t mByteLength;
+ const char* mText;
+ };
+
+ String8 mName;
+ bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed
+
+ RenderProperties mProperties;
+ DisplayListData* mDisplayListData;
+
+ /**
+ * Draw time state - these properties are only set and used during rendering
+ */
+
+ // for projection surfaces, contains a list of all children items
+ Vector<DrawDisplayListOp*> mProjectedNodes;
+}; // class RenderNode
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* RENDERNODE_H */
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 771904a..4d0edfb 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -35,7 +35,8 @@ static inline T max(T a, T b) {
VertexBufferMode ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
- const Vector3& centroid3d, VertexBuffer& shadowVertexBuffer) {
+ const Vector3& centroid3d, const Rect& casterBounds,
+ const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer) {
ATRACE_CALL();
// A bunch of parameters to tweak the shadow.
@@ -43,6 +44,16 @@ VertexBufferMode ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
const float heightFactor = 1.0f / 128;
const float geomFactor = 64;
+ Rect ambientShadowBounds(casterBounds);
+ ambientShadowBounds.outset(maxZ * geomFactor * heightFactor);
+
+ if (!localClip.intersects(ambientShadowBounds)) {
+#if DEBUG_SHADOW
+ ALOGD("Ambient shadow is out of clip rect!");
+#endif
+ return kVertexBufferMode_OnePolyRingShadow;
+ }
+
return AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon,
casterVertexCount, centroid3d, heightFactor, geomFactor,
shadowVertexBuffer);
@@ -52,7 +63,8 @@ VertexBufferMode ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
VertexBufferMode ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
const Vector3& lightPosScale, const mat4& receiverTransform,
- int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer) {
+ int screenWidth, int screenHeight, const Rect& casterBounds,
+ const Rect& localClip, VertexBuffer& shadowVertexBuffer) {
ATRACE_CALL();
// A bunch of parameters to tweak the shadow.
@@ -73,6 +85,18 @@ VertexBufferMode ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
const float lightSize = maximal / 4;
const int lightVertexCount = 8;
+ // Now light and caster are both in local space, we will check whether
+ // the shadow is within the clip area.
+ Rect lightRect = Rect(lightCenter.x - lightSize, lightCenter.y - lightSize,
+ lightCenter.x + lightSize, lightCenter.y + lightSize);
+ lightRect.unionWith(localClip);
+ if (!lightRect.intersects(casterBounds)) {
+#if DEBUG_SHADOW
+ ALOGD("Spot shadow is out of clip rect!");
+#endif
+ return kVertexBufferMode_OnePolyRingShadow;
+ }
+
VertexBufferMode mode = SpotShadow::createSpotShadow(isCasterOpaque,
casterPolygon, casterVertexCount, lightCenter, lightSize,
lightVertexCount, shadowVertexBuffer);
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index ab039fa..ff3de74 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -66,12 +66,14 @@ class ShadowTessellator {
public:
static VertexBufferMode tessellateAmbientShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
- const Vector3& centroid3d, VertexBuffer& shadowVertexBuffer);
+ const Vector3& centroid3d, const Rect& casterBounds,
+ const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer);
static VertexBufferMode tessellateSpotShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
const Vector3& lightPosScale, const mat4& receiverTransform,
- int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer);
+ int screenWidth, int screenHeight, const Rect& casterBounds,
+ const Rect& localClip, VertexBuffer& shadowVertexBuffer);
static void generateShadowIndices(uint16_t* shadowIndices);
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index f81cd12..5fa0ba5 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -573,10 +573,8 @@ void SpotShadow::computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly
for (int j = 0; j < lightPolyLength; j++) {
int m = 0;
for (int i = 0; i < polyLength; i++) {
+ // After validating the input, deltaZ is guaranteed to be positive.
float deltaZ = lightPoly[j].z - poly[i].z;
- if (deltaZ == 0) {
- return;
- }
float ratioZ = lightPoly[j].z / deltaZ;
float x = lightPoly[j].x - ratioZ * (lightPoly[j].x - poly[i].x);
float y = lightPoly[j].y - ratioZ * (lightPoly[j].y - poly[i].y);
@@ -615,9 +613,6 @@ void SpotShadow::computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly
// If there is no real umbra, make a fake one.
for (int i = 0; i < polyLength; i++) {
float deltaZ = lightCenter.z - poly[i].z;
- if (deltaZ == 0) {
- return;
- }
float ratioZ = lightCenter.z / deltaZ;
float x = lightCenter.x - ratioZ * (lightCenter.x - poly[i].x);
float y = lightCenter.y - ratioZ * (lightCenter.y - poly[i].y);
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 46b74da..fe510f6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -258,7 +258,7 @@ public class AudioService extends IAudioService.Stub {
private final boolean mUseFixedVolume;
// stream names used by dumpStreamStates()
- private final String[] STREAM_NAMES = new String[] {
+ private static final String[] STREAM_NAMES = new String[] {
"STREAM_VOICE_CALL",
"STREAM_SYSTEM",
"STREAM_RING",
@@ -614,6 +614,12 @@ public class AudioService extends IAudioService.Stub {
pw.println(Integer.toHexString(mMuteAffectedStreams));
}
+ /** @hide */
+ public static String streamToString(int stream) {
+ if (stream >= 0 && stream < STREAM_NAMES.length) return STREAM_NAMES[stream];
+ if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) return "USE_DEFAULT_STREAM_TYPE";
+ return "UNKNOWN_STREAM_" + stream;
+ }
private void updateStreamVolumeAlias(boolean updateVolumes) {
int dtmfStreamAlias;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 5611efb..dee8705 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -23,11 +23,20 @@ import java.nio.ByteBuffer;
import java.nio.NioUtils;
import android.annotation.IntDef;
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
+import android.content.Context;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
+import com.android.internal.app.IAppOpsService;
+
/**
* The AudioTrack class manages and plays a single audio resource for Java applications.
@@ -239,7 +248,10 @@ public class AudioTrack
* Audio session ID
*/
private int mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
-
+ /**
+ * Reference to the app-ops service.
+ */
+ private final IAppOpsService mAppOps;
//--------------------------------
// Used exclusively by native code
@@ -343,6 +355,9 @@ public class AudioTrack
audioBuffSizeCheck(bufferSizeInBytes);
+ IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+ mAppOps = IAppOpsService.Stub.asInterface(b);
+
if (sessionId < 0) {
throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
}
@@ -841,6 +856,9 @@ public class AudioTrack
* {@link #ERROR_INVALID_OPERATION}
*/
public int setStereoVolume(float leftVolume, float rightVolume) {
+ if (isRestricted()) {
+ return SUCCESS;
+ }
if (mState == STATE_UNINITIALIZED) {
return ERROR_INVALID_OPERATION;
}
@@ -1014,13 +1032,25 @@ public class AudioTrack
if (mState != STATE_INITIALIZED) {
throw new IllegalStateException("play() called on uninitialized AudioTrack.");
}
-
+ if (isRestricted()) {
+ setVolume(0);
+ }
synchronized(mPlayStateLock) {
native_start();
mPlayState = PLAYSTATE_PLAYING;
}
}
+ private boolean isRestricted() {
+ try {
+ final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO, mStreamType,
+ Process.myUid(), ActivityThread.currentPackageName());
+ return mode != AppOpsManager.MODE_ALLOWED;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
/**
* Stops playing the audio data.
* When used on an instance created in {@link #MODE_STREAM} mode, audio will stop playing
@@ -1296,6 +1326,9 @@ public class AudioTrack
* {@link #ERROR_INVALID_OPERATION}
*/
public int setAuxEffectSendLevel(float level) {
+ if (isRestricted()) {
+ return SUCCESS;
+ }
if (mState == STATE_UNINITIALIZED) {
return ERROR_INVALID_OPERATION;
}
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index 67680a8..eb91668 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -269,6 +269,6 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
native_init();
}
- private int mNativeContext;
+ private long mNativeContext;
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index e20a4af..1b92410 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -16,6 +16,8 @@
package android.media;
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -32,6 +34,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
@@ -42,6 +46,8 @@ import android.media.MediaTimeProvider;
import android.media.SubtitleController;
import android.media.SubtitleData;
+import com.android.internal.app.IAppOpsService;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -576,6 +582,8 @@ public class MediaPlayer implements SubtitleController.Listener
private PowerManager.WakeLock mWakeLock = null;
private boolean mScreenOnWhilePlaying;
private boolean mStayAwake;
+ private final IAppOpsService mAppOps;
+ private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
/**
* Default constructor. Consider using one of the create() methods for
@@ -599,6 +607,8 @@ public class MediaPlayer implements SubtitleController.Listener
mOutOfBandSubtitleTracks = new Vector<SubtitleTrack>();
mOpenSubtitleSources = new Vector<InputStream>();
mInbandSubtitleTracks = new SubtitleTrack[0];
+ IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+ mAppOps = IAppOpsService.Stub.asInterface(b);
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
@@ -1055,13 +1065,35 @@ public class MediaPlayer implements SubtitleController.Listener
*
* @throws IllegalStateException if it is called in an invalid state
*/
- public void start() throws IllegalStateException {
+ public void start() throws IllegalStateException {
+ if (isRestricted()) {
+ _setVolume(0, 0);
+ }
stayAwake(true);
_start();
}
private native void _start() throws IllegalStateException;
+ private boolean isRestricted() {
+ try {
+ final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
+ getAudioStreamType(), Process.myUid(), ActivityThread.currentPackageName());
+ return mode != AppOpsManager.MODE_ALLOWED;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ private int getAudioStreamType() {
+ if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
+ mStreamType = _getAudioStreamType();
+ }
+ return mStreamType;
+ }
+
+ private native int _getAudioStreamType() throws IllegalStateException;
+
/**
* Stops playback after playback has been stopped or paused.
*
@@ -1402,7 +1434,12 @@ public class MediaPlayer implements SubtitleController.Listener
* @param streamtype the audio stream type
* @see android.media.AudioManager
*/
- public native void setAudioStreamType(int streamtype);
+ public void setAudioStreamType(int streamtype) {
+ _setAudioStreamType(streamtype);
+ mStreamType = streamtype;
+ }
+
+ private native void _setAudioStreamType(int streamtype);
/**
* Sets the player to be looping or non-looping.
@@ -1435,7 +1472,14 @@ public class MediaPlayer implements SubtitleController.Listener
* The single parameter form below is preferred if the channel volumes don't need
* to be set independently.
*/
- public native void setVolume(float leftVolume, float rightVolume);
+ public void setVolume(float leftVolume, float rightVolume) {
+ if (isRestricted()) {
+ return;
+ }
+ _setVolume(leftVolume, rightVolume);
+ }
+
+ private native void _setVolume(float leftVolume, float rightVolume);
/**
* Similar, excepts sets volume of all channels to same value.
@@ -1500,7 +1544,14 @@ public class MediaPlayer implements SubtitleController.Listener
* 0 < x <= R -> level = 10^(72*(x-R)/20/R)
* @param level send level scalar
*/
- public native void setAuxEffectSendLevel(float level);
+ public void setAuxEffectSendLevel(float level) {
+ if (isRestricted()) {
+ return;
+ }
+ _setAuxEffectSendLevel(level);
+ }
+
+ private native void _setAuxEffectSendLevel(float level);
/*
* @param request Parcel destinated to the media player. The
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index f1b256e..14f0c69 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -20,16 +20,24 @@ import java.io.File;
import java.io.FileDescriptor;
import java.lang.ref.WeakReference;
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemProperties;
import android.util.AndroidRuntimeException;
import android.util.Log;
+import com.android.internal.app.IAppOpsService;
+
/**
* The SoundPool class manages and plays audio resources for applications.
@@ -449,6 +457,8 @@ public class SoundPool {
private SoundPool mProxy;
private final Object mLock;
+ private final int mStreamType;
+ private final IAppOpsService mAppOps;
// SoundPool messages
//
@@ -463,6 +473,9 @@ public class SoundPool {
}
mLock = new Object();
mProxy = proxy;
+ mStreamType = streamType;
+ IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+ mAppOps = IAppOpsService.Stub.asInterface(b);
}
public int load(String path, int priority)
@@ -522,9 +535,27 @@ public class SoundPool {
public native final boolean unload(int soundID);
- public native final int play(int soundID, float leftVolume, float rightVolume,
+ public final int play(int soundID, float leftVolume, float rightVolume,
+ int priority, int loop, float rate) {
+ if (isRestricted()) {
+ leftVolume = rightVolume = 0;
+ }
+ return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
+ }
+
+ public native final int _play(int soundID, float leftVolume, float rightVolume,
int priority, int loop, float rate);
+ private boolean isRestricted() {
+ try {
+ final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
+ mStreamType, Process.myUid(), ActivityThread.currentPackageName());
+ return mode != AppOpsManager.MODE_ALLOWED;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
public native final void pause(int streamID);
public native final void resume(int streamID);
@@ -535,8 +566,14 @@ public class SoundPool {
public native final void stop(int streamID);
- public native final void setVolume(int streamID,
- float leftVolume, float rightVolume);
+ public final void setVolume(int streamID, float leftVolume, float rightVolume) {
+ if (isRestricted()) {
+ return;
+ }
+ _setVolume(streamID, leftVolume, rightVolume);
+ }
+
+ private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
public void setVolume(int streamID, float volume) {
setVolume(streamID, volume, volume);
diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp
index c48af11..0e7d83e 100644
--- a/media/jni/android_media_MediaHTTPConnection.cpp
+++ b/media/jni/android_media_MediaHTTPConnection.cpp
@@ -84,7 +84,7 @@ static fields_t gFields;
static sp<JMediaHTTPConnection> setObject(
JNIEnv *env, jobject thiz, const sp<JMediaHTTPConnection> &conn) {
sp<JMediaHTTPConnection> old =
- (JMediaHTTPConnection *)env->GetIntField(thiz, gFields.context);
+ (JMediaHTTPConnection *)env->GetLongField(thiz, gFields.context);
if (conn != NULL) {
conn->incStrong(thiz);
@@ -92,13 +92,13 @@ static sp<JMediaHTTPConnection> setObject(
if (old != NULL) {
old->decStrong(thiz);
}
- env->SetIntField(thiz, gFields.context, (int)conn.get());
+ env->SetLongField(thiz, gFields.context, (jlong)conn.get());
return old;
}
static sp<JMediaHTTPConnection> getObject(JNIEnv *env, jobject thiz) {
- return (JMediaHTTPConnection *)env->GetIntField(thiz, gFields.context);
+ return (JMediaHTTPConnection *)env->GetLongField(thiz, gFields.context);
}
static void android_media_MediaHTTPConnection_native_init(JNIEnv *env) {
@@ -106,7 +106,7 @@ static void android_media_MediaHTTPConnection_native_init(JNIEnv *env) {
env, env->FindClass("android/media/MediaHTTPConnection"));
CHECK(clazz.get() != NULL);
- gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "I");
+ gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
CHECK(gFields.context != NULL);
gFields.readAtMethodID = env->GetMethodID(clazz.get(), "readAt", "(J[BI)I");
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index dc3ae5b..abebd48 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -500,6 +500,20 @@ android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, jint str
process_media_player_call( env, thiz, mp->setAudioStreamType((audio_stream_type_t) streamtype) , NULL, NULL );
}
+static jint
+android_media_MediaPlayer_getAudioStreamType(JNIEnv *env, jobject thiz)
+{
+ sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+ if (mp == NULL ) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return 0;
+ }
+ audio_stream_type_t streamtype;
+ process_media_player_call( env, thiz, mp->getAudioStreamType(&streamtype), NULL, NULL );
+ ALOGV("getAudioStreamType: %d (streamtype)", streamtype);
+ return (jint) streamtype;
+}
+
static void
android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping)
{
@@ -841,10 +855,11 @@ static JNINativeMethod gMethods[] = {
{"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration},
{"_release", "()V", (void *)android_media_MediaPlayer_release},
{"_reset", "()V", (void *)android_media_MediaPlayer_reset},
- {"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
+ {"_setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
+ {"_getAudioStreamType", "()I", (void *)android_media_MediaPlayer_getAudioStreamType},
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer_isLooping},
- {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
+ {"_setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
{"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
{"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter},
{"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata},
@@ -853,7 +868,7 @@ static JNINativeMethod gMethods[] = {
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
{"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id},
{"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
- {"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
+ {"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect},
{"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData},
{"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer_setRetransmitEndpoint},
diff --git a/media/jni/mediaeditor/Android.mk b/media/jni/mediaeditor/Android.mk
index 76e8346..312c366 100644
--- a/media/jni/mediaeditor/Android.mk
+++ b/media/jni/mediaeditor/Android.mk
@@ -47,7 +47,6 @@ LOCAL_C_INCLUDES += \
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
- libaudioflinger \
libaudioutils \
libbinder \
libcutils \
diff --git a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
index 9cc55ab..bda3b6b 100644
--- a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
+++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
@@ -229,7 +229,7 @@ static JNINativeMethod gMethods[] = {
"(I)Z",
(void *)android_media_SoundPool_SoundPoolImpl_unload
},
- { "play",
+ { "_play",
"(IFFIIF)I",
(void *)android_media_SoundPool_SoundPoolImpl_play
},
@@ -253,7 +253,7 @@ static JNINativeMethod gMethods[] = {
"(I)V",
(void *)android_media_SoundPool_SoundPoolImpl_stop
},
- { "setVolume",
+ { "_setVolume",
"(IFF)V",
(void *)android_media_SoundPool_SoundPoolImpl_setVolume
},
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index 3b448d9..e43a1e2 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -47,7 +47,7 @@
<string name="pref_advanced_devices" msgid="903257239609301276">"Erweiterte Geräte anzeigen"</string>
<string name="pref_file_size" msgid="2826879315743961459">"Dateigröße anzeigen"</string>
<string name="pref_device_size" msgid="3542106883278997222">"Geräteabmessungen anzeigen"</string>
- <string name="empty" msgid="7858882803708117596">"Keine Elemente"</string>
+ <string name="empty" msgid="7858882803708117596">"Keine Dokumente"</string>
<string name="toast_no_application" msgid="1339885974067891667">"Datei kann nicht geöffnet werden."</string>
<string name="toast_failed_delete" msgid="2180678019407244069">"Einige Dokumente konnten nicht gelöscht werden."</string>
<string name="share_via" msgid="8966594246261344259">"Teilen über"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index aec3318..5a91484 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -27,7 +27,7 @@
<string name="menu_settings" msgid="6008033148948428823">"Ρυθμίσεις"</string>
<string name="menu_open" msgid="432922957274920903">"Άνοιγμα"</string>
<string name="menu_save" msgid="2394743337684426338">"Αποθήκευση"</string>
- <string name="menu_share" msgid="3075149983979628146">"Κοινή χρήση"</string>
+ <string name="menu_share" msgid="3075149983979628146">"Κοινοποίηση"</string>
<string name="menu_delete" msgid="8138799623850614177">"Διαγραφή"</string>
<string name="mode_selected_count" msgid="459111894725594625">"Επιλέχθηκαν <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Κατά όνομα"</string>
@@ -50,5 +50,5 @@
<string name="empty" msgid="7858882803708117596">"Δεν υπάρχουν στοιχεία"</string>
<string name="toast_no_application" msgid="1339885974067891667">"Δεν είναι δυνατό το άνοιγμα του αρχείου"</string>
<string name="toast_failed_delete" msgid="2180678019407244069">"Δεν είναι δυνατή η διαγραφή ορισμένων εγγράφων"</string>
- <string name="share_via" msgid="8966594246261344259">"Κοινή χρήση μέσω"</string>
+ <string name="share_via" msgid="8966594246261344259">"Κοινοποίηση μέσω"</string>
</resources>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index 519b936..d9f4475 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -22,7 +22,7 @@
<string name="menu_create_dir" msgid="5947289605844398389">"Buat folder"</string>
<string name="menu_grid" msgid="6878021334497835259">"Tampilan kisi"</string>
<string name="menu_list" msgid="7279285939892417279">"Tampilan daftar"</string>
- <string name="menu_sort" msgid="7677740407158414452">"Sortir menurut"</string>
+ <string name="menu_sort" msgid="7677740407158414452">"Urutkan menurut"</string>
<string name="menu_search" msgid="3816712084502856974">"Telusuri"</string>
<string name="menu_settings" msgid="6008033148948428823">"Setelan"</string>
<string name="menu_open" msgid="432922957274920903">"Buka"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index 2a96b1a..4b5ebcd 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -44,7 +44,7 @@
<string name="root_type_shortcut" msgid="3318760609471618093">"Skratky"</string>
<string name="root_type_device" msgid="7121342474653483538">"Zariadenia"</string>
<string name="root_type_apps" msgid="8838065367985945189">"Ďalšie aplikácie"</string>
- <string name="pref_advanced_devices" msgid="903257239609301276">"Zobraziť rozšírené zariadenia"</string>
+ <string name="pref_advanced_devices" msgid="903257239609301276">"Zobraziť pokročilé zariadenia"</string>
<string name="pref_file_size" msgid="2826879315743961459">"Zobraziť veľkosť súboru"</string>
<string name="pref_device_size" msgid="3542106883278997222">"Zobraziť veľkosť zariadenia"</string>
<string name="empty" msgid="7858882803708117596">"Žiadne položky"</string>
diff --git a/packages/ExternalStorageProvider/res/values-sk/strings.xml b/packages/ExternalStorageProvider/res/values-sk/strings.xml
index fd424c8..9be7b79 100644
--- a/packages/ExternalStorageProvider/res/values-sk/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-sk/strings.xml
@@ -17,6 +17,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Externý ukladací priestor"</string>
- <string name="root_internal_storage" msgid="827844243068584127">"Interný ukladací priestor"</string>
+ <string name="root_internal_storage" msgid="827844243068584127">"Interné úložisko"</string>
<string name="root_documents" msgid="4051252304075469250">"Dokumenty"</string>
</resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index 1e79ee4..2e76f19 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -1120,6 +1120,7 @@ public class KeyguardHostView extends KeyguardViewBase {
KeyguardWidgetFrame frame = mAppWidgetContainer.getWidgetPageAt(i);
frame.removeAllViews();
}
+ getSecurityContainer().onPause(); // clean up any actions in progress
}
public void goToWidget(int appWidgetId) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
index 8738288..5b35ba8 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
@@ -400,6 +400,7 @@ public class KeyguardViewManager {
} else {
mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
}
+ mWindowLayoutParams.format = show ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
}
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index cfb557e..17fabdc 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -33,7 +33,7 @@
<string name="page_count_unknown" msgid="6058852665954511124">"Pages"</string>
<string name="generating_print_job" msgid="3119608742651698916">"Génération tâche impression…"</string>
<string name="save_as_pdf" msgid="5718454119847596853">"Enregistrer au format .PDF"</string>
- <string name="all_printers" msgid="5018829726861876202">"Toutes les imprimantes…"</string>
+ <string name="all_printers" msgid="5018829726861876202">"Toutes les imprim."</string>
<string name="print_dialog" msgid="32628687461331979">"Boîte de dialogue d\'impression"</string>
<string name="search" msgid="5421724265322228497">"Rechercher"</string>
<string name="all_printers_label" msgid="3178848870161526399">"Toutes les imprimantes"</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 55d7def..58086c4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -69,7 +69,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 100;
+ private static final int DATABASE_VERSION = 101;
private Context mContext;
private int mUserHandle;
@@ -1591,6 +1591,28 @@ public class DatabaseHelper extends SQLiteOpenHelper {
}
upgradeVersion = 100;
}
+ if (upgradeVersion == 100) {
+ // Catch devices that were initialized to version 100 and missed these in onCreate()
+ if (mUserHandle == UserHandle.USER_OWNER) {
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)"
+ + " VALUES(?,?);");
+ loadIntegerSetting(stmt, Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ R.integer.def_lock_screen_show_notifications);
+
+ loadIntegerSetting(stmt, Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+ R.integer.def_heads_up_enabled);
+
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ }
+ upgradeVersion = 101;
+ }
// *** Remember to update DATABASE_VERSION above!
@@ -2314,6 +2336,12 @@ public class DatabaseHelper extends SQLiteOpenHelper {
loadIntegerSetting(stmt, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
R.integer.def_wifi_scan_always_available);
+ loadIntegerSetting(stmt, Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ R.integer.def_lock_screen_show_notifications);
+
+ loadIntegerSetting(stmt, Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+ R.integer.def_heads_up_enabled);
+
// --- New global settings start here
} finally {
if (stmt != null) stmt.close();
diff --git a/packages/Shell/res/values-el/strings.xml b/packages/Shell/res/values-el/strings.xml
index 3669f78..9b1eb7b 100644
--- a/packages/Shell/res/values-el/strings.xml
+++ b/packages/Shell/res/values-el/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Κέλυφος"</string>
<string name="bugreport_finished_title" msgid="2293711546892863898">"Η λήψη της αναφοράς ήταν επιτυχής"</string>
- <string name="bugreport_finished_text" msgid="3559904746859400732">"Αγγίξτε για κοινή χρήση της αναφοράς σας σφαλμάτων"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Αγγίξτε για να μοιραστείτε τη αναφορά σφαλμάτων"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Οι αναφορές σφαλμάτων περιέχουν δεδομένα από τα διάφορα αρχεία καταγραφής του συστήματος, συμπεριλαμβανομένων προσωπικών και ιδιωτικών πληροφοριών. Να μοιράζεστε αναφορές σφαλμάτων μόνο με εφαρμογές και άτομα που εμπιστεύεστε."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Εμφάνιση αυτού του μηνύματος την επόμενη φορά"</string>
</resources>
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.png
index 1c3518a..f256fbb 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.png
index 9dbc65e..b946ec9 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.png
index ddb002d..48606a8 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.png
index 43b7ef2..d006f13 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.png
index 1d8b7ee..867947b 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.png
index 11b2134..b1e984c 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.png
index a858573..ceda1bb 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.png
index 04de5d7..25fc759 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.png
index caea37e..9dfc3c6 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.png
index b66aa46..82f4113 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.png
index 10ebcd5..47be502 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.png
index fef43b8..4b12809 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.png
index 05e3267..945c606 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.png
index ef42b27..0a3f73e 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.png
index fc1c95e..398cbef 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.png
index 68b1b7c..8e225af 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.png
index 8a8f890..937202b 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.png
index 12d4a01..4621d18 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.png
index 3cb4421..a1ab61b 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.png
index 4620b3a..ea42a7f 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.png
Binary files differ
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 71a644d..7671ef1 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -206,7 +206,7 @@
<string name="quick_settings_inversion_label" msgid="1666358784283020762">"Modalità inversione colori"</string>
<string name="quick_settings_contrast_label" msgid="3319507551689108692">"Modalità di contrasto avanzata"</string>
<string name="quick_settings_color_space_label" msgid="853443689745584770">"Modalità di correzione del colore"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"ELEMENTI RECENTI"</string>
+ <string name="recents_empty_message" msgid="2269156590813544104">"MESSAGGI RECENTI"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"La rete potrebbe\nessere monitorata"</string>
<string name="description_target_search" msgid="3091587249776033139">"Ricerca"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Su per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 6e09131..032fa34 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -204,7 +204,7 @@
<string name="quick_settings_inversion_label" msgid="1666358784283020762">"Modus voor kleurinversie"</string>
<string name="quick_settings_contrast_label" msgid="3319507551689108692">"Modus voor verbeterd contrast"</string>
<string name="quick_settings_color_space_label" msgid="853443689745584770">"Modus voor kleurcorrectie"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"RECENTEN"</string>
+ <string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netwerk kan\nworden gecontroleerd"</string>
<string name="description_target_search" msgid="3091587249776033139">"Zoeken"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Veeg omhoog voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 0d9be4c..0188563 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -151,7 +151,7 @@
<string name="accessibility_quick_settings_wifi" msgid="6099781031669728709">"<xliff:g id="SIGNAL">%1$s</xliff:g> <xliff:g id="NETWORK">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"มือถือ <xliff:g id="SIGNAL">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> <xliff:g id="NETWORK">%3$s</xliff:g>"</string>
<string name="accessibility_quick_settings_battery" msgid="1480931583381408972">"แบตเตอรี่ <xliff:g id="STATE">%s</xliff:g>"</string>
- <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"โหมดใช้งานบนเครื่องบิน <xliff:g id="STATE">%s</xliff:g>"</string>
+ <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"โหมดใช้บนเครื่องบิน <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"บลูทูธ <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"สถานที่ <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ตั้งเวลาปลุกไว้ที่ <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -174,7 +174,7 @@
<string name="dessert_case" msgid="1295161776223959221">"ชั้นแสดงของหวาน"</string>
<string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
<string name="ethernet_label" msgid="7967563676324087464">"อีเทอร์เน็ต"</string>
- <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"โหมดใช้งานบนเครื่องบิน"</string>
+ <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"โหมดใช้บนเครื่องบิน"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"กำลังชาร์จ, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
<string name="quick_settings_battery_charged_label" msgid="8865413079414246081">"ชาร์จแล้ว"</string>
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"บลูทูธ"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 55399c2..163ef2a 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -199,7 +199,7 @@
<string name="quick_settings_wifi_label" msgid="9135344704899546041">"Wi-Fi"</string>
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"未連線"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"沒有網路"</string>
- <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi:關閉"</string>
+ <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 已關閉"</string>
<string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"投放螢幕"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
diff --git a/packages/services/PacProcessor/jni/Android.mk b/packages/services/PacProcessor/jni/Android.mk
index f16c90b..8a60927 100644
--- a/packages/services/PacProcessor/jni/Android.mk
+++ b/packages/services/PacProcessor/jni/Android.mk
@@ -35,6 +35,7 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_MODULE := libjni_pacprocessor
LOCAL_MODULE_TAGS := optional
+LOCAL_32_BIT_ONLY := true
include external/stlport/libstlport.mk
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 1cca164..3c23c6e 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -91,6 +91,7 @@ import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -212,6 +213,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final Object mServiceAquireLock = new Object();
Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
SearchManager mSearchManager;
+ AccessibilityManager mAccessibilityManager;
// Vibrator pattern for haptic feedback of a long press.
long[] mLongPressVibePattern;
@@ -299,7 +301,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mOrientationSensorEnabled = false;
int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean mHasSoftInput = false;
- boolean mTouchExplorationEnabled = false;
boolean mTranslucentDecorEnabled = true;
int mPointerLocationMode = 0; // guarded by mLock
@@ -905,6 +906,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
com.android.internal.R.bool.config_enableTranslucentDecor);
readConfigurationDependentBehaviors();
+ mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+
// register for dock events
IntentFilter filter = new IntentFilter();
filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
@@ -1105,7 +1109,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* navigation bar and touch exploration is not enabled
*/
private boolean canHideNavigationBar() {
- return mHasNavigationBar && !mTouchExplorationEnabled;
+ return mHasNavigationBar
+ && !mAccessibilityManager.isTouchExplorationEnabled();
}
@Override
@@ -5083,10 +5088,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
if (pattern.length == 1) {
// One-shot vibration
- mVibrator.vibrate(owningUid, owningPackage, pattern[0]);
+ mVibrator.vibrate(owningUid, owningPackage, pattern[0], AudioManager.STREAM_SYSTEM);
} else {
// Pattern vibration
- mVibrator.vibrate(owningUid, owningPackage, pattern, -1);
+ mVibrator.vibrate(owningUid, owningPackage, pattern, -1, AudioManager.STREAM_SYSTEM);
}
return true;
}
@@ -5246,7 +5251,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* R.boolean.config_enableTranslucentDecor is false.
*/
private boolean areTranslucentBarsAllowed() {
- return mTranslucentDecorEnabled && !mTouchExplorationEnabled;
+ return mTranslucentDecorEnabled
+ && !mAccessibilityManager.isTouchExplorationEnabled();
}
// Use this instead of checking config_showNavigationBar so that it can be consistently
@@ -5297,11 +5303,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- public void setTouchExplorationEnabled(boolean enabled) {
- mTouchExplorationEnabled = enabled;
- }
-
- @Override
public boolean isTopLevelWindow(int windowType) {
if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
&& windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index d4fa5a7..dce4f58 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -235,8 +235,8 @@ public class RenderScript {
rsnContextSendMessage(mContext, id, data);
}
- native void rsnContextBindRootScript(long con, int script);
- synchronized void nContextBindRootScript(int script) {
+ native void rsnContextBindRootScript(long con, long script);
+ synchronized void nContextBindRootScript(long script) {
validate();
rsnContextBindRootScript(mContext, script);
}
@@ -245,23 +245,23 @@ public class RenderScript {
validate();
rsnContextBindSampler(mContext, sampler, slot);
}
- native void rsnContextBindProgramStore(long con, int pfs);
- synchronized void nContextBindProgramStore(int pfs) {
+ native void rsnContextBindProgramStore(long con, long pfs);
+ synchronized void nContextBindProgramStore(long pfs) {
validate();
rsnContextBindProgramStore(mContext, pfs);
}
- native void rsnContextBindProgramFragment(long con, int pf);
- synchronized void nContextBindProgramFragment(int pf) {
+ native void rsnContextBindProgramFragment(long con, long pf);
+ synchronized void nContextBindProgramFragment(long pf) {
validate();
rsnContextBindProgramFragment(mContext, pf);
}
- native void rsnContextBindProgramVertex(long con, int pv);
- synchronized void nContextBindProgramVertex(int pv) {
+ native void rsnContextBindProgramVertex(long con, long pv);
+ synchronized void nContextBindProgramVertex(long pv) {
validate();
rsnContextBindProgramVertex(mContext, pv);
}
- native void rsnContextBindProgramRaster(long con, int pr);
- synchronized void nContextBindProgramRaster(int pr) {
+ native void rsnContextBindProgramRaster(long con, long pr);
+ synchronized void nContextBindProgramRaster(long pr) {
validate();
rsnContextBindProgramRaster(mContext, pr);
}
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index b547706..671b43d 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -452,7 +452,7 @@ nElementGetNativeData(JNIEnv *_env, jobject _this, jlong con, jlong id, jintArra
// we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
assert(dataSize == 5);
- uint32_t elementData[5];
+ uintptr_t elementData[5];
rsaElementGetNativeData((RsContext)con, (RsElement)id, elementData, dataSize);
for(jint i = 0; i < dataSize; i ++) {
@@ -473,7 +473,7 @@ nElementGetSubElements(JNIEnv *_env, jobject _this, jlong con, jlong id,
uintptr_t *ids = (uintptr_t*)malloc(dataSize * sizeof(uintptr_t));
const char **names = (const char **)malloc(dataSize * sizeof(const char *));
- size_t *arraySizes = (size_t *)malloc(dataSize * sizeof(size_t));
+ uint32_t *arraySizes = (uint32_t *)malloc(dataSize * sizeof(uint32_t));
rsaElementGetSubElements((RsContext)con, (RsElement)id, ids, names, arraySizes, (uint32_t)dataSize);
@@ -1404,35 +1404,35 @@ nProgramRasterCreate(JNIEnv *_env, jobject _this, jlong con, jboolean pointSprit
// ---------------------------------------------------------------------------
static void
-nContextBindRootScript(JNIEnv *_env, jobject _this, jlong con, jint script)
+nContextBindRootScript(JNIEnv *_env, jobject _this, jlong con, jlong script)
{
LOG_API("nContextBindRootScript, con(%p), script(%p)", (RsContext)con, (RsScript)script);
rsContextBindRootScript((RsContext)con, (RsScript)script);
}
static void
-nContextBindProgramStore(JNIEnv *_env, jobject _this, jlong con, jint pfs)
+nContextBindProgramStore(JNIEnv *_env, jobject _this, jlong con, jlong pfs)
{
LOG_API("nContextBindProgramStore, con(%p), pfs(%p)", (RsContext)con, (RsProgramStore)pfs);
rsContextBindProgramStore((RsContext)con, (RsProgramStore)pfs);
}
static void
-nContextBindProgramFragment(JNIEnv *_env, jobject _this, jlong con, jint pf)
+nContextBindProgramFragment(JNIEnv *_env, jobject _this, jlong con, jlong pf)
{
LOG_API("nContextBindProgramFragment, con(%p), pf(%p)", (RsContext)con, (RsProgramFragment)pf);
rsContextBindProgramFragment((RsContext)con, (RsProgramFragment)pf);
}
static void
-nContextBindProgramVertex(JNIEnv *_env, jobject _this, jlong con, jint pf)
+nContextBindProgramVertex(JNIEnv *_env, jobject _this, jlong con, jlong pf)
{
LOG_API("nContextBindProgramVertex, con(%p), pf(%p)", (RsContext)con, (RsProgramVertex)pf);
rsContextBindProgramVertex((RsContext)con, (RsProgramVertex)pf);
}
static void
-nContextBindProgramRaster(JNIEnv *_env, jobject _this, jlong con, jint pf)
+nContextBindProgramRaster(JNIEnv *_env, jobject _this, jlong con, jlong pf)
{
LOG_API("nContextBindProgramRaster, con(%p), pf(%p)", (RsContext)con, (RsProgramRaster)pf);
rsContextBindProgramRaster((RsContext)con, (RsProgramRaster)pf);
@@ -1676,11 +1676,11 @@ static JNINativeMethod methods[] = {
{"rsnProgramRasterCreate", "(JZI)J", (void*)nProgramRasterCreate },
{"rsnProgramVertexCreate", "(JLjava/lang/String;[Ljava/lang/String;[J)J", (void*)nProgramVertexCreate },
-{"rsnContextBindRootScript", "(JI)V", (void*)nContextBindRootScript },
-{"rsnContextBindProgramStore", "(JI)V", (void*)nContextBindProgramStore },
-{"rsnContextBindProgramFragment", "(JI)V", (void*)nContextBindProgramFragment },
-{"rsnContextBindProgramVertex", "(JI)V", (void*)nContextBindProgramVertex },
-{"rsnContextBindProgramRaster", "(JI)V", (void*)nContextBindProgramRaster },
+{"rsnContextBindRootScript", "(JJ)V", (void*)nContextBindRootScript },
+{"rsnContextBindProgramStore", "(JJ)V", (void*)nContextBindProgramStore },
+{"rsnContextBindProgramFragment", "(JJ)V", (void*)nContextBindProgramFragment },
+{"rsnContextBindProgramVertex", "(JJ)V", (void*)nContextBindProgramVertex },
+{"rsnContextBindProgramRaster", "(JJ)V", (void*)nContextBindProgramRaster },
{"rsnSamplerCreate", "(JIIIIIF)J", (void*)nSamplerCreate },
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 959d4a9..0edce11 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -44,7 +44,6 @@ import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.input.InputManager;
import android.net.Uri;
-import android.opengl.Matrix;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -52,7 +51,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.Parcel;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -63,24 +61,27 @@ import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
+import android.util.LongArray;
import android.util.Pools.Pool;
import android.util.Pools.SimplePool;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.IWindow;
-import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.InputEventConsistencyVerifier;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
+import android.view.WindowInfo;
import android.view.WindowManager;
+import android.view.WindowManagerInternal;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.accessibility.IAccessibilityManager;
@@ -89,6 +90,7 @@ import android.view.accessibility.IAccessibilityManagerClient;
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParserException;
@@ -166,7 +168,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private final PackageManager mPackageManager;
- private final IWindowManager mWindowManagerService;
+ private final WindowManagerInternal mWindowManagerService;
private final SecurityPolicy mSecurityPolicy;
@@ -197,9 +199,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private int mCurrentUserId = UserHandle.USER_OWNER;
+ private final LongArray mTempLongArray = new LongArray();
+
//TODO: Remove this hack
private boolean mInitialized;
+ private WindowsForAccessibilityCallback mWindowsForAccessibilityCallback;
+
private UserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
}
@@ -221,7 +227,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public AccessibilityManagerService(Context context) {
mContext = context;
mPackageManager = mContext.getPackageManager();
- mWindowManagerService = (IWindowManager) ServiceManager.getService(Context.WINDOW_SERVICE);
+ mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
mSecurityPolicy = new SecurityPolicy();
mMainHandler = new MainHandler(mContext.getMainLooper());
//TODO: (multi-display) We need to support multiple displays.
@@ -390,7 +396,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return true; // yes, recycle the event
}
- if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) {
+ if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
mSecurityPolicy.updateEventSourceLocked(event);
mMainHandler.obtainMessage(MainHandler.MSG_UPDATE_ACTIVE_WINDOW,
event.getWindowId(), event.getEventType()).sendToTarget();
@@ -632,11 +638,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mSecurityPolicy.enforceCallingPermission(
Manifest.permission.TEMPORARY_ENABLE_ACCESSIBILITY,
TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED);
- try {
- if (!mWindowManagerService.isKeyguardLocked()) {
- return;
- }
- } catch (RemoteException re) {
+ if (!mWindowManagerService.isKeyguardLocked()) {
return;
}
synchronized (mLock) {
@@ -739,6 +741,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
* @param outBounds The output to which to write the bounds.
*/
boolean getActiveWindowBounds(Rect outBounds) {
+ // TODO: This should be refactored to work with accessibility
+ // focus in multiple windows.
IBinder token;
synchronized (mLock) {
final int windowId = mSecurityPolicy.mActiveWindowId;
@@ -747,13 +751,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
token = getCurrentUserStateLocked().mWindowTokens.get(windowId);
}
}
- try {
- mWindowManagerService.getWindowFrame(token, outBounds);
- if (!outBounds.isEmpty()) {
- return true;
- }
- } catch (RemoteException re) {
- /* ignore */
+ mWindowManagerService.getWindowFrame(token, outBounds);
+ if (!outBounds.isEmpty()) {
+ return true;
}
return false;
}
@@ -771,7 +771,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
void onMagnificationStateChanged() {
- notifyClearAccessibilityNodeInfoCacheLocked();
+ notifyClearAccessibilityCacheLocked();
}
private void switchUser(int userId) {
@@ -879,7 +879,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return false;
}
- private void notifyClearAccessibilityNodeInfoCacheLocked() {
+ private void notifyClearAccessibilityCacheLocked() {
UserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
Service service = state.mBoundServices.get(i);
@@ -887,6 +887,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
+ private void notifyWindowsChangedLocked(List<AccessibilityWindowInfo> windows) {
+ UserState state = getCurrentUserStateLocked();
+ for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+ Service service = state.mBoundServices.get(i);
+ if (mSecurityPolicy.canRetrieveWindowsLocked(service)) {
+ service.notifyWindowsChangedLocked(windows);
+ }
+ }
+ }
+
/**
* Removes an AccessibilityInteractionConnection.
*
@@ -994,7 +1004,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
Service service = state.mBoundServices.get(i);
if (service.mIsDefault == isDefault) {
- if (canDispathEventLocked(service, event, state.mHandledFeedbackTypes)) {
+ if (canDispatchEventToServiceLocked(service, event,
+ state.mHandledFeedbackTypes)) {
state.mHandledFeedbackTypes |= service.mFeedbackType;
service.notifyAccessibilityEvent(event);
}
@@ -1043,7 +1054,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
* @param handledFeedbackTypes The feedback types for which services have been notified.
* @return True if the listener should be notified, false otherwise.
*/
- private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
+ private boolean canDispatchEventToServiceLocked(Service service, AccessibilityEvent event,
int handledFeedbackTypes) {
if (!service.canReceiveEventsLocked()) {
@@ -1232,11 +1243,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
if (setInputFilter) {
- try {
- mWindowManagerService.setInputFilter(inputFilter);
- } catch (RemoteException re) {
- /* ignore */
- }
+ mWindowManagerService.setInputFilter(inputFilter);
}
}
@@ -1296,6 +1303,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mInitialized = true;
updateLegacyCapabilities(userState);
updateServicesLocked(userState);
+ updateWindowsForAccessibilityCallback(userState);
updateFilterKeyEventsLocked(userState);
updateTouchExplorationLocked(userState);
updateEnhancedWebAccessibilityLocked(userState);
@@ -1304,6 +1312,43 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
scheduleUpdateClientsIfNeededLocked(userState);
}
+ private void updateWindowsForAccessibilityCallback(UserState userState) {
+ if (userState.mIsAccessibilityEnabled) {
+ // We observe windows for accessibility only if there is at least
+ // one bound service that can retrieve window content that specified
+ // it is interested in accessing such windows. For services that are
+ // binding we do an update pass after each bind event, so we run this
+ // code and register the callback if needed.
+ boolean boundServiceCanRetrieveInteractiveWindows = false;
+
+ List<Service> boundServices = userState.mBoundServices;
+ final int boundServiceCount = boundServices.size();
+ for (int i = 0; i < boundServiceCount; i++) {
+ Service boundService = boundServices.get(i);
+ if (mSecurityPolicy.canRetrieveWindowContentLocked(boundService)
+ && boundService.mRetrieveInteractiveWindows) {
+ boundServiceCanRetrieveInteractiveWindows = true;
+ break;
+ }
+ }
+
+ if (boundServiceCanRetrieveInteractiveWindows) {
+ if (mWindowsForAccessibilityCallback == null) {
+ mWindowsForAccessibilityCallback = new WindowsForAccessibilityCallback();
+ mWindowManagerService.setWindowsForAccessibilityCallback(
+ mWindowsForAccessibilityCallback);
+ }
+ return;
+ }
+ }
+
+ if (mWindowsForAccessibilityCallback != null) {
+ mWindowsForAccessibilityCallback = null;
+ mWindowManagerService.setWindowsForAccessibilityCallback(
+ mWindowsForAccessibilityCallback);
+ }
+ }
+
private void updateLegacyCapabilities(UserState userState) {
// Up to JB-MR1 we had a white list with services that can enable touch
// exploration. When a service is first started we show a dialog to the
@@ -1435,11 +1480,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
Settings.Secure.TOUCH_EXPLORATION_ENABLED, enabled ? 1 : 0,
userState.mUserId);
}
- try {
- mWindowManagerService.setTouchExplorationEnabled(enabled);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
}
private boolean canRequestAndRequestsTouchExplorationLocked(Service service) {
@@ -1605,6 +1645,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
event.recycle();
} break;
+
case MSG_SEND_KEY_EVENT_TO_INPUT_FILTER: {
KeyEvent event = (KeyEvent) msg.obj;
final int policyFlags = msg.arg1;
@@ -1615,28 +1656,34 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
event.recycle();
} break;
+
case MSG_SEND_STATE_TO_CLIENTS: {
final int clientState = msg.arg1;
final int userId = msg.arg2;
sendStateToClients(clientState, mGlobalClients);
sendStateToClientsForUser(clientState, userId);
} break;
+
case MSG_SEND_CLEARED_STATE_TO_CLIENTS_FOR_USER: {
final int userId = msg.arg1;
sendStateToClientsForUser(0, userId);
} break;
+
case MSG_UPDATE_ACTIVE_WINDOW: {
final int windowId = msg.arg1;
final int eventType = msg.arg2;
mSecurityPolicy.updateActiveWindow(windowId, eventType);
} break;
+
case MSG_ANNOUNCE_NEW_USER_IF_NEEDED: {
announceNewUserIfNeeded();
} break;
+
case MSG_UPDATE_INPUT_FILTER: {
UserState userState = (UserState) msg.obj;
updateInputFilter(userState);
} break;
+
case MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG: {
Service service = (Service) msg.obj;
showEnableTouchExplorationDialog(service);
@@ -1655,7 +1702,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_ANNOUNCEMENT);
event.getText().add(message);
- event.setWindowId(mSecurityPolicy.getRetrievalAllowingWindowLocked());
sendAccessibilityEvent(event, mCurrentUserId);
}
}
@@ -1703,6 +1749,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mPendingEventPool.release(pendingEvent);
}
+ private int findWindowIdLocked(IBinder token) {
+ final int globalIndex = mGlobalWindowTokens.indexOfValue(token);
+ if (globalIndex >= 0) {
+ return mGlobalWindowTokens.keyAt(globalIndex);
+ }
+ UserState userState = getCurrentUserStateLocked();
+ final int userIndex = userState.mWindowTokens.indexOfValue(token);
+ if (userIndex >= 0) {
+ return userState.mWindowTokens.keyAt(userIndex);
+ }
+ return -1;
+ }
+
/**
* This class represents an accessibility service. It stores all per service
* data required for the service management, provides API for starting/stopping the
@@ -1738,6 +1797,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
boolean mRequestFilterKeyEvents;
+ boolean mRetrieveInteractiveWindows;
+
int mFetchFlags;
long mNotificationTimeout;
@@ -1748,7 +1809,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
boolean mIsAutomation;
- final Rect mTempBounds = new Rect();
+ final Rect mTempBounds1 = new Rect();
+
+ final Rect mTempBounds2 = new Rect();
final ResolveInfo mResolveInfo;
@@ -1758,6 +1821,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher();
+ final SparseArray<AccessibilityWindowInfo> mIntrospectedWindows =
+ new SparseArray<AccessibilityWindowInfo>();
+
boolean mWasConnectedAndDied;
// Handler only for dispatching accessibility events since we use event
@@ -1822,7 +1888,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mRequestEnhancedWebAccessibility = (info.flags
& AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY) != 0;
mRequestFilterKeyEvents = (info.flags
- & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
+ & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
+ mRetrieveInteractiveWindows = (info.flags
+ & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
+
+ if (!mRetrieveInteractiveWindows) {
+ clearIntrospectedWindows();
+ }
}
/**
@@ -1932,6 +2004,59 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
@Override
+ public List<AccessibilityWindowInfo> getWindows() {
+ synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.getCallingUserId());
+ if (resolvedUserId != mCurrentUserId) {
+ return null;
+ }
+ final boolean permissionGranted =
+ mSecurityPolicy.canRetrieveWindowsLocked(this);
+ if (!permissionGranted) {
+ return null;
+ }
+ List<AccessibilityWindowInfo> windows = new ArrayList<AccessibilityWindowInfo>();
+ final int windowCount = mSecurityPolicy.mWindows.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(i);
+ AccessibilityWindowInfo windowClone =
+ AccessibilityWindowInfo.obtain(window);
+ windowClone.setConnectionId(mId);
+ mIntrospectedWindows.put(window.getId(), windowClone);
+ windows.add(windowClone);
+ }
+ return windows;
+ }
+ }
+
+ @Override
+ public AccessibilityWindowInfo getWindow(int windowId) {
+ synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.getCallingUserId());
+ if (resolvedUserId != mCurrentUserId) {
+ return null;
+ }
+ final boolean permissionGranted =
+ mSecurityPolicy.canRetrieveWindowsLocked(this);
+ if (!permissionGranted) {
+ return null;
+ }
+ AccessibilityWindowInfo window = mSecurityPolicy.findWindowById(windowId);
+ if (window != null) {
+ AccessibilityWindowInfo windowClone = AccessibilityWindowInfo.obtain(window);
+ windowClone.setConnectionId(mId);
+ mIntrospectedWindows.put(windowId, windowClone);
+ return windowClone;
+ }
+ return null;
+ }
+ }
+
+ @Override
public boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
long accessibilityNodeId, String viewIdResName, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
@@ -1945,12 +2070,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
- final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
+ resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
+ final boolean permissionGranted =
+ mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
return false;
} else {
- resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
connection = getConnectionLocked(resolvedWindowId);
if (connection == null) {
return false;
@@ -1989,7 +2114,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2034,7 +2158,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2079,7 +2202,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2123,7 +2245,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2167,7 +2288,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
resolvedWindowId, action, arguments);
@@ -2365,7 +2485,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
mPendingEvents.remove(eventType);
- if (mSecurityPolicy.canRetrieveWindowContent(this)) {
+ if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) {
event.setConnectionId(mId);
} else {
event.setSource(null);
@@ -2396,12 +2516,69 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
public void notifyClearAccessibilityNodeInfoCache() {
+ clearIntrospectedWindows();
mInvocationHandler.sendEmptyMessage(
- InvocationHandler.MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE);
+ InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
+ }
+
+ private void clearIntrospectedWindows() {
+ final int windowCount = mIntrospectedWindows.size();
+ for (int i = windowCount - 1; i >= 0; i--) {
+ mIntrospectedWindows.valueAt(i).recycle();
+ mIntrospectedWindows.removeAt(i);
+ }
+ }
+
+ public void notifyWindowsChangedLocked(List<AccessibilityWindowInfo> windows) {
+ LongArray changedWindows = mTempLongArray;
+ changedWindows.clear();
+
+ // Figure out which windows the service cares about changed.
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo newState = windows.get(i);
+ final int windowId = newState.getId();
+ AccessibilityWindowInfo oldState = mIntrospectedWindows.get(windowId);
+ if (oldState != null && oldState.changed(newState)) {
+ oldState.recycle();
+ mIntrospectedWindows.put(newState.getId(),
+ AccessibilityWindowInfo.obtain(newState));
+ changedWindows.add(windowId);
+ }
+ }
+
+ // Figure out which windows the service cares about went away.
+ final int introspectedWindowCount = mIntrospectedWindows.size();
+ for (int i = introspectedWindowCount - 1; i >= 0; i--) {
+ AccessibilityWindowInfo window = mIntrospectedWindows.valueAt(i);
+ if (changedWindows.indexOf(window.getId()) < 0 && !windows.contains(window)) {
+ changedWindows.add(window.getId());
+ mIntrospectedWindows.removeAt(i);
+ window.recycle();
+ }
+ }
+
+ int[] windowIds = null;
+
+ final int changedWindowCount = changedWindows.size();
+ if (changedWindowCount > 0) {
+ windowIds = new int[changedWindowCount];
+ for (int i = 0; i < changedWindowCount; i++) {
+ // Cast is fine as we use long array to cache ints.
+ windowIds[i] = (int) changedWindows.get(i);
+ }
+ changedWindows.clear();
+ }
+
+ mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_WINDOWS_CHANGED,
+ windowIds).sendToTarget();
}
private void notifyGestureInternal(int gestureId) {
- IAccessibilityServiceClient listener = mServiceInterface;
+ final IAccessibilityServiceClient listener;
+ synchronized (mLock) {
+ listener = mServiceInterface;
+ }
if (listener != null) {
try {
listener.onGesture(gestureId);
@@ -2416,11 +2593,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mKeyEventDispatcher.notifyKeyEvent(event, policyFlags);
}
- private void notifyClearAccessibilityNodeInfoCacheInternal() {
- IAccessibilityServiceClient listener = mServiceInterface;
+ private void notifyClearAccessibilityCacheInternal() {
+ final IAccessibilityServiceClient listener;
+ synchronized (mLock) {
+ listener = mServiceInterface;
+ }
if (listener != null) {
try {
- listener.clearAccessibilityNodeInfoCache();
+ listener.clearAccessibilityCache();
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error during requesting accessibility info cache"
+ " to be cleared.", re);
@@ -2428,6 +2608,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
+ private void notifyWindowsChangedInternal(int[] windowIds) {
+ final IAccessibilityServiceClient listener;
+ synchronized (mLock) {
+ listener = mServiceInterface;
+ }
+ if (listener != null) {
+ try {
+ listener.onWindowsChanged(windowIds);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error during sending windows to: " + mService, re);
+ }
+ }
+ }
+
private void sendDownAndUpKeyEvents(int keyCode) {
final long token = Binder.clearCallingIdentity();
@@ -2511,27 +2705,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private MagnificationSpec getCompatibleMagnificationSpec(int windowId) {
- try {
- IBinder windowToken = mGlobalWindowTokens.get(windowId);
- if (windowToken == null) {
- windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
- }
- if (windowToken != null) {
- return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
- windowToken);
- }
- } catch (RemoteException re) {
- /* ignore */
+ IBinder windowToken = mGlobalWindowTokens.get(windowId);
+ if (windowToken == null) {
+ windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
+ }
+ if (windowToken != null) {
+ return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
+ windowToken);
}
return null;
}
private final class InvocationHandler extends Handler {
-
public static final int MSG_ON_GESTURE = 1;
public static final int MSG_ON_KEY_EVENT = 2;
- public static final int MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 3;
+ public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 3;
public static final int MSG_ON_KEY_EVENT_TIMEOUT = 4;
+ public static final int MSG_ON_WINDOWS_CHANGED = 5;
public InvocationHandler(Looper looper) {
super(looper, null, true);
@@ -2545,18 +2735,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int gestureId = message.arg1;
notifyGestureInternal(gestureId);
} break;
+
case MSG_ON_KEY_EVENT: {
KeyEvent event = (KeyEvent) message.obj;
final int policyFlags = message.arg1;
notifyKeyEventInternal(event, policyFlags);
} break;
- case MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: {
- notifyClearAccessibilityNodeInfoCacheInternal();
+
+ case MSG_CLEAR_ACCESSIBILITY_CACHE: {
+ notifyClearAccessibilityCacheInternal();
} break;
+
case MSG_ON_KEY_EVENT_TIMEOUT: {
PendingEvent eventState = (PendingEvent) message.obj;
setOnKeyEventResult(false, eventState.sequence);
} break;
+
+ case MSG_ON_WINDOWS_CHANGED: {
+ final int[] windowIds = (int[]) message.obj;
+ notifyWindowsChangedInternal(windowIds);
+ } break;
+
default: {
throw new IllegalArgumentException("Unknown message: " + type);
}
@@ -2700,6 +2899,113 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
+ final class WindowsForAccessibilityCallback implements
+ WindowManagerInternal.WindowsForAccessibilityCallback {
+
+ @Override
+ public void onWindowsForAccessibilityChanged(List<WindowInfo> windows) {
+ synchronized (mLock) {
+ List<WindowInfo> receivedWindows = (List<WindowInfo>) windows;
+
+ // Populate the windows to report.
+ List<AccessibilityWindowInfo> reportedWindows =
+ new ArrayList<AccessibilityWindowInfo>();
+ final int receivedWindowCount = receivedWindows.size();
+ for (int i = 0; i < receivedWindowCount; i++) {
+ WindowInfo receivedWindow = receivedWindows.get(i);
+ AccessibilityWindowInfo reportedWindow = populateReportedWindow(
+ receivedWindow);
+ if (reportedWindow != null) {
+ reportedWindows.add(reportedWindow);
+ }
+ }
+
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Windows changed: " + reportedWindows);
+ }
+
+ // Let the policy update the focused and active windows.
+ mSecurityPolicy.updateWindowsLocked(reportedWindows);
+ }
+ }
+
+ private AccessibilityWindowInfo populateReportedWindow(WindowInfo window) {
+ final int windowId = findWindowIdLocked(window.token);
+ if (windowId < 0) {
+ return null;
+ }
+
+ AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain();
+
+ reportedWindow.setId(windowId);
+ reportedWindow.setType(getTypeForWindowManagerWindowType(window.type));
+ reportedWindow.setLayer(window.layer);
+ reportedWindow.setFocused(window.focused);
+ reportedWindow.setBoundsInScreen(window.boundsInScreen);
+
+ final int parentId = findWindowIdLocked(window.parentToken);
+ if (parentId >= 0) {
+ reportedWindow.setParentId(parentId);
+ }
+
+ if (window.childTokens != null) {
+ final int childCount = window.childTokens.size();
+ for (int i = 0; i < childCount; i++) {
+ IBinder childToken = window.childTokens.get(i);
+ final int childId = findWindowIdLocked(childToken);
+ if (childId >= 0) {
+ reportedWindow.addChild(childId);
+ }
+ }
+ }
+
+ return reportedWindow;
+ }
+
+ private int getTypeForWindowManagerWindowType(int windowType) {
+ switch (windowType) {
+ case WindowManager.LayoutParams.TYPE_APPLICATION:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_STARTING:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+ case WindowManager.LayoutParams.TYPE_BASE_APPLICATION:
+ case WindowManager.LayoutParams.TYPE_PHONE:
+ case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
+ case WindowManager.LayoutParams.TYPE_TOAST:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: {
+ return AccessibilityWindowInfo.TYPE_APPLICATION;
+ }
+
+ case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
+ case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: {
+ return AccessibilityWindowInfo.TYPE_INPUT_METHOD;
+ }
+
+ case WindowManager.LayoutParams.TYPE_KEYGUARD:
+ case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
+ case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
+ case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
+ case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
+ case WindowManager.LayoutParams.TYPE_STATUS_BAR:
+ case WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL:
+ case WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL:
+ case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: {
+ return AccessibilityWindowInfo.TYPE_SYSTEM;
+ }
+
+ default: {
+ return -1;
+ }
+ }
+ }
+ }
+
final class SecurityPolicy {
private static final int VALID_ACTIONS =
AccessibilityNodeInfo.ACTION_CLICK
@@ -2738,17 +3044,37 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
| AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
| AccessibilityEvent.TYPE_VIEW_SCROLLED
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
- | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
+ | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
+ | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
+
+ public final List<AccessibilityWindowInfo> mWindows =
+ new ArrayList<AccessibilityWindowInfo>();
+
+ public int mActiveWindowId;
+ public int mFocusedWindowId;
+ public AccessibilityEvent mShowingFocusedWindowEvent;
- private int mActiveWindowId;
private boolean mTouchInteractionInProgress;
- private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) {
+ private boolean canDispatchAccessibilityEventLocked(AccessibilityEvent event) {
final int eventType = event.getEventType();
switch (eventType) {
// All events that are for changes in a global window
// state should *always* be dispatched.
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
+ if (mWindowsForAccessibilityCallback != null) {
+ // OK, this is fun. Sometimes the focused window is notified
+ // it has focus before being shown. Historically this event
+ // means that the window is focused and can be introspected.
+ // But we still have not gotten the window state from the
+ // window manager, so delay the notification until then.
+ AccessibilityWindowInfo window = findWindowById(event.getWindowId());
+ if (window == null || !window.isFocused()) {
+ mShowingFocusedWindowEvent = AccessibilityEvent.obtain(event);
+ return false;
+ }
+ }
+ // $fall-through$
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
// All events generated by the user touching the
// screen should *always* be dispatched.
@@ -2758,15 +3084,81 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
case AccessibilityEvent.TYPE_GESTURE_DETECTION_END:
case AccessibilityEvent.TYPE_TOUCH_INTERACTION_START:
case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END:
- // These will change the active window, so dispatch.
case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
return true;
}
// All events for changes in window content should be
- // dispatched *only* if this window is the active one.
- default:
- return event.getWindowId() == mActiveWindowId;
+ // dispatched *only* if this window is one of the windows
+ // the accessibility layer reports which are windows
+ // that a sighted user can touch.
+ default: {
+ return isRetrievalAllowingWindow(event.getWindowId());
+ }
+ }
+ }
+
+ public void updateWindowsLocked(List<AccessibilityWindowInfo> windows) {
+ final int oldWindowCount = mWindows.size();
+ for (int i = oldWindowCount - 1; i >= 0; i--) {
+ mWindows.remove(i).recycle();
+ }
+
+ mFocusedWindowId = -1;
+ if (!mTouchInteractionInProgress) {
+ mActiveWindowId = -1;
+ }
+
+ // If the active window goes away while the user is touch exploring we
+ // reset the active window id and wait for the next hover event from
+ // under the user's finger to determine which one is the new one. It
+ // is possible that the finger is not moving and the input system
+ // filters out such events.
+ boolean activeWindowGone = true;
+
+ final int windowCount = windows.size();
+ if (windowCount > 0) {
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = windows.get(i);
+ final int windowId = window.getId();
+ if (window.isFocused()) {
+ mFocusedWindowId = windowId;
+ if (!mTouchInteractionInProgress) {
+ mActiveWindowId = windowId;
+ window.setActive(true);
+ } else if (windowId == mActiveWindowId) {
+ activeWindowGone = false;
+ }
+ }
+ mWindows.add(window);
+ }
+
+ if (mTouchInteractionInProgress && activeWindowGone) {
+ mActiveWindowId = -1;
+ }
+
+ // Focused window may change the active one, so set the
+ // active window once we decided which it is.
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = mWindows.get(i);
+ if (window.getId() == mActiveWindowId) {
+ window.setActive(true);
+ }
+ }
+ }
+
+ notifyWindowsChangedLocked(mWindows);
+
+ // If we are delaying a window state change event as the window
+ // source was showing when it was fired, now is the time to send.
+ if (mShowingFocusedWindowEvent != null) {
+ final int windowId = mShowingFocusedWindowEvent.getWindowId();
+ AccessibilityWindowInfo window = findWindowById(windowId);
+ if (window != null && window.isFocused()) {
+ // Sending does the recycle.
+ sendAccessibilityEvent(mShowingFocusedWindowEvent, mCurrentUserId);
+ }
+ mShowingFocusedWindowEvent = null;
}
}
@@ -2781,67 +3173,96 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// the window that the user is currently touching. If the user is
// touching a window that does not have input focus as soon as the
// the user stops touching that window the focused window becomes
- // the active one.
+ // the active one. Here we detect the touched window and make it
+ // active. In updateWindowsLocked() we update the focused window
+ // and if the user is not touching the screen, we make the focused
+ // window the active one.
switch (eventType) {
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
- if (getFocusedWindowId() == windowId) {
- mActiveWindowId = windowId;
+ // If no service has the capability to introspect screen,
+ // we do not register callback in the window manager for
+ // window changes, so we have to ask the window manager
+ // what the focused window is to update the active one.
+ // The active window also determined events from which
+ // windows are delivered.
+ boolean focusedWindowActive = false;
+ synchronized (mLock) {
+ if (mWindowsForAccessibilityCallback == null) {
+ focusedWindowActive = true;
+ }
+ }
+ if (focusedWindowActive) {
+ if (windowId == getFocusedWindowId()) {
+ synchronized (mLock) {
+ mActiveWindowId = windowId;
+ }
+ }
}
} break;
+
case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: {
// Do not allow delayed hover events to confuse us
// which the active window is.
- if (mTouchInteractionInProgress) {
- mActiveWindowId = windowId;
+ synchronized (mLock) {
+ if (mTouchInteractionInProgress && mActiveWindowId != windowId) {
+ setActiveWindowLocked(windowId);
+ }
}
} break;
}
}
public void onTouchInteractionStart() {
- mTouchInteractionInProgress = true;
+ synchronized (mLock) {
+ mTouchInteractionInProgress = true;
+ }
}
public void onTouchInteractionEnd() {
- mTouchInteractionInProgress = false;
- // We want to set the active window to be current immediately
- // after the user has stopped touching the screen since if the
- // user types with the IME he should get a feedback for the
- // letter typed in the text view which is in the input focused
- // window. Note that we always deliver hover accessibility events
- // (they are a result of user touching the screen) so change of
- // the active window before all hover accessibility events from
- // the touched window are delivered is fine.
- mActiveWindowId = getFocusedWindowId();
+ synchronized (mLock) {
+ mTouchInteractionInProgress = false;
+ // We want to set the active window to be current immediately
+ // after the user has stopped touching the screen since if the
+ // user types with the IME he should get a feedback for the
+ // letter typed in the text view which is in the input focused
+ // window. Note that we always deliver hover accessibility events
+ // (they are a result of user touching the screen) so change of
+ // the active window before all hover accessibility events from
+ // the touched window are delivered is fine.
+ setActiveWindowLocked(mFocusedWindowId);
+ }
}
- public int getRetrievalAllowingWindowLocked() {
- return mActiveWindowId;
+ private void setActiveWindowLocked(int windowId) {
+ if (mActiveWindowId != windowId) {
+ mActiveWindowId = windowId;
+ final int windowCount = mWindows.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = mWindows.get(i);
+ window.setActive(window.getId() == windowId);
+ }
+ notifyWindowsChangedLocked(mWindows);
+ }
}
public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
- return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId);
+ return canRetrieveWindowContentLocked(service) && isRetrievalAllowingWindow(windowId);
}
public boolean canPerformActionLocked(Service service, int windowId, int action,
Bundle arguments) {
- return canRetrieveWindowContent(service)
+ return canRetrieveWindowContentLocked(service)
&& isRetrievalAllowingWindow(windowId)
&& isActionPermitted(action);
}
- public boolean canRetrieveWindowContent(Service service) {
- return (service.mAccessibilityServiceInfo.getCapabilities()
- & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
+ public boolean canRetrieveWindowsLocked(Service service) {
+ return canRetrieveWindowContentLocked(service) && service.mRetrieveInteractiveWindows;
}
- public void enforceCanRetrieveWindowContent(Service service) throws RemoteException {
- // This happens due to incorrect registration so make it apparent.
- if (!canRetrieveWindowContent(service)) {
- Slog.e(LOG_TAG, "Accessibility serivce " + service.mComponentName + " does not " +
- "declare android:canRetrieveWindowContent.");
- throw new RemoteException();
- }
+ public boolean canRetrieveWindowContentLocked(Service service) {
+ return (service.mAccessibilityServiceInfo.getCapabilities()
+ & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
}
public int resolveCallingUserIdEnforcingPermissionsLocked(int userId) {
@@ -2878,7 +3299,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private boolean isRetrievalAllowingWindow(int windowId) {
- return (mActiveWindowId == windowId);
+ if (windowId == mActiveWindowId) {
+ return true;
+ }
+ return findWindowById(windowId) != null;
+ }
+
+ private AccessibilityWindowInfo findWindowById(int windowId) {
+ final int windowCount = mWindows.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = mWindows.get(i);
+ if (window.getId() == windowId) {
+ return window;
+ }
+ }
+ return null;
}
private boolean isActionPermitted(int action) {
@@ -2901,35 +3336,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private int getFocusedWindowId() {
- try {
- // We call this only on window focus change or after touch
- // exploration gesture end and the shown windows are not that
- // many, so the linear look up is just fine.
- IBinder token = mWindowManagerService.getFocusedWindowToken();
- if (token != null) {
- synchronized (mLock) {
- int windowId = getFocusedWindowIdLocked(token, mGlobalWindowTokens);
- if (windowId < 0) {
- windowId = getFocusedWindowIdLocked(token,
- getCurrentUserStateLocked().mWindowTokens);
- }
- return windowId;
- }
- }
- } catch (RemoteException re) {
- /* ignore */
- }
- return -1;
- }
-
- private int getFocusedWindowIdLocked(IBinder token, SparseArray<IBinder> windows) {
- final int windowCount = windows.size();
- for (int i = 0; i < windowCount; i++) {
- if (windows.valueAt(i) == token) {
- return windows.keyAt(i);
- }
- }
- return -1;
+ IBinder token = mWindowManagerService.getFocusedWindowToken();
+ return findWindowIdLocked(token);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
index 5f12cf4..c8b080e 100644
--- a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -29,8 +29,6 @@ import android.os.AsyncTask;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.TextUtils;
@@ -38,8 +36,6 @@ import android.util.Property;
import android.util.Slog;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
-import android.view.IMagnificationCallbacks;
-import android.view.IWindowManager;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
@@ -48,10 +44,12 @@ import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.WindowManagerInternal;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.DecelerateInterpolator;
import com.android.internal.os.SomeArgs;
+import com.android.server.LocalServices;
import java.util.Locale;
@@ -94,8 +92,8 @@ import java.util.Locale;
*
* 6. The magnification scale will be persisted in settings and in the cloud.
*/
-public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
- implements EventStreamTransformation {
+public final class ScreenMagnifier implements WindowManagerInternal.MagnificationCallbacks,
+ EventStreamTransformation {
private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName();
@@ -127,7 +125,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
private final Rect mTempRect1 = new Rect();
private final Context mContext;
- private final IWindowManager mWindowManager;
+ private final WindowManagerInternal mWindowManager;
private final MagnificationController mMagnificationController;
private final ScreenStateObserver mScreenStateObserver;
@@ -191,8 +189,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
public ScreenMagnifier(Context context, int displayId, AccessibilityManagerService service) {
mContext = context;
- mWindowManager = IWindowManager.Stub.asInterface(
- ServiceManager.getService("window"));
+ mWindowManager = LocalServices.getService(WindowManagerInternal.class);
mAms = service;
mLongAnimationDuration = context.getResources().getInteger(
@@ -208,11 +205,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
mMagnificationController = new MagnificationController(mLongAnimationDuration);
mScreenStateObserver = new ScreenStateObserver(context, mMagnificationController);
- try {
- mWindowManager.setMagnificationCallbacks(this);
- } catch (RemoteException re) {
- /* ignore */
- }
+ mWindowManager.setMagnificationCallbacks(this);
transitionToState(STATE_DETECTING);
}
@@ -378,11 +371,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
@Override
public void onDestroy() {
mScreenStateObserver.destroy();
- try {
- mWindowManager.setMagnificationCallbacks(null);
- } catch (RemoteException re) {
- /* ignore */
- }
+ mWindowManager.setMagnificationCallbacks(null);
}
private void handleMotionEventStateDelegating(MotionEvent event,
@@ -462,7 +451,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
}
return mTempPointerProperties;
}
-
+
private void transitionToState(int state) {
if (DEBUG_STATE_TRANSITIONS) {
switch (state) {
@@ -1120,15 +1109,10 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
if (DEBUG_SET_MAGNIFICATION_SPEC) {
Slog.i(LOG_TAG, "Sending: " + spec);
}
- try {
- mSentMagnificationSpec.scale = spec.scale;
- mSentMagnificationSpec.offsetX = spec.offsetX;
- mSentMagnificationSpec.offsetY = spec.offsetY;
- mWindowManager.setMagnificationSpec(
- MagnificationSpec.obtain(spec));
- } catch (RemoteException re) {
- /* ignore */
- }
+ mSentMagnificationSpec.scale = spec.scale;
+ mSentMagnificationSpec.offsetX = spec.offsetX;
+ mSentMagnificationSpec.offsetY = spec.offsetY;
+ mWindowManager.setMagnificationSpec(MagnificationSpec.obtain(spec));
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
index e208677..77d5076 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
@@ -23,7 +23,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -38,18 +37,19 @@ import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.AppWidgetBackupBridge;
+import com.android.server.WidgetBackupProvider;
import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
-import java.util.Locale;
/**
* SystemService that publishes an IAppWidgetService.
*/
-public class AppWidgetService extends SystemService {
+public class AppWidgetService extends SystemService implements WidgetBackupProvider {
static final String TAG = "AppWidgetService";
@@ -72,6 +72,7 @@ public class AppWidgetService extends SystemService {
@Override
public void onStart() {
publishBinderService(Context.APPWIDGET_SERVICE, mServiceImpl);
+ AppWidgetBackupBridge.register(this);
}
@Override
@@ -81,13 +82,40 @@ public class AppWidgetService extends SystemService {
}
}
+
+ // backup <-> app widget service bridge surface
+ @Override
+ public List<String> getWidgetParticipants(int userId) {
+ return mServiceImpl.getWidgetParticipants(userId);
+ }
+
+ @Override
+ public byte[] getWidgetState(String packageName, int userId) {
+ return mServiceImpl.getWidgetState(packageName, userId);
+ }
+
+ @Override
+ public void restoreStarting(int userId) {
+ mServiceImpl.restoreStarting(userId);
+ }
+
+ @Override
+ public void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
+ mServiceImpl.restoreWidgetState(packageName, restoredState, userId);
+ }
+
+ @Override
+ public void restoreFinished(int userId) {
+ mServiceImpl.restoreFinished(userId);
+ }
+
+
+ // implementation entry point and binder service
private final AppWidgetServiceStub mServiceImpl = new AppWidgetServiceStub();
- private class AppWidgetServiceStub extends IAppWidgetService.Stub {
+ class AppWidgetServiceStub extends IAppWidgetService.Stub {
private boolean mSafeMode;
- private Locale mLocale;
- private PackageManager mPackageManager;
public void systemRunning(boolean safeMode) {
mSafeMode = safeMode;
@@ -227,6 +255,29 @@ public class AppWidgetService extends SystemService {
}
}
+
+ // support of the widget/backup bridge
+ public List<String> getWidgetParticipants(int userId) {
+ return getImplForUser(userId).getWidgetParticipants();
+ }
+
+ public byte[] getWidgetState(String packageName, int userId) {
+ return getImplForUser(userId).getWidgetState(packageName);
+ }
+
+ public void restoreStarting(int userId) {
+ getImplForUser(userId).restoreStarting();
+ }
+
+ public void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
+ getImplForUser(userId).restoreWidgetState(packageName, restoredState);
+ }
+
+ public void restoreFinished(int userId) {
+ getImplForUser(userId).restoreFinished();
+ }
+
+
private void checkPermission(int userId) {
int realUserId = ActivityManager.handleIncomingUser(
Binder.getCallingPid(),
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b6391b6..b84df79 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -31,6 +31,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
@@ -51,6 +52,7 @@ import android.util.AtomicFile;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TypedValue;
import android.util.Xml;
import android.view.Display;
@@ -66,6 +68,8 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -79,8 +83,11 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
+import java.util.Map.Entry;
import java.util.Set;
+import libcore.util.MutableInt;
+
class AppWidgetServiceImpl {
private static final String KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
@@ -89,8 +96,10 @@ class AppWidgetServiceImpl {
private static final String SETTINGS_FILENAME = "appwidgets.xml";
private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
private static final int CURRENT_VERSION = 1; // Bump if the stored widgets need to be upgraded.
+ private static final int WIDGET_STATE_VERSION = 1; // version of backed-up widget state
- private static boolean DBG = false;
+ private static boolean DBG = true;
+ private static boolean DEBUG_BACKUP = DBG || true;
/*
* When identifying a Host or Provider based on the calling process, use the uid field. When
@@ -105,6 +114,25 @@ class AppWidgetServiceImpl {
boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
int tag; // for use while saving state (the index)
+
+ // is there an instance of this provider hosted by the given app?
+ public boolean isHostedBy(String packageName) {
+ final int N = instances.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = instances.get(i);
+ if (packageName.equals(id.host.packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "Provider{" + ((info == null) ? "null" : info.provider)
+ + (zombie ? " Z" : "")
+ + '}';
+ }
}
static class Host {
@@ -125,14 +153,62 @@ class AppWidgetServiceImpl {
return this.uid == callingUid;
}
}
+
+ boolean hostsPackage(String pkg) {
+ final int N = instances.size();
+ for (int i = 0; i < N; i++) {
+ Provider p = instances.get(i).provider;
+ if (p.info != null && pkg.equals(p.info.provider.getPackageName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "Host{" + packageName + ":" + hostId + '}';
+ }
}
static class AppWidgetId {
int appWidgetId;
+ int restoredId; // tracking & remapping any restored state
Provider provider;
RemoteViews views;
Bundle options;
Host host;
+
+ @Override
+ public String toString() {
+ return "AppWidgetId{" + appWidgetId + ':' + host + ':' + provider + '}';
+ }
+ }
+
+ AppWidgetId findRestoredWidgetLocked(int restoredId, Host host, Provider p) {
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Find restored widget: id=" + restoredId
+ + " host=" + host + " provider=" + p);
+ }
+
+ if (p == null || host == null) {
+ return null;
+ }
+
+ final int N = mAppWidgetIds.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId widget = mAppWidgetIds.get(i);
+ if (widget.restoredId == restoredId
+ && widget.host.hostId == host.hostId
+ && widget.host.packageName.equals(host.packageName)
+ && widget.provider.info.provider.equals(p.info.provider)) {
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, " Found at " + i + " : " + widget);
+ }
+ return widget;
+ }
+ }
+ return null;
}
/**
@@ -194,6 +270,22 @@ class AppWidgetServiceImpl {
boolean mStateLoaded;
int mMaxWidgetBitmapMemory;
+ // Map old (restored) widget IDs to new AppWidgetId instances. This object is used
+ // as the lock around manipulation of the overall restored-widget state, just as
+ // mAppWidgetIds is used as the lock object around all "live" widget state
+ // manipulations. Methods that must be called with this lock held are decorated
+ // with the suffix "Lr".
+ //
+ // In cases when both of those locks must be held concurrently, mRestoredWidgetIds
+ // must be acquired *first.*
+ private final SparseArray<AppWidgetId> mRestoredWidgetIds = new SparseArray<AppWidgetId>();
+
+ // We need to make sure to wipe the pre-restore widget state only once for
+ // a given package. Keep track of what we've done so far here; the list is
+ // cleared at the start of every system restore pass, but preserved through
+ // any install-time restore operations.
+ HashSet<String> mPrunedApps = new HashSet<String>();
+
private final Handler mSaveStateHandler;
// These are for debugging only -- widgets are going missing in some rare instances
@@ -300,10 +392,19 @@ class AppWidgetServiceImpl {
providersModified |= updateProvidersForPackageLocked(pkgName, null);
}
} else {
- // The package was just added
+ // The package was just added. Fix up the providers...
for (String pkgName : pkgList) {
providersModified |= addProvidersForPackageLocked(pkgName);
}
+ // ...and see if these are hosts we've been awaiting
+ for (String pkg : pkgList) {
+ try {
+ int uid = getUidForPackage(pkg);
+ resolveHostUidLocked(pkg, uid);
+ } catch (NameNotFoundException e) {
+ // shouldn't happen; we just installed it!
+ }
+ }
}
saveStateAsync();
}
@@ -331,6 +432,19 @@ class AppWidgetServiceImpl {
}
}
+ void resolveHostUidLocked(String pkg, int uid) {
+ final int N = mHosts.size();
+ for (int i = 0; i < N; i++) {
+ Host h = mHosts.get(i);
+ if (h.uid == -1 && pkg.equals(h.packageName)) {
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "host " + pkg + ":" + h.hostId + " resolved to uid " + uid);
+ }
+ h.uid = uid;
+ }
+ }
+ }
+
private void dumpProvider(Provider p, int index, PrintWriter pw) {
AppWidgetProviderInfo info = p.info;
pw.print(" ["); pw.print(index); pw.print("] provider ");
@@ -433,7 +547,7 @@ class AppWidgetServiceImpl {
if (!mHasFeature) {
return;
}
- loadAppWidgetListLocked();
+ loadWidgetProviderListLocked();
loadStateLocked();
mStateLoaded = true;
}
@@ -516,6 +630,7 @@ class AppWidgetServiceImpl {
}
void deleteHostLocked(Host host) {
+ if (DBG) log("Deleting host " + host);
final int N = host.instances.size();
for (int i = N - 1; i >= 0; i--) {
AppWidgetId id = host.instances.get(i);
@@ -719,7 +834,7 @@ class AppWidgetServiceImpl {
}
final ComponentName componentName = intent.getComponent();
try {
- final ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(componentName,
+ final ServiceInfo si = mPm.getServiceInfo(componentName,
PackageManager.GET_PERMISSIONS, mUserId);
if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
throw new SecurityException("Selected service does not require "
@@ -981,7 +1096,6 @@ class AppWidgetServiceImpl {
if (!mHasFeature) {
return;
}
- options = cloneIfLocalBinder(options);
ensureStateLoadedLocked();
AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
@@ -991,7 +1105,7 @@ class AppWidgetServiceImpl {
Provider p = id.provider;
// Merge the options
- id.options.putAll(options);
+ id.options.putAll(cloneIfLocalBinder(options));
// send the broacast saying that this appWidgetId has been deleted
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
@@ -1278,9 +1392,13 @@ class AppWidgetServiceImpl {
}
Provider lookupProviderLocked(ComponentName provider) {
- final int N = mInstalledProviders.size();
+ return lookupProviderLocked(provider, mInstalledProviders);
+ }
+
+ Provider lookupProviderLocked(ComponentName provider, ArrayList<Provider> installedProviders) {
+ final int N = installedProviders.size();
for (int i = 0; i < N; i++) {
- Provider p = mInstalledProviders.get(i);
+ Provider p = installedProviders.get(i);
if (p.info.provider.equals(provider)) {
return p;
}
@@ -1317,11 +1435,12 @@ class AppWidgetServiceImpl {
void pruneHostLocked(Host host) {
if (host.instances.size() == 0 && host.callbacks == null) {
+ if (DBG) log("Pruning host " + host);
mHosts.remove(host);
}
}
- void loadAppWidgetListLocked() {
+ void loadWidgetProviderListLocked() {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
try {
List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
@@ -1348,7 +1467,23 @@ class AppWidgetServiceImpl {
Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
ri.activityInfo.name), ri);
if (p != null) {
- mInstalledProviders.add(p);
+ // we might have an inactive entry for this provider already due to
+ // a preceding restore operation. if so, fix it up in place; otherwise
+ // just add this new one.
+ Provider existing = lookupProviderLocked(p.info.provider);
+ if (existing != null) {
+ if (existing.zombie && !mSafeMode) {
+ // it's a placeholder that was set up during an app restore
+ existing.zombie = false;
+ existing.info = p.info; // the real one filled out from the ResolveInfo
+ existing.uid = p.uid;
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Provider placeholder now reified: " + existing);
+ }
+ }
+ } else {
+ mInstalledProviders.add(p);
+ }
return true;
} else {
return false;
@@ -1463,6 +1598,554 @@ class AppWidgetServiceImpl {
}
}
+ public List<String> getWidgetParticipants() {
+ HashSet<String> packages = new HashSet<String>();
+ synchronized (mAppWidgetIds) {
+ final int N = mAppWidgetIds.size();
+ for (int i = 0; i < N; i++) {
+ final AppWidgetId id = mAppWidgetIds.get(i);
+ packages.add(id.host.packageName);
+ packages.add(id.provider.info.provider.getPackageName());
+ }
+ }
+ return new ArrayList<String>(packages);
+ }
+
+ private void serializeProvider(XmlSerializer out, Provider p) throws IOException {
+ out.startTag(null, "p");
+ out.attribute(null, "pkg", p.info.provider.getPackageName());
+ out.attribute(null, "cl", p.info.provider.getClassName());
+ out.endTag(null, "p");
+ }
+
+ private void serializeHost(XmlSerializer out, Host host) throws IOException {
+ out.startTag(null, "h");
+ out.attribute(null, "pkg", host.packageName);
+ out.attribute(null, "id", Integer.toHexString(host.hostId));
+ out.endTag(null, "h");
+ }
+
+ private void serializeAppWidgetId(XmlSerializer out, AppWidgetId id) throws IOException {
+ out.startTag(null, "g");
+ out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
+ out.attribute(null, "rid", Integer.toHexString(id.restoredId));
+ out.attribute(null, "h", Integer.toHexString(id.host.tag));
+ if (id.provider != null) {
+ out.attribute(null, "p", Integer.toHexString(id.provider.tag));
+ }
+ if (id.options != null) {
+ out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
+ AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
+ out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
+ AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
+ out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
+ AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
+ out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
+ AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
+ out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
+ AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
+ }
+ out.endTag(null, "g");
+ }
+
+ private Bundle parseWidgetIdOptions(XmlPullParser parser) {
+ Bundle options = new Bundle();
+ String minWidthString = parser.getAttributeValue(null, "min_width");
+ if (minWidthString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
+ Integer.parseInt(minWidthString, 16));
+ }
+ String minHeightString = parser.getAttributeValue(null, "min_height");
+ if (minHeightString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
+ Integer.parseInt(minHeightString, 16));
+ }
+ String maxWidthString = parser.getAttributeValue(null, "max_width");
+ if (maxWidthString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
+ Integer.parseInt(maxWidthString, 16));
+ }
+ String maxHeightString = parser.getAttributeValue(null, "max_height");
+ if (maxHeightString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
+ Integer.parseInt(maxHeightString, 16));
+ }
+ String categoryString = parser.getAttributeValue(null, "host_category");
+ if (categoryString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+ Integer.parseInt(categoryString, 16));
+ }
+ return options;
+ }
+
+ // Does this package either host or provide any active widgets?
+ private boolean packageNeedsWidgetBackupLocked(String packageName) {
+ int N = mAppWidgetIds.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = mAppWidgetIds.get(i);
+ if (packageName.equals(id.host.packageName)) {
+ // this package is hosting widgets, so it knows widget IDs
+ return true;
+ }
+ Provider p = id.provider;
+ if (p != null && packageName.equals(p.info.provider.getPackageName())) {
+ // someone is hosting this app's widgets, so it knows widget IDs
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // build the widget-state blob that we save for the app during backup.
+ public byte[] getWidgetState(String backupTarget) {
+ if (!mHasFeature) {
+ return null;
+ }
+
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ synchronized (mAppWidgetIds) {
+ // Preflight: if this app neither hosts nor provides any live widgets
+ // we have no work to do.
+ if (!packageNeedsWidgetBackupLocked(backupTarget)) {
+ return null;
+ }
+
+ try {
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, "ws"); // widget state
+ out.attribute(null, "version", String.valueOf(WIDGET_STATE_VERSION));
+ out.attribute(null, "pkg", backupTarget);
+
+ // Remember all the providers that are currently hosted or published
+ // by this package: that is, all of the entities related to this app
+ // which will need to be told about id remapping.
+ int N = mInstalledProviders.size();
+ int index = 0;
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (p.instances.size() > 0) {
+ if (backupTarget.equals(p.info.provider.getPackageName())
+ || p.isHostedBy(backupTarget)) {
+ serializeProvider(out, p);
+ p.tag = index++;
+ }
+ }
+ }
+
+ N = mHosts.size();
+ index = 0;
+ for (int i = 0; i < N; i++) {
+ Host host = mHosts.get(i);
+ if (backupTarget.equals(host.packageName)
+ || host.hostsPackage(backupTarget)) {
+ serializeHost(out, host);
+ host.tag = index++;
+ }
+ }
+
+ // All widget instances involving this package,
+ // either as host or as provider
+ N = mAppWidgetIds.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = mAppWidgetIds.get(i);
+ if (backupTarget.equals(id.host.packageName)
+ || backupTarget.equals(id.provider.info.provider.getPackageName())) {
+ serializeAppWidgetId(out, id);
+ }
+ }
+
+ out.endTag(null, "ws");
+ out.endDocument();
+ } catch (IOException e) {
+ Slog.w(TAG, "Unable to save widget state for " + backupTarget);
+ return null;
+ }
+
+ }
+ return stream.toByteArray();
+ }
+
+ public void restoreStarting() {
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "restore starting for user " + mUserId);
+ }
+ synchronized (mRestoredWidgetIds) {
+ // We're starting a new "system" restore operation, so any widget restore
+ // state that we see from here on is intended to replace the current
+ // widget configuration of any/all of the affected apps.
+ mPrunedApps.clear();
+ mUpdatesByProvider.clear();
+ mUpdatesByHost.clear();
+ }
+ }
+
+ // We're restoring widget state for 'pkg', so we start by wiping (a) all widget
+ // instances that are hosted by that app, and (b) all instances in other hosts
+ // for which 'pkg' is the provider. We assume that we'll be restoring all of
+ // these hosts & providers, so will be reconstructing a correct live state.
+ private void pruneWidgetStateLr(String pkg) {
+ if (!mPrunedApps.contains(pkg)) {
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "pruning widget state for restoring package " + pkg);
+ }
+ for (int i = mAppWidgetIds.size() - 1; i >= 0; i--) {
+ AppWidgetId id = mAppWidgetIds.get(i);
+ Provider p = id.provider;
+ if (id.host.packageName.equals(pkg)
+ || p.info.provider.getPackageName().equals(pkg)) {
+ // 'pkg' is either the host or the provider for this instances,
+ // so we tear it down in anticipation of it (possibly) being
+ // reconstructed due to the restore
+ p.instances.remove(id);
+
+ unbindAppWidgetRemoteViewsServicesLocked(id);
+ mAppWidgetIds.remove(i);
+ }
+ }
+ mPrunedApps.add(pkg);
+ } else {
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "already pruned " + pkg + ", continuing normally");
+ }
+ }
+ }
+
+ // Accumulate a list of updates that affect the given provider for a final
+ // coalesced notification broadcast once restore is over.
+ class RestoreUpdateRecord {
+ public int oldId;
+ public int newId;
+ public boolean notified;
+
+ public RestoreUpdateRecord(int theOldId, int theNewId) {
+ oldId = theOldId;
+ newId = theNewId;
+ notified = false;
+ }
+ }
+
+ HashMap<Provider, ArrayList<RestoreUpdateRecord>> mUpdatesByProvider
+ = new HashMap<Provider, ArrayList<RestoreUpdateRecord>>();
+ HashMap<Host, ArrayList<RestoreUpdateRecord>> mUpdatesByHost
+ = new HashMap<Host, ArrayList<RestoreUpdateRecord>>();
+
+ private boolean alreadyStashed(ArrayList<RestoreUpdateRecord> stash,
+ final int oldId, final int newId) {
+ final int N = stash.size();
+ for (int i = 0; i < N; i++) {
+ RestoreUpdateRecord r = stash.get(i);
+ if (r.oldId == oldId && r.newId == newId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void stashProviderRestoreUpdateLr(Provider provider, int oldId, int newId) {
+ ArrayList<RestoreUpdateRecord> r = mUpdatesByProvider.get(provider);
+ if (r == null) {
+ r = new ArrayList<RestoreUpdateRecord>();
+ mUpdatesByProvider.put(provider, r);
+ } else {
+ // don't duplicate
+ if (alreadyStashed(r, oldId, newId)) {
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "ID remap " + oldId + " -> " + newId
+ + " already stashed for " + provider);
+ }
+ return;
+ }
+ }
+ r.add(new RestoreUpdateRecord(oldId, newId));
+ }
+
+ private void stashHostRestoreUpdateLr(Host host, int oldId, int newId) {
+ ArrayList<RestoreUpdateRecord> r = mUpdatesByHost.get(host);
+ if (r == null) {
+ r = new ArrayList<RestoreUpdateRecord>();
+ mUpdatesByHost.put(host, r);
+ } else {
+ if (alreadyStashed(r, oldId, newId)) {
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "ID remap " + oldId + " -> " + newId
+ + " already stashed for " + host);
+ }
+ return;
+ }
+ }
+ r.add(new RestoreUpdateRecord(oldId, newId));
+ }
+
+ public void restoreWidgetState(String packageName, byte[] restoredState) {
+ if (!mHasFeature) {
+ return;
+ }
+
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Restoring widget state for " + packageName);
+ }
+
+ ByteArrayInputStream stream = new ByteArrayInputStream(restoredState);
+ try {
+ // Providers mentioned in the widget dataset by ordinal
+ ArrayList<Provider> restoredProviders = new ArrayList<Provider>();
+
+ // Hosts mentioned in the widget dataset by ordinal
+ ArrayList<Host> restoredHosts = new ArrayList<Host>();
+
+ //HashSet<String> toNotify = new HashSet<String>();
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+
+ synchronized (mAppWidgetIds) {
+ synchronized (mRestoredWidgetIds) {
+ int type;
+ do {
+ type = parser.next();
+ if (type == XmlPullParser.START_TAG) {
+ final String tag = parser.getName();
+ if ("ws".equals(tag)) {
+ String v = parser.getAttributeValue(null, "version");
+ String pkg = parser.getAttributeValue(null, "pkg");
+
+ // TODO: fix up w.r.t. canonical vs current package names
+ if (!packageName.equals(pkg)) {
+ Slog.w(TAG, "Package mismatch in ws");
+ return;
+ }
+
+ int version = Integer.parseInt(v);
+ if (version > WIDGET_STATE_VERSION) {
+ Slog.w(TAG, "Unable to process state version " + version);
+ return;
+ }
+ } else if ("p".equals(tag)) {
+ String pkg = parser.getAttributeValue(null, "pkg");
+ String cl = parser.getAttributeValue(null, "cl");
+
+ // hostedProviders index will match 'p' attribute in widget's
+ // entry in the xml file being restored
+ // If there's no live entry for this provider, add an inactive one
+ // so that widget IDs referring to them can be properly allocated
+ final ComponentName cn = new ComponentName(pkg, cl);
+ Provider p = lookupProviderLocked(cn, mInstalledProviders);
+ if (p == null) {
+ p = new Provider();
+ p.info = new AppWidgetProviderInfo();
+ p.info.provider = cn;
+ p.zombie = true;
+ mInstalledProviders.add(p);
+ }
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, " provider " + cn);
+ }
+ restoredProviders.add(p);
+ } else if ("h".equals(tag)) {
+ // The host app may not yet exist on the device. If it's here we
+ // just use the existing Host entry, otherwise we create a
+ // placeholder whose uid will be fixed up at PACKAGE_ADDED time.
+ String pkg = parser.getAttributeValue(null, "pkg");
+ int uid;
+ try {
+ uid = getUidForPackage(pkg);
+ } catch (NameNotFoundException e) {
+ uid = -1;
+ }
+ int hostId = Integer.parseInt(
+ parser.getAttributeValue(null, "id"), 16);
+ Host h = lookupOrAddHostLocked(uid, pkg, hostId);
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, " host[" + restoredHosts.size()
+ + "]: {" + h.packageName + ":" + h.hostId + "}");
+ }
+ restoredHosts.add(h);
+ } else if ("g".equals(tag)) {
+ int restoredId = Integer.parseInt(
+ parser.getAttributeValue(null, "id"), 16);
+ int hostIndex = Integer.parseInt(
+ parser.getAttributeValue(null, "h"), 16);
+ Host host = restoredHosts.get(hostIndex);
+ Provider p = null;
+ String prov = parser.getAttributeValue(null, "p");
+ if (prov != null) {
+ // could have been null if the app had allocated an id
+ // but not yet established a binding under that id
+ int which = Integer.parseInt(prov, 16);
+ p = restoredProviders.get(which);
+ }
+
+ // We'll be restoring widget state for both the host and
+ // provider sides of this widget ID, so make sure we are
+ // beginning from a clean slate on both fronts.
+ pruneWidgetStateLr(host.packageName);
+ if (p != null) {
+ pruneWidgetStateLr(p.info.provider.getPackageName());
+ }
+
+ // Have we heard about this ancestral widget instance before?
+ AppWidgetId id = findRestoredWidgetLocked(restoredId, host, p);
+ if (id == null) {
+ id = new AppWidgetId();
+ id.appWidgetId = mNextAppWidgetId++;
+ id.restoredId = restoredId;
+ id.options = parseWidgetIdOptions(parser);
+ id.host = host;
+ id.host.instances.add(id);
+ id.provider = p;
+ if (id.provider != null) {
+ id.provider.instances.add(id);
+ }
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "New restored id " + restoredId
+ + " now " + id);
+ }
+ mAppWidgetIds.add(id);
+ }
+ if (id.provider.info != null) {
+ stashProviderRestoreUpdateLr(id.provider,
+ restoredId, id.appWidgetId);
+ } else {
+ Slog.w(TAG, "Missing provider for restored widget " + id);
+ }
+ stashHostRestoreUpdateLr(id.host, restoredId, id.appWidgetId);
+
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, " instance: " + restoredId
+ + " -> " + id.appWidgetId
+ + " :: p=" + id.provider);
+ }
+ }
+ }
+ } while (type != XmlPullParser.END_DOCUMENT);
+
+ // We've updated our own bookkeeping. We'll need to notify the hosts and
+ // providers about the changes, but we can't do that yet because the restore
+ // target is not necessarily fully live at this moment. Set aside the
+ // information for now; the backup manager will call us once more at the
+ // end of the process when all of the targets are in a known state, and we
+ // will update at that point.
+ }
+ }
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "Unable to restore widget state for " + packageName);
+ } catch (IOException e) {
+ Slog.w(TAG, "Unable to restore widget state for " + packageName);
+ } finally {
+ saveStateAsync();
+ }
+ }
+
+ // Called once following the conclusion of a restore operation. This is when we
+ // send out updates to apps involved in widget-state restore telling them about
+ // the new widget ID space.
+ public void restoreFinished() {
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "restoreFinished for " + mUserId);
+ }
+
+ final UserHandle userHandle = new UserHandle(mUserId);
+ synchronized (mRestoredWidgetIds) {
+ // Build the providers' broadcasts and send them off
+ Set<Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
+ = mUpdatesByProvider.entrySet();
+ for (Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
+ // For each provider there's a list of affected IDs
+ Provider provider = e.getKey();
+ ArrayList<RestoreUpdateRecord> updates = e.getValue();
+ final int pending = countPendingUpdates(updates);
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Provider " + provider + " pending: " + pending);
+ }
+ if (pending > 0) {
+ int[] oldIds = new int[pending];
+ int[] newIds = new int[pending];
+ final int N = updates.size();
+ int nextPending = 0;
+ for (int i = 0; i < N; i++) {
+ RestoreUpdateRecord r = updates.get(i);
+ if (!r.notified) {
+ r.notified = true;
+ oldIds[nextPending] = r.oldId;
+ newIds[nextPending] = r.newId;
+ nextPending++;
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, " " + r.oldId + " => " + r.newId);
+ }
+ }
+ }
+ sendWidgetRestoreBroadcast(AppWidgetManager.ACTION_APPWIDGET_RESTORED,
+ provider, null, oldIds, newIds, userHandle);
+ }
+ }
+
+ // same thing per host
+ Set<Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
+ = mUpdatesByHost.entrySet();
+ for (Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
+ Host host = e.getKey();
+ if (host.uid > 0) {
+ ArrayList<RestoreUpdateRecord> updates = e.getValue();
+ final int pending = countPendingUpdates(updates);
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Host " + host + " pending: " + pending);
+ }
+ if (pending > 0) {
+ int[] oldIds = new int[pending];
+ int[] newIds = new int[pending];
+ final int N = updates.size();
+ int nextPending = 0;
+ for (int i = 0; i < N; i++) {
+ RestoreUpdateRecord r = updates.get(i);
+ if (!r.notified) {
+ r.notified = true;
+ oldIds[nextPending] = r.oldId;
+ newIds[nextPending] = r.newId;
+ nextPending++;
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, " " + r.oldId + " => " + r.newId);
+ }
+ }
+ }
+ sendWidgetRestoreBroadcast(AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED,
+ null, host, oldIds, newIds, userHandle);
+ }
+ }
+ }
+ }
+ }
+
+ private int countPendingUpdates(ArrayList<RestoreUpdateRecord> updates) {
+ int pending = 0;
+ final int N = updates.size();
+ for (int i = 0; i < N; i++) {
+ RestoreUpdateRecord r = updates.get(i);
+ if (!r.notified) {
+ pending++;
+ }
+ }
+ return pending;
+ }
+
+ void sendWidgetRestoreBroadcast(String action, Provider provider, Host host,
+ int[] oldIds, int[] newIds, UserHandle userHandle) {
+ Intent intent = new Intent(action);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS, oldIds);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, newIds);
+ if (provider != null) {
+ intent.setComponent(provider.info.provider);
+ mContext.sendBroadcastAsUser(intent, userHandle);
+ }
+ if (host != null) {
+ intent.setComponent(null);
+ intent.setPackage(host.packageName);
+ intent.putExtra(AppWidgetManager.EXTRA_HOST_ID, host.hostId);
+ mContext.sendBroadcastAsUser(intent, userHandle);
+ }
+ }
+
private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
Provider p = null;
@@ -1660,10 +2343,7 @@ class AppWidgetServiceImpl {
for (int i = 0; i < N; i++) {
Provider p = mInstalledProviders.get(i);
if (p.instances.size() > 0) {
- out.startTag(null, "p");
- out.attribute(null, "pkg", p.info.provider.getPackageName());
- out.attribute(null, "cl", p.info.provider.getClassName());
- out.endTag(null, "p");
+ serializeProvider(out, p);
p.tag = providerIndex;
providerIndex++;
}
@@ -1672,35 +2352,14 @@ class AppWidgetServiceImpl {
N = mHosts.size();
for (int i = 0; i < N; i++) {
Host host = mHosts.get(i);
- out.startTag(null, "h");
- out.attribute(null, "pkg", host.packageName);
- out.attribute(null, "id", Integer.toHexString(host.hostId));
- out.endTag(null, "h");
+ serializeHost(out, host);
host.tag = i;
}
N = mAppWidgetIds.size();
for (int i = 0; i < N; i++) {
AppWidgetId id = mAppWidgetIds.get(i);
- out.startTag(null, "g");
- out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
- out.attribute(null, "h", Integer.toHexString(id.host.tag));
- if (id.provider != null) {
- out.attribute(null, "p", Integer.toHexString(id.provider.tag));
- }
- if (id.options != null) {
- out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
- out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
- out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
- out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
- out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
- }
- out.endTag(null, "g");
+ serializeAppWidgetId(out, id);
}
Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
@@ -1801,6 +2460,11 @@ class AppWidgetServiceImpl {
mNextAppWidgetId = id.appWidgetId + 1;
}
+ // restored ID is allowed to be absent
+ String restoredIdString = parser.getAttributeValue(null, "rid");
+ id.restoredId = (restoredIdString == null) ? 0
+ : Integer.parseInt(restoredIdString, 16);
+
Bundle options = new Bundle();
String minWidthString = parser.getAttributeValue(null, "min_width");
if (minWidthString != null) {
@@ -1975,8 +2639,7 @@ class AppWidgetServiceImpl {
continue;
}
if (pkgName.equals(ai.packageName)) {
- addProviderLocked(ri);
- providersAdded = true;
+ providersAdded = addProviderLocked(ri);
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 8eaefef..1a1512f 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -24,6 +24,7 @@ import android.app.IApplicationThread;
import android.app.IBackupAgent;
import android.app.PendingIntent;
import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.FullBackup;
import android.app.backup.RestoreSet;
@@ -82,12 +83,14 @@ 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;
import com.android.server.EventLogTags;
import com.android.server.SystemService;
import com.android.server.backup.PackageManagerBackupAgent.Metadata;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -115,10 +118,13 @@ import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
@@ -136,22 +142,43 @@ import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+
public class BackupManagerService extends IBackupManager.Stub {
private static final String TAG = "BackupManagerService";
private static final boolean DEBUG = true;
private static final boolean MORE_DEBUG = false;
+ // System-private key used for backing up an app's widget state. Must
+ // begin with U+FFxx by convention (we reserve all keys starting
+ // with U+FF00 or higher for system use).
+ static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
+
// Historical and current algorithm names
static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1";
static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit";
// Name and current contents version of the full-backup manifest file
+ //
+ // Manifest version history:
+ //
+ // 1 : initial release
static final String BACKUP_MANIFEST_FILENAME = "_manifest";
static final int BACKUP_MANIFEST_VERSION = 1;
+
+ // External archive format version history:
+ //
+ // 1 : initial release
+ // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
+ // 3 : introduced "_meta" metadata file; no other format change per se
+ static final int BACKUP_FILE_VERSION = 3;
static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
- static final int BACKUP_FILE_VERSION = 2;
static final int BACKUP_PW_FILE_VERSION = 2;
+ static final String BACKUP_METADATA_FILENAME = "_meta";
+ static final int BACKUP_METADATA_VERSION = 1;
+ static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
@@ -186,6 +213,7 @@ public class BackupManagerService extends IBackupManager.Stub {
private static final int MSG_RUN_FULL_RESTORE = 10;
private static final int MSG_RETRY_INIT = 11;
private static final int MSG_RETRY_CLEAR = 12;
+ private static final int MSG_WIDGET_BROADCAST = 13;
// backup task state machine tick
static final int MSG_BACKUP_RESTORE_STEP = 20;
@@ -337,42 +365,47 @@ public class BackupManagerService extends IBackupManager.Stub {
public long token;
public PackageInfo pkgInfo;
public int pmToken; // in post-install restore, the PM's token for this transaction
- public boolean needFullBackup;
+ public boolean isSystemRestore;
public String[] filterSet;
+ // Restore a single package
RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
- long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) {
+ long _token, PackageInfo _pkg, int _pmToken) {
transport = _transport;
dirName = _dirName;
observer = _obs;
token = _token;
pkgInfo = _pkg;
pmToken = _pmToken;
- needFullBackup = _needFullBackup;
+ isSystemRestore = false;
filterSet = null;
}
+ // Restore everything possible. This is the form that Setup Wizard or similar
+ // restore UXes use.
RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
- long _token, boolean _needFullBackup) {
+ long _token) {
transport = _transport;
dirName = _dirName;
observer = _obs;
token = _token;
pkgInfo = null;
pmToken = 0;
- needFullBackup = _needFullBackup;
+ isSystemRestore = true;
filterSet = null;
}
+ // Restore some set of packages. Leave this one up to the caller to specify
+ // whether it's to be considered a system-level restore.
RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
- long _token, String[] _filterSet, boolean _needFullBackup) {
+ long _token, String[] _filterSet, boolean _isSystemRestore) {
transport = _transport;
dirName = _dirName;
observer = _obs;
token = _token;
pkgInfo = null;
pmToken = 0;
- needFullBackup = _needFullBackup;
+ isSystemRestore = _isSystemRestore;
filterSet = _filterSet;
}
}
@@ -413,16 +446,19 @@ public class BackupManagerService extends IBackupManager.Stub {
public boolean includeApks;
public boolean includeObbs;
public boolean includeShared;
+ public boolean doWidgets;
public boolean allApps;
public boolean includeSystem;
public String[] packages;
FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs,
- boolean saveShared, boolean doAllApps, boolean doSystem, String[] pkgList) {
+ boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem,
+ String[] pkgList) {
fd = output;
includeApks = saveApks;
includeObbs = saveObbs;
includeShared = saveShared;
+ doWidgets = alsoWidgets;
allApps = doAllApps;
includeSystem = doSystem;
packages = pkgList;
@@ -618,7 +654,8 @@ public class BackupManagerService extends IBackupManager.Stub {
FullBackupParams params = (FullBackupParams)msg.obj;
PerformFullBackupTask task = new PerformFullBackupTask(params.fd,
params.observer, params.includeApks, params.includeObbs,
- params.includeShared, params.curPassword, params.encryptPassword,
+ params.includeShared, params.doWidgets,
+ params.curPassword, params.encryptPassword,
params.allApps, params.includeSystem, params.packages, params.latch);
(new Thread(task)).start();
break;
@@ -631,7 +668,7 @@ public class BackupManagerService extends IBackupManager.Stub {
PerformRestoreTask task = new PerformRestoreTask(
params.transport, params.dirName, params.observer,
params.token, params.pkgInfo, params.pmToken,
- params.needFullBackup, params.filterSet);
+ params.isSystemRestore, params.filterSet);
Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
sendMessage(restoreMsg);
break;
@@ -770,6 +807,13 @@ public class BackupManagerService extends IBackupManager.Stub {
}
break;
}
+
+ case MSG_WIDGET_BROADCAST:
+ {
+ final Intent intent = (Intent) msg.obj;
+ mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
+ break;
+ }
}
}
}
@@ -2360,10 +2404,40 @@ public class BackupManagerService extends IBackupManager.Stub {
@Override
public void operationComplete() {
- // Okay, the agent successfully reported back to us. Spin the data off to the
+ // Okay, the agent successfully reported back to us. The next thing we do is
+ // push the app widget state for the app, if any.
+ final String pkgName = mCurrentPackage.packageName;
+ final long filepos = mBackupDataName.length();
+ FileDescriptor fd = mBackupData.getFileDescriptor();
+ try {
+ BackupDataOutput out = new BackupDataOutput(fd);
+ byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
+ UserHandle.USER_OWNER);
+ if (widgetState != null) {
+ out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
+ out.writeEntityData(widgetState, widgetState.length);
+ } else {
+ // No widget state for this app, but push a 'delete' operation for it
+ // in case they're trying to play games with the payload.
+ out.writeEntityHeader(KEY_WIDGET_STATE, -1);
+ }
+ } catch (IOException e) {
+ // Hard disk error; recovery/failure policy TBD. For now roll back,
+ // but we may want to consider this a transport-level failure (i.e.
+ // we're in such a bad state that we can't contemplate doing backup
+ // operations any more during this pass).
+ Slog.w(TAG, "Unable to save widget state for " + pkgName);
+ try {
+ Libcore.os.ftruncate(fd, filepos);
+ } catch (ErrnoException ee) {
+ Slog.w(TAG, "Unable to roll back!");
+ }
+ }
+
+ // Spin the data off to the
// transport and proceed with the next stage.
if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
- + mCurrentPackage.packageName);
+ + pkgName);
mBackupHandler.removeMessages(MSG_TIMEOUT);
clearAgentState();
addBackupTrace("operation complete");
@@ -2402,17 +2476,14 @@ public class BackupManagerService extends IBackupManager.Stub {
if (mStatus == BackupConstants.TRANSPORT_OK) {
mBackupDataName.delete();
mNewStateName.renameTo(mSavedStateName);
- EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE,
- mCurrentPackage.packageName, size);
- logBackupComplete(mCurrentPackage.packageName);
+ EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
+ logBackupComplete(pkgName);
} else {
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE,
- mCurrentPackage.packageName);
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
}
} catch (Exception e) {
- Slog.e(TAG, "Transport error backing up " + mCurrentPackage.packageName, e);
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE,
- mCurrentPackage.packageName);
+ Slog.e(TAG, "Transport error backing up " + pkgName, e);
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
mStatus = BackupConstants.TRANSPORT_ERROR;
} finally {
try { if (backupData != null) backupData.close(); } catch (IOException e) {}
@@ -2631,18 +2702,21 @@ public class BackupManagerService extends IBackupManager.Stub {
boolean mIncludeApks;
boolean mIncludeObbs;
boolean mIncludeShared;
+ boolean mDoWidgets;
boolean mAllApps;
final boolean mIncludeSystem;
- String[] mPackages;
+ ArrayList<String> mPackages;
String mCurrentPassword;
String mEncryptPassword;
AtomicBoolean mLatchObject;
File mFilesDir;
File mManifestFile;
+ File mMetadataFile;
class FullBackupRunner implements Runnable {
PackageInfo mPackage;
+ byte[] mWidgetData;
IBackupAgent mAgent;
ParcelFileDescriptor mPipe;
int mToken;
@@ -2650,8 +2724,10 @@ public class BackupManagerService extends IBackupManager.Stub {
boolean mWriteManifest;
FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
- int token, boolean sendApk, boolean writeManifest) throws IOException {
+ int token, boolean sendApk, boolean writeManifest, byte[] widgetData)
+ throws IOException {
mPackage = pack;
+ mWidgetData = widgetData;
mAgent = agent;
mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
mToken = token;
@@ -2666,12 +2742,24 @@ public class BackupManagerService extends IBackupManager.Stub {
mPipe.getFileDescriptor());
if (mWriteManifest) {
+ final boolean writeWidgetData = mWidgetData != null;
if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
- writeAppManifest(mPackage, mManifestFile, mSendApk);
+ writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData);
FullBackup.backupToTar(mPackage.packageName, null, null,
mFilesDir.getAbsolutePath(),
mManifestFile.getAbsolutePath(),
output);
+ mManifestFile.delete();
+
+ // We only need to write a metadata file if we have widget data to stash
+ if (writeWidgetData) {
+ writeMetadata(mPackage, mMetadataFile, mWidgetData);
+ FullBackup.backupToTar(mPackage.packageName, null, null,
+ mFilesDir.getAbsolutePath(),
+ mMetadataFile.getAbsolutePath(),
+ output);
+ mMetadataFile.delete();
+ }
}
if (mSendApk) {
@@ -2696,16 +2784,19 @@ public class BackupManagerService extends IBackupManager.Stub {
PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
boolean includeApks, boolean includeObbs, boolean includeShared,
- String curPassword, String encryptPassword, boolean doAllApps,
+ boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps,
boolean doSystem, String[] packages, AtomicBoolean latch) {
mOutputFile = fd;
mObserver = observer;
mIncludeApks = includeApks;
mIncludeObbs = includeObbs;
mIncludeShared = includeShared;
+ mDoWidgets = doWidgets;
mAllApps = doAllApps;
mIncludeSystem = doSystem;
- mPackages = packages;
+ mPackages = (packages == null)
+ ? new ArrayList<String>()
+ : new ArrayList<String>(Arrays.asList(packages));
mCurrentPassword = curPassword;
// when backing up, if there is a current backup password, we require that
// the user use a nonempty encryption password as well. if one is supplied
@@ -2720,13 +2811,28 @@ public class BackupManagerService extends IBackupManager.Stub {
mFilesDir = new File("/data/system");
mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
+ mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
+ }
+
+ void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
+ for (String pkgName : pkgNames) {
+ if (!set.containsKey(pkgName)) {
+ try {
+ PackageInfo info = mPackageManager.getPackageInfo(pkgName,
+ PackageManager.GET_SIGNATURES);
+ set.put(pkgName, info);
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
+ }
+ }
+ }
}
@Override
public void run() {
Slog.i(TAG, "--- Performing full-dataset backup ---");
- List<PackageInfo> packagesToBackup = new ArrayList<PackageInfo>();
+ TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<String, PackageInfo>();
FullBackupObbConnection obbConnection = new FullBackupObbConnection();
obbConnection.establish(); // we'll want this later
@@ -2734,18 +2840,35 @@ public class BackupManagerService extends IBackupManager.Stub {
// doAllApps supersedes the package set if any
if (mAllApps) {
- packagesToBackup = mPackageManager.getInstalledPackages(
+ List<PackageInfo> allPackages = mPackageManager.getInstalledPackages(
PackageManager.GET_SIGNATURES);
- // Exclude system apps if we've been asked to do so
- if (mIncludeSystem == false) {
- for (int i = 0; i < packagesToBackup.size(); ) {
- PackageInfo pkg = packagesToBackup.get(i);
- if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- packagesToBackup.remove(i);
- } else {
- i++;
+ for (int i = 0; i < allPackages.size(); i++) {
+ PackageInfo pkg = allPackages.get(i);
+ // Exclude system apps if we've been asked to do so
+ if (mIncludeSystem == true
+ || ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
+ packagesToBackup.put(pkg.packageName, pkg);
+ }
+ }
+ }
+
+ // If we're doing widget state as well, ensure that we have all the involved
+ // host & provider packages in the set
+ if (mDoWidgets) {
+ List<String> pkgs =
+ AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_OWNER);
+ if (pkgs != null) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Adding widget participants to backup set:");
+ StringBuilder sb = new StringBuilder(128);
+ sb.append(" ");
+ for (String s : pkgs) {
+ sb.append(' ');
+ sb.append(s);
}
+ Slog.i(TAG, sb.toString());
}
+ addPackagesToSet(packagesToBackup, pkgs);
}
}
@@ -2753,44 +2876,34 @@ public class BackupManagerService extends IBackupManager.Stub {
// named system-partition packages will be included even if includeSystem was
// set to false.
if (mPackages != null) {
- for (String pkgName : mPackages) {
- try {
- packagesToBackup.add(mPackageManager.getPackageInfo(pkgName,
- PackageManager.GET_SIGNATURES));
- } catch (NameNotFoundException e) {
- Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
- }
- }
+ addPackagesToSet(packagesToBackup, mPackages);
}
- // Cull any packages that have indicated that backups are not permitted, as well
- // as any explicit mention of the 'special' shared-storage agent package (we
- // handle that one at the end).
- for (int i = 0; i < packagesToBackup.size(); ) {
- PackageInfo pkg = packagesToBackup.get(i);
+ // Now we cull any inapplicable / inappropriate packages from the set
+ Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
+ while (iter.hasNext()) {
+ PackageInfo pkg = iter.next().getValue();
if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0
|| pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE)) {
- packagesToBackup.remove(i);
- } else {
- i++;
- }
- }
-
- // Cull any packages that run as system-domain uids but do not define their
- // own backup agents
- for (int i = 0; i < packagesToBackup.size(); ) {
- PackageInfo pkg = packagesToBackup.get(i);
- if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
+ // Cull any packages that have indicated that backups are not permitted, as well
+ // as any explicit mention of the 'special' shared-storage agent package (we
+ // handle that one at the end).
+ iter.remove();
+ } else if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
&& (pkg.applicationInfo.backupAgentName == null)) {
+ // Cull any packages that run as system-domain uids but do not define their
+ // own backup agents
if (MORE_DEBUG) {
Slog.i(TAG, "... ignoring non-agent system package " + pkg.packageName);
}
- packagesToBackup.remove(i);
- } else {
- i++;
+ iter.remove();
}
}
+ // flatten the set of packages now so we can explicitly control the ordering
+ ArrayList<PackageInfo> backupQueue =
+ new ArrayList<PackageInfo>(packagesToBackup.values());
+
FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
OutputStream out = null;
@@ -2866,16 +2979,16 @@ public class BackupManagerService extends IBackupManager.Stub {
if (mIncludeShared) {
try {
pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0);
- packagesToBackup.add(pkg);
+ backupQueue.add(pkg);
} catch (NameNotFoundException e) {
Slog.e(TAG, "Unable to find shared-storage backup handler");
}
}
// Now back up the app data via the agent mechanism
- int N = packagesToBackup.size();
+ int N = backupQueue.size();
for (int i = 0; i < N; i++) {
- pkg = packagesToBackup.get(i);
+ pkg = backupQueue.get(i);
backupOnePackage(pkg, out);
// after the app's agent runs to handle its private filesystem
@@ -3006,11 +3119,13 @@ public class BackupManagerService extends IBackupManager.Stub {
&& ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
(app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+ byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
+ UserHandle.USER_OWNER);
sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
final int token = generateToken();
FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
- token, sendApk, !isSharedStorage);
+ token, sendApk, !isSharedStorage, widgetBlob);
pipes[1].close(); // the runner has dup'd it
pipes[1] = null;
Thread t = new Thread(runner);
@@ -3086,8 +3201,8 @@ public class BackupManagerService extends IBackupManager.Stub {
}
}
- private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
- throws IOException {
+ private void writeAppManifest(PackageInfo pkg, File manifestFile,
+ boolean withApk, boolean withWidgets) throws IOException {
// Manifest format. All data are strings ending in LF:
// BACKUP_MANIFEST_VERSION, currently 1
//
@@ -3125,6 +3240,43 @@ public class BackupManagerService extends IBackupManager.Stub {
outstream.close();
}
+ // Widget metadata format. All header entries are strings ending in LF:
+ //
+ // Version 1 header:
+ // BACKUP_METADATA_VERSION, currently "1"
+ // package name
+ //
+ // File data (all integers are binary in network byte order)
+ // *N: 4 : integer token identifying which metadata blob
+ // 4 : integer size of this blob = N
+ // N : raw bytes of this metadata blob
+ //
+ // Currently understood blobs (always in network byte order):
+ //
+ // widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN)
+ //
+ // Unrecognized blobs are *ignored*, not errors.
+ private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData)
+ throws IOException {
+ StringBuilder b = new StringBuilder(512);
+ StringBuilderPrinter printer = new StringBuilderPrinter(b);
+ printer.println(Integer.toString(BACKUP_METADATA_VERSION));
+ printer.println(pkg.packageName);
+
+ FileOutputStream fout = new FileOutputStream(destination);
+ BufferedOutputStream bout = new BufferedOutputStream(fout);
+ DataOutputStream out = new DataOutputStream(bout);
+ bout.write(b.toString().getBytes()); // bypassing DataOutputStream
+
+ if (widgetData != null && widgetData.length > 0) {
+ out.writeInt(BACKUP_WIDGET_METADATA_TOKEN);
+ out.writeInt(widgetData.length);
+ out.write(widgetData);
+ }
+ bout.flush();
+ out.close();
+ }
+
private void tearDown(PackageInfo pkg) {
if (pkg != null) {
final ApplicationInfo app = pkg.applicationInfo;
@@ -3228,6 +3380,7 @@ public class BackupManagerService extends IBackupManager.Stub {
ApplicationInfo mTargetApp;
FullBackupObbConnection mObbConnection = null;
ParcelFileDescriptor[] mPipes = null;
+ byte[] mWidgetData = null;
long mBytes;
@@ -3524,7 +3677,8 @@ public class BackupManagerService extends IBackupManager.Stub {
// Clean up the previous agent relationship if necessary,
// and let the observer know we're considering a new app.
if (mAgent != null) {
- if (DEBUG) Slog.d(TAG, "Saw new package; tearing down old one");
+ if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
+ // Now we're really done
tearDownPipes();
tearDownAgent(mTargetApp);
mTargetApp = null;
@@ -3540,6 +3694,9 @@ public class BackupManagerService extends IBackupManager.Stub {
// input file
skipTarPadding(info.size, instream);
sendOnRestorePackage(pkg);
+ } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
+ // Metadata blobs!
+ readMetadata(info, instream);
} else {
// Non-manifest, so it's actual file data. Is this a package
// we're ignoring?
@@ -3996,6 +4153,71 @@ public class BackupManagerService extends IBackupManager.Stub {
}
}
+ // Read a widget metadata file, returning the restored blob
+ void readMetadata(FileMetadata info, InputStream instream) throws IOException {
+ byte[] data = null;
+
+ // Fail on suspiciously large widget dump files
+ if (info.size > 64 * 1024) {
+ throw new IOException("Metadata too big; corrupt? size=" + info.size);
+ }
+
+ byte[] buffer = new byte[(int) info.size];
+ if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
+ mBytes += info.size;
+ } else throw new IOException("Unexpected EOF in widget data");
+
+ String[] str = new String[1];
+ int offset = extractLine(buffer, 0, str);
+ int version = Integer.parseInt(str[0]);
+ if (version == BACKUP_MANIFEST_VERSION) {
+ offset = extractLine(buffer, offset, str);
+ final String pkg = str[0];
+ if (info.packageName.equals(pkg)) {
+ // Data checks out -- the rest of the buffer is a concatenation of
+ // binary blobs as described in the comment at writeAppWidgetData()
+ ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
+ offset, buffer.length - offset);
+ DataInputStream in = new DataInputStream(bin);
+ while (bin.available() > 0) {
+ int token = in.readInt();
+ int size = in.readInt();
+ if (size > 64 * 1024) {
+ throw new IOException("Datum "
+ + Integer.toHexString(token)
+ + " too big; corrupt? size=" + info.size);
+ }
+ switch (token) {
+ case BACKUP_WIDGET_METADATA_TOKEN:
+ {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Got widget metadata for " + info.packageName);
+ }
+ mWidgetData = new byte[size];
+ in.read(mWidgetData);
+ break;
+ }
+ default:
+ {
+ if (DEBUG) {
+ Slog.i(TAG, "Ignoring metadata blob "
+ + Integer.toHexString(token)
+ + " for " + info.packageName);
+ }
+ in.skipBytes(size);
+ break;
+ }
+ }
+ }
+ } else {
+ Slog.w(TAG, "Metadata mismatch: package " + info.packageName
+ + " but widget data for " + pkg);
+ }
+ } else {
+ Slog.w(TAG, "Unsupported metadata version " + version);
+ }
+ }
+
// Returns a policy constant; takes a buffer arg to reduce memory churn
RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
throws IOException {
@@ -4486,7 +4708,7 @@ public class BackupManagerService extends IBackupManager.Stub {
private PackageInfo mTargetPackage;
private File mStateDir;
private int mPmToken;
- private boolean mNeedFullBackup;
+ private boolean mIsSystemRestore;
private HashSet<String> mFilterSet;
private long mStartRealtime;
private PackageManagerBackupAgent mPmAgent;
@@ -4497,11 +4719,13 @@ public class BackupManagerService extends IBackupManager.Stub {
private boolean mFinished;
private int mStatus;
private File mBackupDataName;
+ private File mStageName;
private File mNewStateName;
private File mSavedStateName;
private ParcelFileDescriptor mBackupData;
private ParcelFileDescriptor mNewState;
private PackageInfo mCurrentPackage;
+ private byte[] mWidgetData;
class RestoreRequest {
@@ -4516,7 +4740,7 @@ public class BackupManagerService extends IBackupManager.Stub {
PerformRestoreTask(IBackupTransport transport, String dirName, IRestoreObserver observer,
long restoreSetToken, PackageInfo targetPackage, int pmToken,
- boolean needFullBackup, String[] filterSet) {
+ boolean isSystemRestore, String[] filterSet) {
mCurrentState = RestoreState.INITIAL;
mFinished = false;
mPmAgent = null;
@@ -4526,7 +4750,7 @@ public class BackupManagerService extends IBackupManager.Stub {
mToken = restoreSetToken;
mTargetPackage = targetPackage;
mPmToken = pmToken;
- mNeedFullBackup = needFullBackup;
+ mIsSystemRestore = isSystemRestore;
if (filterSet != null) {
mFilterSet = new HashSet<String>();
@@ -4696,8 +4920,7 @@ public class BackupManagerService extends IBackupManager.Stub {
omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
mPmAgent = new PackageManagerBackupAgent(
mPackageManager, mAgentPackages);
- initiateOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(mPmAgent.onBind()),
- mNeedFullBackup);
+ initiateOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(mPmAgent.onBind()));
// The PM agent called operationComplete() already, because our invocation
// of it is process-local and therefore synchronous. That means that a
// RUNNING_QUEUE message is already enqueued. Only if we're unable to
@@ -4727,6 +4950,12 @@ public class BackupManagerService extends IBackupManager.Stub {
// Metadata is intact, so we can now run the restore queue. If we get here,
// we have already enqueued the necessary next-step message on the looper.
+ // We've deferred telling the App Widget service that we might be replacing
+ // the widget environment with something else, but now we know we've got
+ // data coming, so we do it here.
+ if (mIsSystemRestore) {
+ AppWidgetBackupBridge.restoreStarting(UserHandle.USER_OWNER);
+ }
}
void restoreNextAgent() {
@@ -4835,7 +5064,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// And then finally start the restore on this agent
try {
- initiateOneRestore(packageInfo, metaInfo.versionCode, agent, mNeedFullBackup);
+ initiateOneRestore(packageInfo, metaInfo.versionCode, agent);
++mCount;
} catch (Exception e) {
Slog.e(TAG, "Error when attempting restore: " + e.toString());
@@ -4889,6 +5118,9 @@ public class BackupManagerService extends IBackupManager.Stub {
mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
TIMEOUT_RESTORE_INTERVAL);
+ // Kick off any work that may be needed regarding app widget restores
+ AppWidgetBackupBridge.restoreFinished(UserHandle.USER_OWNER);
+
// done; we can finally release the wakelock
Slog.i(TAG, "Restore complete.");
mWakelock.release();
@@ -4896,43 +5128,92 @@ public class BackupManagerService extends IBackupManager.Stub {
// Call asynchronously into the app, passing it the restore data. The next step
// after this is always a callback, either operationComplete() or handleTimeout().
- void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent,
- boolean needFullBackup) {
+ void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
mCurrentPackage = app;
+ mWidgetData = null;
final String packageName = app.packageName;
if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
// !!! TODO: get the dirs from the transport
mBackupDataName = new File(mDataDir, packageName + ".restore");
+ mStageName = new File(mDataDir, packageName + ".stage");
mNewStateName = new File(mStateDir, packageName + ".new");
mSavedStateName = new File(mStateDir, packageName);
+ // don't stage the 'android' package where the wallpaper data lives. this is
+ // an optimization: we know there's no widget data hosted/published by that
+ // package, and this way we avoid doing a spurious copy of MB-sized wallpaper
+ // data following the download.
+ boolean staging = !packageName.equals("android");
+ ParcelFileDescriptor stage;
+ File downloadFile = (staging) ? mStageName : mBackupDataName;
+
final int token = generateToken();
try {
// Run the transport's restore pass
- mBackupData = ParcelFileDescriptor.open(mBackupDataName,
+ stage = ParcelFileDescriptor.open(downloadFile,
ParcelFileDescriptor.MODE_READ_WRITE |
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE);
if (!SELinux.restorecon(mBackupDataName)) {
- Slog.e(TAG, "SElinux restorecon failed for " + mBackupDataName);
+ Slog.e(TAG, "SElinux restorecon failed for " + downloadFile);
}
- if (mTransport.getRestoreData(mBackupData) != BackupConstants.TRANSPORT_OK) {
+ if (mTransport.getRestoreData(stage) != BackupConstants.TRANSPORT_OK) {
// Transport-level failure, so we wind everything up and
// terminate the restore operation.
Slog.e(TAG, "Error getting restore data for " + packageName);
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
- mBackupData.close();
- mBackupDataName.delete();
+ stage.close();
+ downloadFile.delete();
executeNextState(RestoreState.FINAL);
return;
}
+ // We have the data from the transport. Now we extract and strip
+ // any per-package metadata (typically widget-related information)
+ // if appropriate
+ if (staging) {
+ stage.close();
+ stage = ParcelFileDescriptor.open(downloadFile,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+
+ mBackupData = ParcelFileDescriptor.open(mBackupDataName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE);
+
+ BackupDataInput in = new BackupDataInput(stage.getFileDescriptor());
+ BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor());
+ byte[] buffer = new byte[8192]; // will grow when needed
+ while (in.readNextHeader()) {
+ final String key = in.getKey();
+ final int size = in.getDataSize();
+
+ // is this a special key?
+ if (key.equals(KEY_WIDGET_STATE)) {
+ if (DEBUG) {
+ Slog.i(TAG, "Restoring widget state for " + packageName);
+ }
+ mWidgetData = new byte[size];
+ in.readEntityData(mWidgetData, 0, size);
+ } else {
+ if (size > buffer.length) {
+ buffer = new byte[size];
+ }
+ in.readEntityData(buffer, 0, size);
+ out.writeEntityHeader(key, size);
+ out.writeEntityData(buffer, size);
+ }
+ }
+
+ mBackupData.close();
+ }
+
// Okay, we have the data. Now have the agent do the restore.
- mBackupData.close();
+ stage.close();
mBackupData = ParcelFileDescriptor.open(mBackupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
@@ -4943,7 +5224,8 @@ public class BackupManagerService extends IBackupManager.Stub {
// Kick off the restore, checking for hung agents
prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
- agent.doRestore(mBackupData, appVersionCode, mNewState, token, mBackupManagerBinder);
+ agent.doRestore(mBackupData, appVersionCode, mNewState,
+ token, mBackupManagerBinder);
} catch (Exception e) {
Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString());
@@ -4966,6 +5248,7 @@ public class BackupManagerService extends IBackupManager.Stub {
void agentCleanup() {
mBackupDataName.delete();
+ mStageName.delete();
try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
mBackupData = mNewState = null;
@@ -5024,9 +5307,17 @@ public class BackupManagerService extends IBackupManager.Stub {
public void operationComplete() {
int size = (int) mBackupDataName.length();
EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size);
+
// Just go back to running the restore queue
agentCleanup();
+ // If there was widget state associated with this app, get the OS to
+ // incorporate it into current bookeeping and then pass that along to
+ // the app as part of the restore operation.
+ if (mWidgetData != null) {
+ restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
+ }
+
executeNextState(RestoreState.RUNNING_QUEUE);
}
@@ -5050,6 +5341,12 @@ public class BackupManagerService extends IBackupManager.Stub {
}
}
+ // Used by both incremental and full restore
+ void restoreWidgetData(String packageName, byte[] widgetData) {
+ // Apply the restored widget state and generate the ID update for the app
+ AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_OWNER);
+ }
+
class PerformClearTask implements Runnable {
IBackupTransport mTransport;
PackageInfo mPackage;
@@ -5345,8 +5642,9 @@ public class BackupManagerService extends IBackupManager.Stub {
// Run a *full* backup pass for the given package, writing the resulting data stream
// to the supplied file descriptor. This method is synchronous and does not return
// to the caller until the backup has been completed.
+ @Override
public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
- boolean includeObbs, boolean includeShared,
+ boolean includeObbs, boolean includeShared, boolean doWidgets,
boolean doAllApps, boolean includeSystem, String[] pkgList) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
@@ -5382,7 +5680,7 @@ public class BackupManagerService extends IBackupManager.Stub {
Slog.i(TAG, "Beginning full backup...");
FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
- includeShared, doAllApps, includeSystem, pkgList);
+ includeShared, doWidgets, doAllApps, includeSystem, pkgList);
final int token = generateToken();
synchronized (mFullConfirmations) {
mFullConfirmations.put(token, params);
@@ -5839,7 +6137,7 @@ public class BackupManagerService extends IBackupManager.Stub {
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj = new RestoreParams(transport, dirName, null,
- restoreSet, pkg, token, true);
+ restoreSet, pkg, token);
mBackupHandler.sendMessage(msg);
} catch (RemoteException e) {
// Binding to the transport broke; back off and proceed with the installation.
@@ -6020,7 +6318,7 @@ public class BackupManagerService extends IBackupManager.Stub {
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj = new RestoreParams(mRestoreTransport, dirName,
- observer, token, true);
+ observer, token);
mBackupHandler.sendMessage(msg);
Binder.restoreCallingIdentity(oldId);
return 0;
@@ -6032,6 +6330,7 @@ public class BackupManagerService extends IBackupManager.Stub {
return -1;
}
+ // Restores of more than a single package are treated as 'system' restores
public synchronized int restoreSome(long token, IRestoreObserver observer,
String[] packages) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -6090,7 +6389,7 @@ public class BackupManagerService extends IBackupManager.Stub {
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
- packages, true);
+ packages, packages.length > 1);
mBackupHandler.sendMessage(msg);
Binder.restoreCallingIdentity(oldId);
return 0;
@@ -6169,7 +6468,7 @@ public class BackupManagerService extends IBackupManager.Stub {
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj = new RestoreParams(mRestoreTransport, dirName,
- observer, token, app, 0, false);
+ observer, token, app, 0);
mBackupHandler.sendMessage(msg);
Binder.restoreCallingIdentity(oldId);
return 0;
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index e5615c0..e26747c 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -33,6 +33,7 @@ import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.media.AudioService;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Handler;
@@ -42,6 +43,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Pair;
@@ -123,6 +125,8 @@ public class AppOpsService extends IAppOpsService.Stub {
= new ArrayMap<String, ArrayList<Callback>>();
final ArrayMap<IBinder, Callback> mModeWatchers
= new ArrayMap<IBinder, Callback>();
+ final SparseArray<SparseArray<Restriction>> mAudioRestrictions
+ = new SparseArray<SparseArray<Restriction>>();
public final class Callback implements DeathRecipient {
final IAppOpsCallback mCallback;
@@ -553,6 +557,58 @@ public class AppOpsService extends IAppOpsService.Stub {
}
@Override
+ public int checkAudioOperation(int code, int stream, int uid, String packageName) {
+ synchronized (this) {
+ final int mode = checkRestrictionLocked(code, stream, uid, packageName);
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ return mode;
+ }
+ }
+ return checkOperation(code, uid, packageName);
+ }
+
+ private int checkRestrictionLocked(int code, int stream, int uid, String packageName) {
+ final SparseArray<Restriction> streamRestrictions = mAudioRestrictions.get(code);
+ if (streamRestrictions != null) {
+ final Restriction r = streamRestrictions.get(stream);
+ if (r != null && !r.exceptionPackages.contains(packageName)) {
+ return r.mode;
+ }
+ }
+ return AppOpsManager.MODE_ALLOWED;
+ }
+
+ @Override
+ public void setAudioRestriction(int code, int stream, int uid, int mode,
+ String[] exceptionPackages) {
+ verifyIncomingUid(uid);
+ verifyIncomingOp(code);
+ synchronized (this) {
+ SparseArray<Restriction> streamRestrictions = mAudioRestrictions.get(code);
+ if (streamRestrictions == null) {
+ streamRestrictions = new SparseArray<Restriction>();
+ mAudioRestrictions.put(code, streamRestrictions);
+ }
+ streamRestrictions.remove(stream);
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ final Restriction r = new Restriction();
+ r.mode = mode;
+ if (exceptionPackages != null) {
+ final int N = exceptionPackages.length;
+ r.exceptionPackages = new ArraySet<String>(N);
+ for (int i = 0; i < N; i++) {
+ final String pkg = exceptionPackages[i];
+ if (pkg != null) {
+ r.exceptionPackages.add(pkg.trim());
+ }
+ }
+ }
+ streamRestrictions.put(stream, r);
+ }
+ }
+ }
+
+ @Override
public int checkPackage(int uid, String packageName) {
synchronized (this) {
if (getOpsLocked(uid, packageName, true) != null) {
@@ -1048,6 +1104,31 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
}
+ if (mAudioRestrictions.size() > 0) {
+ boolean printedHeader = false;
+ for (int o=0; o<mAudioRestrictions.size(); o++) {
+ final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o));
+ final SparseArray<Restriction> restrictions = mAudioRestrictions.valueAt(o);
+ for (int i=0; i<restrictions.size(); i++) {
+ if (!printedHeader){
+ pw.println(" Audio Restrictions:");
+ printedHeader = true;
+ needSep = true;
+ }
+ final int stream = restrictions.keyAt(i);
+ pw.print(" "); pw.print(op);
+ pw.print(" stream="); pw.print(AudioService.streamToString(stream));
+ Restriction r = restrictions.valueAt(i);
+ pw.print(": mode="); pw.println(r.mode);
+ if (!r.exceptionPackages.isEmpty()) {
+ pw.println(" Exceptions:");
+ for (int j=0; j<r.exceptionPackages.size(); j++) {
+ pw.print(" "); pw.println(r.exceptionPackages.valueAt(j));
+ }
+ }
+ }
+ }
+ }
if (needSep) {
pw.println();
}
@@ -1080,4 +1161,10 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
}
+
+ private static final class Restriction {
+ private static final ArraySet<String> NO_EXCEPTIONS = new ArraySet<String>();
+ int mode;
+ ArraySet<String> exceptionPackages = NO_EXCEPTIONS;
+ }
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 68b779c..d2513d3 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2434,7 +2434,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (timeout > 0 && iface != null) {
try {
mNetd.addIdleTimer(iface, timeout, type);
- } catch (RemoteException e) {
+ } catch (Exception e) {
+ // You shall not crash!
+ loge("Exception in setupDataActivityTracking " + e);
}
}
}
@@ -2451,7 +2453,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
try {
// the call fails silently if no idletimer setup for this interface
mNetd.removeIdleTimer(iface);
- } catch (RemoteException e) {
+ } catch (Exception e) {
+ loge("Exception in removeDataActivityTracking " + e);
}
}
}
@@ -4041,6 +4044,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private static final int CMP_RESULT_CODE_PROVISIONING_NETWORK = 5;
+ /**
+ * The mobile network is provisioning
+ */
+ private static final int CMP_RESULT_CODE_IS_PROVISIONING = 6;
+
+ private AtomicBoolean mIsProvisioningNetwork = new AtomicBoolean(false);
+ private AtomicBoolean mIsStartingProvisioning = new AtomicBoolean(false);
+
private AtomicBoolean mIsCheckingMobileProvisioning = new AtomicBoolean(false);
@Override
@@ -4111,11 +4122,25 @@ public class ConnectivityService extends IConnectivityManager.Stub {
setProvNotificationVisible(true,
ConnectivityManager.TYPE_MOBILE_HIPRI, ni.getExtraInfo(),
url);
+ // Mark that we've got a provisioning network and
+ // Disable Mobile Data until user actually starts provisioning.
+ mIsProvisioningNetwork.set(true);
+ MobileDataStateTracker mdst = (MobileDataStateTracker)
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+ mdst.setInternalDataEnable(false);
} else {
if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), no url");
}
break;
}
+ case CMP_RESULT_CODE_IS_PROVISIONING: {
+ // FIXME: Need to know when provisioning is done. Probably we can
+ // check the completion status if successful we're done if we
+ // "timedout" or still connected to provisioning APN turn off data?
+ if (DBG) log("CheckMp.onComplete: provisioning started");
+ mIsStartingProvisioning.set(false);
+ break;
+ }
default: {
loge("CheckMp.onComplete: ignore unexpected result=" + result);
break;
@@ -4265,6 +4290,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return result;
}
+ if (mCs.mIsStartingProvisioning.get()) {
+ result = CMP_RESULT_CODE_IS_PROVISIONING;
+ log("isMobileOk: X is provisioning result=" + result);
+ return result;
+ }
+
// See if we've already determined we've got a provisioning connection,
// if so we don't need to do anything active.
MobileDataStateTracker mdstDefault = (MobileDataStateTracker)
@@ -4602,19 +4633,20 @@ public class ConnectivityService extends IConnectivityManager.Stub {
};
private void handleMobileProvisioningAction(String url) {
- // Notication mark notification as not visible
+ // Mark notification as not visible
setProvNotificationVisible(false, ConnectivityManager.TYPE_MOBILE_HIPRI, null, null);
// If provisioning network handle as a special case,
// otherwise launch browser with the intent directly.
- NetworkInfo ni = getProvisioningNetworkInfo();
- if ((ni != null) && ni.isConnectedToProvisioningNetwork()) {
- if (DBG) log("handleMobileProvisioningAction: on provisioning network");
+ if (mIsProvisioningNetwork.get()) {
+ if (DBG) log("handleMobileProvisioningAction: on prov network enable then launch");
+ mIsStartingProvisioning.set(true);
MobileDataStateTracker mdst = (MobileDataStateTracker)
mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+ mdst.setEnableFailFastMobileData(DctConstants.ENABLED);
mdst.enableMobileProvisioning(url);
} else {
- if (DBG) log("handleMobileProvisioningAction: on default network");
+ if (DBG) log("handleMobileProvisioningAction: not prov network");
// Check for apps that can handle provisioning first
Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP);
provisioningIntent.addCategory(TelephonyIntents.CATEGORY_MCCMNC_PREFIX
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 28eb948..52f9aa9 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -84,24 +84,27 @@ public class VibratorService extends IVibratorService.Stub
private final long mStartTime;
private final long[] mPattern;
private final int mRepeat;
+ private final int mStreamHint;
private final int mUid;
private final String mPackageName;
- Vibration(IBinder token, long millis, int uid, String packageName) {
- this(token, millis, null, 0, uid, packageName);
+ Vibration(IBinder token, long millis, int streamHint, int uid, String packageName) {
+ this(token, millis, null, 0, streamHint, uid, packageName);
}
- Vibration(IBinder token, long[] pattern, int repeat, int uid, String packageName) {
- this(token, 0, pattern, repeat, uid, packageName);
+ Vibration(IBinder token, long[] pattern, int repeat, int streamHint, int uid,
+ String packageName) {
+ this(token, 0, pattern, repeat, streamHint, uid, packageName);
}
private Vibration(IBinder token, long millis, long[] pattern,
- int repeat, int uid, String packageName) {
+ int repeat, int streamHint, int uid, String packageName) {
mToken = token;
mTimeout = millis;
mStartTime = SystemClock.uptimeMillis();
mPattern = pattern;
mRepeat = repeat;
+ mStreamHint = streamHint;
mUid = uid;
mPackageName = packageName;
}
@@ -191,7 +194,8 @@ public class VibratorService extends IVibratorService.Stub
Binder.getCallingPid(), Binder.getCallingUid(), null);
}
- public void vibrate(int uid, String packageName, long milliseconds, IBinder token) {
+ public void vibrate(int uid, String packageName, long milliseconds, int streamHint,
+ IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
@@ -207,7 +211,7 @@ public class VibratorService extends IVibratorService.Stub
return;
}
- Vibration vib = new Vibration(token, milliseconds, uid, packageName);
+ Vibration vib = new Vibration(token, milliseconds, streamHint, uid, packageName);
final long ident = Binder.clearCallingIdentity();
try {
@@ -233,7 +237,7 @@ public class VibratorService extends IVibratorService.Stub
}
public void vibratePattern(int uid, String packageName, long[] pattern, int repeat,
- IBinder token) {
+ int streamHint, IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
@@ -258,7 +262,7 @@ public class VibratorService extends IVibratorService.Stub
return;
}
- Vibration vib = new Vibration(token, pattern, repeat, uid, packageName);
+ Vibration vib = new Vibration(token, pattern, repeat, streamHint, uid, packageName);
try {
token.linkToDeath(vib, 0);
} catch (RemoteException e) {
@@ -342,8 +346,12 @@ public class VibratorService extends IVibratorService.Stub
// Lock held on mVibrations
private void startVibrationLocked(final Vibration vib) {
try {
- int mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
+ int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
+ vib.mStreamHint, vib.mUid, vib.mPackageName);
+ if (mode == AppOpsManager.MODE_ALLOWED) {
+ mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
+ }
if (mode != AppOpsManager.MODE_ALLOWED) {
if (mode == AppOpsManager.MODE_ERRORED) {
Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 1345cfd..033b967 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2087,10 +2087,12 @@ public final class ActiveServices {
// Sanity check: if the service listed for the app is not one
// we actually are maintaining, just let it drop.
- if (smap.mServicesByName.get(sr.name) != sr) {
- ServiceRecord cur = smap.mServicesByName.get(sr.name);
- Slog.wtf(TAG, "Service " + sr + " in process " + app
- + " not same as in map: " + cur);
+ final ServiceRecord curRec = smap.mServicesByName.get(sr.name);
+ if (curRec != sr) {
+ if (curRec != null) {
+ Slog.wtf(TAG, "Service " + sr + " in process " + app
+ + " not same as in map: " + curRec);
+ }
continue;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 09ec4f6..9783a66 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1776,6 +1776,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
targetStack = sourceTask.stack;
+ targetStack.moveToFront();
mWindowManager.moveTaskToTop(sourceTask.taskId);
if (!addingToTask &&
(launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
@@ -1827,6 +1828,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
// of a new task... just put it in the top task, though these days
// this case should never happen.
targetStack = adjustStackFocus(r);
+ targetStack.moveToFront();
ActivityRecord prev = targetStack.topActivity();
r.setTask(prev != null ? prev.task
: targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3414daf..fa4a9d1 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -135,14 +135,15 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
enforceCallingPermission();
synchronized (mStats) {
mStats.noteStartWakeLocked(uid, pid, name, historyName, type, unimportantForLogging,
- SystemClock.elapsedRealtime());
+ SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
}
}
public void noteStopWakelock(int uid, int pid, String name, int type) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteStopWakeLocked(uid, pid, name, type, SystemClock.elapsedRealtime());
+ mStats.noteStopWakeLocked(uid, pid, name, type, SystemClock.elapsedRealtime(),
+ SystemClock.uptimeMillis());
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e2226aa..7c2de8b 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -206,10 +206,6 @@ public class NotificationManagerService extends SystemService {
final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
private int mZenMode;
- private int mPreZenAlarmVolume = -1;
- private int mPreZenRingerMode = -1;
- private boolean mZenMutingAlarm;
- private boolean mZenMutingRinger;
// temporary, until we update apps to provide metadata
private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
"com.google.android.dialer",
@@ -1122,9 +1118,6 @@ public class NotificationManagerService extends SystemService {
private final Uri ZEN_MODE
= Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
- private final Uri MODE_RINGER
- = Settings.Global.getUriFor(Settings.Global.MODE_RINGER);
-
SettingsObserver(Handler handler) {
super(handler);
}
@@ -1137,8 +1130,6 @@ public class NotificationManagerService extends SystemService {
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(ZEN_MODE,
false, this);
- resolver.registerContentObserver(MODE_RINGER,
- false, this);
update(null);
}
@@ -1162,9 +1153,6 @@ public class NotificationManagerService extends SystemService {
if (ZEN_MODE.equals(uri)) {
updateZenMode();
}
- if (MODE_RINGER.equals(uri)) {
- updateRingerMode();
- }
}
}
@@ -1230,7 +1218,6 @@ public class NotificationManagerService extends SystemService {
Settings.Global.DEVICE_PROVISIONED, 0)) {
mDisableNotificationAlerts = true;
}
- updateRingerMode();
updateZenMode();
// register for various Intents
@@ -1694,10 +1681,6 @@ public class NotificationManagerService extends SystemService {
pw.println(" mVibrateNotification=" + mVibrateNotification);
pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts);
pw.println(" mZenMode=" + Settings.Global.zenModeToString(mZenMode));
- pw.println(" mPreZenAlarmVolume=" + mPreZenAlarmVolume);
- pw.println(" mPreZenRingerMode=" + mPreZenRingerMode);
- pw.println(" mZenMutingAlarm=" + mZenMutingAlarm);
- pw.println(" mZenMutingRinger=" + mZenMutingRinger);
pw.println(" mSystemReady=" + mSystemReady);
pw.println(" mArchive=" + mArchive.toString());
Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
@@ -2023,7 +2006,7 @@ public class NotificationManagerService extends SystemService {
useDefaultVibrate ? mDefaultVibrationPattern
: mFallbackVibrationPattern,
((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1);
+ ? 0: -1, notification.audioStreamType);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2033,7 +2016,7 @@ public class NotificationManagerService extends SystemService {
mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
notification.vibrate,
((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1);
+ ? 0: -1, notification.audioStreamType);
}
}
}
@@ -2552,17 +2535,6 @@ public class NotificationManagerService extends SystemService {
}
}
- private void updateRingerMode() {
- final int ringerMode = Settings.Global.getInt(getContext().getContentResolver(),
- Settings.Global.MODE_RINGER, -1);
- final boolean nonSilentRingerMode = ringerMode == AudioManager.RINGER_MODE_NORMAL
- || ringerMode == AudioManager.RINGER_MODE_VIBRATE;
- if (mZenMode != Settings.Global.ZEN_MODE_OFF && nonSilentRingerMode) {
- Settings.Global.putInt(getContext().getContentResolver(),
- Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
- }
- }
-
private void updateZenMode() {
final int mode = Settings.Global.getInt(getContext().getContentResolver(),
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
@@ -2572,62 +2544,31 @@ public class NotificationManagerService extends SystemService {
Settings.Global.zenModeToString(mode)));
}
mZenMode = mode;
- if (mAudioManager != null) {
- // call audio
- final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
- if (muteCalls) {
- if (!mZenMutingRinger) {
- if (DBG) Slog.d(TAG, "Muting STREAM_RING");
- mAudioManager.setStreamMute(AudioManager.STREAM_RING, true);
- mZenMutingRinger = true;
- }
- // calls vibrate if ringer mode = vibrate, so set the ringer mode as well
- final int ringerMode = mAudioManager.getRingerMode();
- if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
- if (DBG) Slog.d(TAG, "Saving ringer mode of " + ringerMode);
- mPreZenRingerMode = ringerMode;
- mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
- }
- } else {
- if (mZenMutingRinger) {
- if (DBG) Slog.d(TAG, "Unmuting STREAM_RING");
- mAudioManager.setStreamMute(AudioManager.STREAM_RING, false);
- mZenMutingRinger = false;
- }
- if (mPreZenRingerMode != -1) {
- if (DBG) Slog.d(TAG, "Restoring ringer mode to " + mPreZenRingerMode);
- mAudioManager.setRingerMode(mPreZenRingerMode);
- mPreZenRingerMode = -1;
- }
- }
- // alarm audio
- final boolean muteAlarms = mZenMode == Settings.Global.ZEN_MODE_FULL;
- if (muteAlarms) {
- if (!mZenMutingAlarm) {
- if (DBG) Slog.d(TAG, "Muting STREAM_ALARM");
- mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, true);
- mZenMutingAlarm = true;
- }
- // alarms don't simply respect mute, so set the volume as well
- final int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
- if (volume != 0) {
- if (DBG) Slog.d(TAG, "Saving STREAM_ALARM volume of " + volume);
- mPreZenAlarmVolume = volume;
- mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 0, 0);
- }
- } else {
- if (mZenMutingAlarm) {
- if (DBG) Slog.d(TAG, "Unmuting STREAM_ALARM");
- mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, false);
- mZenMutingAlarm = false;
- }
- if (mPreZenAlarmVolume != -1) {
- if (DBG) Slog.d(TAG, "Restoring STREAM_ALARM volume to " + mPreZenAlarmVolume);
- mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mPreZenAlarmVolume, 0);
- mPreZenAlarmVolume = -1;
- }
- }
- }
+
+ final String[] exceptionPackages = null; // none (for now)
+
+ // call restrictions
+ final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
+ mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
+ muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+ mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
+ muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+
+ // alarm restrictions
+ final boolean muteAlarms = mZenMode == Settings.Global.ZEN_MODE_FULL;
+ mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
+ muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+ mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
+ muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+
+ // restrict vibrations with no hints
+ mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
+ (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
}
private void updateRelatedUserCache(Context context) {
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index b7e367b..ae1cfab 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -406,4 +406,8 @@ public final class Installer extends SystemService {
return execute(builder.toString());
}
+
+ public boolean restoreconData() {
+ return (execute("restorecondata") == 0);
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cbcf408..cb4cf98 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -302,12 +302,15 @@ public class PackageManagerService extends IPackageManager.Stub {
// This is the object monitoring the privileged system app dir.
final FileObserver mPrivilegedInstallObserver;
- // This is the object monitoring the system app dir.
+ // This is the object monitoring the vendor app dir.
final FileObserver mVendorInstallObserver;
// This is the object monitoring the vendor overlay package dir.
final FileObserver mVendorOverlayInstallObserver;
+ // This is the object monitoring the OEM app dir.
+ final FileObserver mOemInstallObserver;
+
// This is the object monitoring mAppInstallDir.
final FileObserver mAppInstallObserver;
@@ -1157,7 +1160,12 @@ public class PackageManagerService extends IPackageManager.Stub {
sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);
- readPermissions();
+ // Read permissions and features from system
+ readPermissions(Environment.buildPath(
+ Environment.getRootDirectory(), "etc", "permissions"), false);
+ // Only read features from OEM
+ readPermissions(Environment.buildPath(
+ Environment.getOemDirectory(), "etc", "permissions"), true);
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
@@ -1343,6 +1351,14 @@ public class PackageManagerService extends IPackageManager.Stub {
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
+ // Collect all OEM packages.
+ File oemAppDir = new File(Environment.getOemDirectory(), "app");
+ mOemInstallObserver = new AppDirObserver(
+ oemAppDir.getPath(), OBSERVER_EVENTS, true, false);
+ mOemInstallObserver.startWatching();
+ scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
+
if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
mInstaller.moveFiles();
@@ -1493,6 +1509,13 @@ public class PackageManagerService extends IPackageManager.Stub {
// can downgrade to reader
mSettings.writeLPr();
+ if (SELinuxMMAC.shouldRestorecon()) {
+ Slog.i(TAG, "Relabeling of /data/data and /data/user issued.");
+ if (mInstaller.restoreconData()) {
+ SELinuxMMAC.setRestoreconDone();
+ }
+ }
+
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
@@ -1581,9 +1604,8 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.removePackageLPw(ps.name);
}
- void readPermissions() {
+ void readPermissions(File libraryDir, boolean onlyFeatures) {
// Read permissions from .../etc/permission directory.
- File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions");
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
Slog.w(TAG, "No directory " + libraryDir + ", skipping");
return;
@@ -1609,16 +1631,16 @@ public class PackageManagerService extends IPackageManager.Stub {
continue;
}
- readPermissionsFromXml(f);
+ readPermissionsFromXml(f, onlyFeatures);
}
// Read permissions from .../etc/permissions/platform.xml last so it will take precedence
final File permFile = new File(Environment.getRootDirectory(),
"etc/permissions/platform.xml");
- readPermissionsFromXml(permFile);
+ readPermissionsFromXml(permFile, onlyFeatures);
}
- private void readPermissionsFromXml(File permFile) {
+ private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
FileReader permReader = null;
try {
permReader = new FileReader(permFile);
@@ -1640,7 +1662,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
String name = parser.getName();
- if ("group".equals(name)) {
+ if ("group".equals(name) && !onlyFeatures) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = Process.getGidForName(gidStr);
@@ -1652,7 +1674,7 @@ public class PackageManagerService extends IPackageManager.Stub {
XmlUtils.skipCurrentTag(parser);
continue;
- } else if ("permission".equals(name)) {
+ } else if ("permission".equals(name) && !onlyFeatures) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<permission> without name at "
@@ -1663,7 +1685,7 @@ public class PackageManagerService extends IPackageManager.Stub {
perm = perm.intern();
readPermission(parser, perm);
- } else if ("assign-permission".equals(name)) {
+ } else if ("assign-permission".equals(name) && !onlyFeatures) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<assign-permission> without name at "
@@ -1695,7 +1717,7 @@ public class PackageManagerService extends IPackageManager.Stub {
perms.add(perm);
XmlUtils.skipCurrentTag(parser);
- } else if ("library".equals(name)) {
+ } else if ("library".equals(name) && !onlyFeatures) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
if (lname == null) {
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 6e12e41..d70c725 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -25,10 +25,15 @@ import android.util.Xml;
import com.android.internal.util.XmlUtils;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
@@ -59,6 +64,13 @@ public final class SELinuxMMAC {
new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
null};
+ // Location of seapp_contexts policy file.
+ private static final String SEAPP_CONTEXTS_FILE = "/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;
@@ -102,7 +114,6 @@ public final class SELinuxMMAC {
/**
* Parses an MMAC install policy from a predefined list of locations.
- * @param none
* @return boolean indicating whether an install policy was correctly parsed.
*/
public static boolean readInstallPolicy() {
@@ -112,7 +123,7 @@ public final class SELinuxMMAC {
/**
* Parses an MMAC install policy given as an argument.
- * @param File object representing the path of the policy.
+ * @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) {
@@ -344,8 +355,7 @@ public final class SELinuxMMAC {
/**
* Labels a package based on an seinfo tag from install policy.
* The label is attached to the ApplicationInfo instance of the package.
- * @param PackageParser.Package object representing the package
- * to labeled.
+ * @param pkg object representing the package to be labeled.
* @return boolean which determines whether a non null seinfo label
* was assigned to the package. A null value simply meaning that
* no policy matched.
@@ -390,4 +400,89 @@ public final class SELinuxMMAC {
return (sDefaultSeinfo != null);
}
+
+ /**
+ * Determines if a recursive restorecon on /data/data and /data/user is needed.
+ * It does this by comparing the SHA-1 of the seapp_contexts file against the
+ * stored hash at /data/system/seapp_hash.
+ *
+ * @return Returns true if the restorecon should occur or false otherwise.
+ */
+ public static boolean shouldRestorecon() {
+ // Any error with the seapp_contexts file should be fatal
+ byte[] currentHash = null;
+ try {
+ currentHash = returnHash(SEAPP_CONTEXTS_FILE);
+ } catch (IOException ioe) {
+ Slog.e(TAG, "Error with hashing seapp_contexts.", ioe);
+ return false;
+ }
+
+ // Push past any error with the stored hash file
+ byte[] storedHash = null;
+ try {
+ storedHash = IoUtils.readFileAsByteArray(SEAPP_HASH_FILE);
+ } catch (IOException ioe) {
+ Slog.w(TAG, "Error opening " + SEAPP_HASH_FILE + ". Assuming first boot.");
+ }
+
+ return (storedHash == null || !MessageDigest.isEqual(storedHash, currentHash));
+ }
+
+ /**
+ * Stores the SHA-1 of the seapp_contexts to /data/system/seapp_hash.
+ */
+ public static void setRestoreconDone() {
+ try {
+ final byte[] currentHash = returnHash(SEAPP_CONTEXTS_FILE);
+ dumpHash(new File(SEAPP_HASH_FILE), currentHash);
+ } catch (IOException ioe) {
+ Slog.e(TAG, "Error with saving hash to " + SEAPP_HASH_FILE, ioe);
+ }
+ }
+
+ /**
+ * Dump the contents of a byte array to a specified file.
+ *
+ * @param file The file that receives the byte array content.
+ * @param content A byte array that will be written to the specified file.
+ * @throws IOException if any failed I/O operation occured.
+ * Included is the failure to atomically rename the tmp
+ * file used in the process.
+ */
+ private static void dumpHash(File file, byte[] content) throws IOException {
+ FileOutputStream fos = null;
+ File tmp = null;
+ try {
+ tmp = File.createTempFile("seapp_hash", ".journal", file.getParentFile());
+ tmp.setReadable(true);
+ fos = new FileOutputStream(tmp);
+ fos.write(content);
+ fos.getFD().sync();
+ if (!tmp.renameTo(file)) {
+ throw new IOException("Failure renaming " + file.getCanonicalPath());
+ }
+ } finally {
+ if (tmp != null) {
+ tmp.delete();
+ }
+ IoUtils.closeQuietly(fos);
+ }
+ }
+
+ /**
+ * Return the SHA-1 of a file.
+ *
+ * @param file The path to the file given as a string.
+ * @return Returns the SHA-1 of the file as a byte array.
+ * @throws IOException if any failed I/O operations occured.
+ */
+ private static byte[] returnHash(String file) throws IOException {
+ try {
+ final byte[] contents = IoUtils.readFileAsByteArray(file);
+ return MessageDigest.getInstance("SHA-1").digest(contents);
+ } catch (NoSuchAlgorithmException nsae) {
+ throw new RuntimeException(nsae); // impossible
+ }
+ }
}
diff --git a/services/core/java/com/android/server/power/AutomaticBrightnessController.java b/services/core/java/com/android/server/power/AutomaticBrightnessController.java
index 3ca628a..807262a 100644
--- a/services/core/java/com/android/server/power/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/power/AutomaticBrightnessController.java
@@ -200,8 +200,7 @@ class AutomaticBrightnessController {
public void updatePowerState(DisplayPowerRequest request) {
if (setScreenAutoBrightnessAdjustment(request.screenAutoBrightnessAdjustment)
- || setLightSensorEnabled(request.useAutoBrightness
- && request.wantScreenOnNormal())) {
+ || setLightSensorEnabled(request.wantLightSensorEnabled())) {
updateAutoBrightness(false /*sendUpdate*/);
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
new file mode 100644
index 0000000..35b7f99
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -0,0 +1,1173 @@
+/*
+ * 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.wm;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Service;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.MagnificationSpec;
+import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
+import android.view.SurfaceControl;
+import android.view.WindowInfo;
+import android.view.WindowManager;
+import android.view.WindowManagerInternal.MagnificationCallbacks;
+import android.view.WindowManagerInternal.WindowsForAccessibilityCallback;
+import android.view.WindowManagerPolicy;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.internal.R;
+import com.android.internal.os.SomeArgs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class contains the accessibility related logic of the window manger.
+ */
+final class AccessibilityController {
+
+ private final WindowManagerService mWindowManagerService;
+
+ private static final float[] sTempFloats = new float[9];
+
+ public AccessibilityController(WindowManagerService service) {
+ mWindowManagerService = service;
+ }
+
+ private DisplayMagnifier mDisplayMagnifier;
+
+ private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
+
+ public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
+ if (callbacks != null) {
+ if (mDisplayMagnifier != null) {
+ throw new IllegalStateException("Magnification callbacks already set!");
+ }
+ mDisplayMagnifier = new DisplayMagnifier(mWindowManagerService, callbacks);
+ } else {
+ if (mDisplayMagnifier == null) {
+ throw new IllegalStateException("Magnification callbacks already cleared!");
+ }
+ mDisplayMagnifier.destroyLocked();
+ mDisplayMagnifier = null;
+ }
+ }
+
+ public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
+ if (callback != null) {
+ if (mWindowsForAccessibilityObserver != null) {
+ throw new IllegalStateException(
+ "Windows for accessibility callback already set!");
+ }
+ mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver(
+ mWindowManagerService, callback);
+ } else {
+ if (mWindowsForAccessibilityObserver == null) {
+ throw new IllegalStateException(
+ "Windows for accessibility callback already cleared!");
+ }
+ mWindowsForAccessibilityObserver = null;
+ }
+ }
+
+ public void setMagnificationSpecLocked(MagnificationSpec spec) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.setMagnificationSpecLocked(spec);
+ }
+ if (mWindowsForAccessibilityObserver != null) {
+ mWindowsForAccessibilityObserver.computeChangedWindows();
+ }
+ }
+
+ public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate);
+ }
+ // Not relevant for the window observer.
+ }
+
+ public void onWindowLayersChangedLocked() {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.onWindowLayersChangedLocked();
+ }
+ if (mWindowsForAccessibilityObserver != null) {
+ mWindowsForAccessibilityObserver.computeChangedWindows();
+ }
+ }
+
+ public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.onRotationChangedLocked(displayContent, rotation);
+ }
+ if (mWindowsForAccessibilityObserver != null) {
+ mWindowsForAccessibilityObserver.computeChangedWindows();
+ }
+ }
+
+ public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
+ }
+ // Not relevant for the window observer.
+ }
+
+ public void onWindowTransitionLocked(WindowState windowState, int transition) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
+ }
+ if (mWindowsForAccessibilityObserver != null) {
+ mWindowsForAccessibilityObserver.computeChangedWindows();
+ }
+ }
+
+ public void onWindowFocusChangedLocked() {
+ // Not relevant for the display magnifier.
+
+ if (mWindowsForAccessibilityObserver != null) {
+ mWindowsForAccessibilityObserver.computeChangedWindows();
+ }
+ }
+
+ /** NOTE: This has to be called within a surface transaction. */
+ public void drawMagnifiedRegionBorderIfNeededLocked() {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+ }
+ // Not relevant for the window observer.
+ }
+
+ public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
+ if (mDisplayMagnifier != null) {
+ return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
+ }
+ return null;
+ }
+
+ public boolean hasCallbacksLocked() {
+ return (mDisplayMagnifier != null
+ || mWindowsForAccessibilityObserver != null);
+ }
+
+ private static void populateTransformationMatrixLocked(WindowState windowState,
+ Matrix outMatrix) {
+ sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
+ sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
+ sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
+ sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
+ sTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left;
+ sTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top;
+ sTempFloats[Matrix.MPERSP_0] = 0;
+ sTempFloats[Matrix.MPERSP_1] = 0;
+ sTempFloats[Matrix.MPERSP_2] = 1;
+ outMatrix.setValues(sTempFloats);
+ }
+
+ /**
+ * This class encapsulates the functionality related to display magnification.
+ */
+ private static final class DisplayMagnifier {
+
+ private static final String LOG_TAG = "DisplayMagnifier";
+
+ private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
+ private static final boolean DEBUG_ROTATION = false;
+ private static final boolean DEBUG_LAYERS = false;
+ private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
+ private static final boolean DEBUG_VIEWPORT_WINDOW = false;
+
+ private final Rect mTempRect1 = new Rect();
+ private final Rect mTempRect2 = new Rect();
+
+ private final Region mTempRegion1 = new Region();
+ private final Region mTempRegion2 = new Region();
+ private final Region mTempRegion3 = new Region();
+ private final Region mTempRegion4 = new Region();
+
+ private final Context mContext;
+ private final WindowManagerService mWindowManagerService;
+ private final MagnifiedViewport mMagnifedViewport;
+ private final Handler mHandler;
+
+ private final MagnificationCallbacks mCallbacks;
+
+ private final long mLongAnimationDuration;
+
+ public DisplayMagnifier(WindowManagerService windowManagerService,
+ MagnificationCallbacks callbacks) {
+ mContext = windowManagerService.mContext;
+ mWindowManagerService = windowManagerService;
+ mCallbacks = callbacks;
+ mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
+ mMagnifedViewport = new MagnifiedViewport();
+ mLongAnimationDuration = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longAnimTime);
+ }
+
+ public void setMagnificationSpecLocked(MagnificationSpec spec) {
+ mMagnifedViewport.updateMagnificationSpecLocked(spec);
+ mMagnifedViewport.recomputeBoundsLocked();
+ mWindowManagerService.scheduleAnimationLocked();
+ }
+
+ public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
+ if (DEBUG_RECTANGLE_REQUESTED) {
+ Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
+ }
+ if (!mMagnifedViewport.isMagnifyingLocked()) {
+ return;
+ }
+ Rect magnifiedRegionBounds = mTempRect2;
+ mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
+ if (magnifiedRegionBounds.contains(rectangle)) {
+ return;
+ }
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = rectangle.left;
+ args.argi2 = rectangle.top;
+ args.argi3 = rectangle.right;
+ args.argi4 = rectangle.bottom;
+ mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
+ args).sendToTarget();
+ }
+
+ public void onWindowLayersChangedLocked() {
+ if (DEBUG_LAYERS) {
+ Slog.i(LOG_TAG, "Layers changed.");
+ }
+ mMagnifedViewport.recomputeBoundsLocked();
+ mWindowManagerService.scheduleAnimationLocked();
+ }
+
+ public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
+ if (DEBUG_ROTATION) {
+ Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
+ + " displayId: " + displayContent.getDisplayId());
+ }
+ mMagnifedViewport.onRotationChangedLocked();
+ mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
+ }
+
+ public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
+ if (DEBUG_WINDOW_TRANSITIONS) {
+ Slog.i(LOG_TAG, "Window transition: "
+ + AppTransition.appTransitionToString(transition)
+ + " displayId: " + windowState.getDisplayId());
+ }
+ final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+ if (magnifying) {
+ switch (transition) {
+ case AppTransition.TRANSIT_ACTIVITY_OPEN:
+ case AppTransition.TRANSIT_TASK_OPEN:
+ case AppTransition.TRANSIT_TASK_TO_FRONT:
+ case AppTransition.TRANSIT_WALLPAPER_OPEN:
+ case AppTransition.TRANSIT_WALLPAPER_CLOSE:
+ case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
+ mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
+ }
+ }
+ }
+ }
+
+ public void onWindowTransitionLocked(WindowState windowState, int transition) {
+ if (DEBUG_WINDOW_TRANSITIONS) {
+ Slog.i(LOG_TAG, "Window transition: "
+ + AppTransition.appTransitionToString(transition)
+ + " displayId: " + windowState.getDisplayId());
+ }
+ final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+ final int type = windowState.mAttrs.type;
+ switch (transition) {
+ case WindowManagerPolicy.TRANSIT_ENTER:
+ case WindowManagerPolicy.TRANSIT_SHOW: {
+ if (!magnifying) {
+ break;
+ }
+ switch (type) {
+ case WindowManager.LayoutParams.TYPE_APPLICATION:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
+ case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
+ case WindowManager.LayoutParams.TYPE_PHONE:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
+ case WindowManager.LayoutParams.TYPE_TOAST:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
+ case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
+ case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
+ case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
+ Rect magnifiedRegionBounds = mTempRect2;
+ mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
+ magnifiedRegionBounds);
+ Rect touchableRegionBounds = mTempRect1;
+ windowState.getTouchableRegion(mTempRegion1);
+ mTempRegion1.getBounds(touchableRegionBounds);
+ if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
+ mCallbacks.onRectangleOnScreenRequested(
+ touchableRegionBounds.left,
+ touchableRegionBounds.top,
+ touchableRegionBounds.right,
+ touchableRegionBounds.bottom);
+ }
+ } break;
+ } break;
+ }
+ }
+ }
+
+ public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
+ MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
+ if (spec != null && !spec.isNop()) {
+ WindowManagerPolicy policy = mWindowManagerService.mPolicy;
+ final int windowType = windowState.mAttrs.type;
+ if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null
+ && !policy.canMagnifyWindow(windowType)) {
+ return null;
+ }
+ if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
+ return null;
+ }
+ }
+ return spec;
+ }
+
+ public void destroyLocked() {
+ mMagnifedViewport.destroyWindow();
+ }
+
+ /** NOTE: This has to be called within a surface transaction. */
+ public void drawMagnifiedRegionBorderIfNeededLocked() {
+ mMagnifedViewport.drawWindowIfNeededLocked();
+ }
+
+ private final class MagnifiedViewport {
+
+ private static final int DEFAUTLT_BORDER_WIDTH_DIP = 5;
+
+ private final SparseArray<WindowState> mTempWindowStates =
+ new SparseArray<WindowState>();
+
+ private final RectF mTempRectF = new RectF();
+
+ private final Point mTempPoint = new Point();
+
+ private final Matrix mTempMatrix = new Matrix();
+
+ private final Region mMagnifiedBounds = new Region();
+ private final Region mOldMagnifiedBounds = new Region();
+
+ private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
+
+ private final WindowManager mWindowManager;
+
+ private final int mBorderWidth;
+ private final int mHalfBorderWidth;
+
+ private final ViewportWindow mWindow;
+
+ private boolean mFullRedrawNeeded;
+
+ public MagnifiedViewport() {
+ mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
+ mBorderWidth = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, DEFAUTLT_BORDER_WIDTH_DIP,
+ mContext.getResources().getDisplayMetrics());
+ mHalfBorderWidth = (int) (mBorderWidth + 0.5) / 2;
+ mWindow = new ViewportWindow(mContext);
+ recomputeBoundsLocked();
+ }
+
+ public void updateMagnificationSpecLocked(MagnificationSpec spec) {
+ if (spec != null) {
+ mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
+ } else {
+ mMagnificationSpec.clear();
+ }
+ // If this message is pending we are in a rotation animation and do not want
+ // to show the border. We will do so when the pending message is handled.
+ if (!mHandler.hasMessages(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
+ setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
+ }
+ }
+
+ public void recomputeBoundsLocked() {
+ mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+ final int screenWidth = mTempPoint.x;
+ final int screenHeight = mTempPoint.y;
+
+ Region magnifiedBounds = mMagnifiedBounds;
+ magnifiedBounds.set(0, 0, 0, 0);
+
+ Region availableBounds = mTempRegion1;
+ availableBounds.set(0, 0, screenWidth, screenHeight);
+
+ Region nonMagnifiedBounds = mTempRegion4;
+ nonMagnifiedBounds.set(0, 0, 0, 0);
+
+ SparseArray<WindowState> visibleWindows = mTempWindowStates;
+ visibleWindows.clear();
+ populateWindowsOnScreenLocked(visibleWindows);
+
+ final int visibleWindowCount = visibleWindows.size();
+ for (int i = visibleWindowCount - 1; i >= 0; i--) {
+ WindowState windowState = visibleWindows.valueAt(i);
+ if (windowState.mAttrs.type == WindowManager
+ .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
+ continue;
+ }
+
+ Region windowBounds = mTempRegion2;
+ Matrix matrix = mTempMatrix;
+ populateTransformationMatrixLocked(windowState, matrix);
+ RectF windowFrame = mTempRectF;
+
+ if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+ windowFrame.set(windowState.mFrame);
+ windowFrame.offset(-windowFrame.left, -windowFrame.top);
+ matrix.mapRect(windowFrame);
+ windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
+ (int) windowFrame.right, (int) windowFrame.bottom);
+ magnifiedBounds.op(windowBounds, Region.Op.UNION);
+ magnifiedBounds.op(availableBounds, Region.Op.INTERSECT);
+ } else {
+ Region touchableRegion = mTempRegion3;
+ windowState.getTouchableRegion(touchableRegion);
+ Rect touchableFrame = mTempRect1;
+ touchableRegion.getBounds(touchableFrame);
+ windowFrame.set(touchableFrame);
+ windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+ matrix.mapRect(windowFrame);
+ windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
+ (int) windowFrame.right, (int) windowFrame.bottom);
+ nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
+ windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE);
+ availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
+ }
+
+ Region accountedBounds = mTempRegion2;
+ accountedBounds.set(magnifiedBounds);
+ accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
+ accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
+
+ if (accountedBounds.isRect()) {
+ Rect accountedFrame = mTempRect1;
+ accountedBounds.getBounds(accountedFrame);
+ if (accountedFrame.width() == screenWidth
+ && accountedFrame.height() == screenHeight) {
+ break;
+ }
+ }
+ }
+
+ visibleWindows.clear();
+
+ magnifiedBounds.op(mHalfBorderWidth, mHalfBorderWidth,
+ screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth,
+ Region.Op.INTERSECT);
+
+ if (!mOldMagnifiedBounds.equals(magnifiedBounds)) {
+ Region bounds = Region.obtain();
+ bounds.set(magnifiedBounds);
+ mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED,
+ bounds).sendToTarget();
+
+ mWindow.setBounds(magnifiedBounds);
+ Rect dirtyRect = mTempRect1;
+ if (mFullRedrawNeeded) {
+ mFullRedrawNeeded = false;
+ dirtyRect.set(mHalfBorderWidth, mHalfBorderWidth,
+ screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth);
+ mWindow.invalidate(dirtyRect);
+ } else {
+ Region dirtyRegion = mTempRegion3;
+ dirtyRegion.set(magnifiedBounds);
+ dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
+ dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
+ dirtyRegion.getBounds(dirtyRect);
+ mWindow.invalidate(dirtyRect);
+ }
+
+ mOldMagnifiedBounds.set(magnifiedBounds);
+ }
+ }
+
+ public void onRotationChangedLocked() {
+ // If we are magnifying, hide the magnified border window immediately so
+ // the user does not see strange artifacts during rotation. The screenshot
+ // used for rotation has already the border. After the rotation is complete
+ // we will show the border.
+ if (isMagnifyingLocked()) {
+ setMagnifiedRegionBorderShownLocked(false, false);
+ final long delay = (long) (mLongAnimationDuration
+ * mWindowManagerService.mWindowAnimationScale);
+ Message message = mHandler.obtainMessage(
+ MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
+ mHandler.sendMessageDelayed(message, delay);
+ }
+ recomputeBoundsLocked();
+ mWindow.updateSize();
+ }
+
+ public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
+ if (shown) {
+ mFullRedrawNeeded = true;
+ mOldMagnifiedBounds.set(0, 0, 0, 0);
+ }
+ mWindow.setShown(shown, animate);
+ }
+
+ public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
+ MagnificationSpec spec = mMagnificationSpec;
+ mMagnifiedBounds.getBounds(rect);
+ rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
+ rect.scale(1.0f / spec.scale);
+ }
+
+ public boolean isMagnifyingLocked() {
+ return mMagnificationSpec.scale > 1.0f;
+ }
+
+ public MagnificationSpec getMagnificationSpecLocked() {
+ return mMagnificationSpec;
+ }
+
+ /** NOTE: This has to be called within a surface transaction. */
+ public void drawWindowIfNeededLocked() {
+ recomputeBoundsLocked();
+ mWindow.drawIfNeeded();
+ }
+
+ public void destroyWindow() {
+ mWindow.releaseSurface();
+ }
+
+ private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+ DisplayContent displayContent = mWindowManagerService
+ .getDefaultDisplayContentLocked();
+ WindowList windowList = displayContent.getWindowList();
+ final int windowCount = windowList.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowState windowState = windowList.get(i);
+ if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager
+ .LayoutParams.TYPE_UNIVERSE_BACKGROUND)
+ && !windowState.mWinAnimator.mEnterAnimationPending) {
+ outWindows.put(windowState.mLayer, windowState);
+ }
+ }
+ }
+
+ private final class ViewportWindow {
+ private static final String SURFACE_TITLE = "Magnification Overlay";
+
+ private static final String PROPERTY_NAME_ALPHA = "alpha";
+
+ private static final int MIN_ALPHA = 0;
+ private static final int MAX_ALPHA = 255;
+
+ private final Region mBounds = new Region();
+ private final Rect mDirtyRect = new Rect();
+ private final Paint mPaint = new Paint();
+
+ private final ValueAnimator mShowHideFrameAnimator;
+ private final SurfaceControl mSurfaceControl;
+ private final Surface mSurface = new Surface();
+
+ private boolean mShown;
+ private int mAlpha;
+
+ private boolean mInvalidated;
+
+ public ViewportWindow(Context context) {
+ SurfaceControl surfaceControl = null;
+ try {
+ mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+ surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession,
+ SURFACE_TITLE, mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT,
+ SurfaceControl.HIDDEN);
+ } catch (OutOfResourcesException oore) {
+ /* ignore */
+ }
+ mSurfaceControl = surfaceControl;
+ mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
+ .getLayerStack());
+ mSurfaceControl.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw(
+ WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
+ * WindowManagerService.TYPE_LAYER_MULTIPLIER);
+ mSurfaceControl.setPosition(0, 0);
+ mSurface.copyFrom(mSurfaceControl);
+
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
+ typedValue, true);
+ final int borderColor = context.getResources().getColor(typedValue.resourceId);
+
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(mBorderWidth);
+ mPaint.setColor(borderColor);
+
+ Interpolator interpolator = new DecelerateInterpolator(2.5f);
+ final long longAnimationDuration = context.getResources().getInteger(
+ com.android.internal.R.integer.config_longAnimTime);
+
+ mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA,
+ MIN_ALPHA, MAX_ALPHA);
+ mShowHideFrameAnimator.setInterpolator(interpolator);
+ mShowHideFrameAnimator.setDuration(longAnimationDuration);
+ mInvalidated = true;
+ }
+
+ public void setShown(boolean shown, boolean animate) {
+ synchronized (mWindowManagerService.mWindowMap) {
+ if (mShown == shown) {
+ return;
+ }
+ mShown = shown;
+ if (animate) {
+ if (mShowHideFrameAnimator.isRunning()) {
+ mShowHideFrameAnimator.reverse();
+ } else {
+ if (shown) {
+ mShowHideFrameAnimator.start();
+ } else {
+ mShowHideFrameAnimator.reverse();
+ }
+ }
+ } else {
+ mShowHideFrameAnimator.cancel();
+ if (shown) {
+ setAlpha(MAX_ALPHA);
+ } else {
+ setAlpha(MIN_ALPHA);
+ }
+ }
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
+ }
+ }
+ }
+
+ @SuppressWarnings("unused")
+ // Called reflectively from an animator.
+ public int getAlpha() {
+ synchronized (mWindowManagerService.mWindowMap) {
+ return mAlpha;
+ }
+ }
+
+ public void setAlpha(int alpha) {
+ synchronized (mWindowManagerService.mWindowMap) {
+ if (mAlpha == alpha) {
+ return;
+ }
+ mAlpha = alpha;
+ invalidate(null);
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
+ }
+ }
+ }
+
+ public void setBounds(Region bounds) {
+ synchronized (mWindowManagerService.mWindowMap) {
+ if (mBounds.equals(bounds)) {
+ return;
+ }
+ mBounds.set(bounds);
+ invalidate(mDirtyRect);
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
+ }
+ }
+ }
+
+ public void updateSize() {
+ synchronized (mWindowManagerService.mWindowMap) {
+ mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+ mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
+ invalidate(mDirtyRect);
+ }
+ }
+
+ public void invalidate(Rect dirtyRect) {
+ if (dirtyRect != null) {
+ mDirtyRect.set(dirtyRect);
+ } else {
+ mDirtyRect.setEmpty();
+ }
+ mInvalidated = true;
+ mWindowManagerService.scheduleAnimationLocked();
+ }
+
+ /** NOTE: This has to be called within a surface transaction. */
+ public void drawIfNeeded() {
+ synchronized (mWindowManagerService.mWindowMap) {
+ if (!mInvalidated) {
+ return;
+ }
+ mInvalidated = false;
+ Canvas canvas = null;
+ try {
+ // Empty dirty rectangle means unspecified.
+ if (mDirtyRect.isEmpty()) {
+ mBounds.getBounds(mDirtyRect);
+ }
+ mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
+ canvas = mSurface.lockCanvas(mDirtyRect);
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
+ }
+ } catch (IllegalArgumentException iae) {
+ /* ignore */
+ } catch (Surface.OutOfResourcesException oore) {
+ /* ignore */
+ }
+ if (canvas == null) {
+ return;
+ }
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "Bounds: " + mBounds);
+ }
+ canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
+ mPaint.setAlpha(mAlpha);
+ Path path = mBounds.getBoundaryPath();
+ canvas.drawPath(path, mPaint);
+
+ mSurface.unlockCanvasAndPost(canvas);
+
+ if (mAlpha > 0) {
+ mSurfaceControl.show();
+ } else {
+ mSurfaceControl.hide();
+ }
+ }
+ }
+
+ public void releaseSurface() {
+ mSurfaceControl.release();
+ mSurface.release();
+ }
+ }
+ }
+
+ private class MyHandler extends Handler {
+ public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1;
+ public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
+ public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
+ public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
+ public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
+
+ public MyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: {
+ Region bounds = (Region) message.obj;
+ mCallbacks.onMagnifedBoundsChanged(bounds);
+ bounds.recycle();
+ } break;
+
+ case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
+ SomeArgs args = (SomeArgs) message.obj;
+ final int left = args.argi1;
+ final int top = args.argi2;
+ final int right = args.argi3;
+ final int bottom = args.argi4;
+ mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
+ args.recycle();
+ } break;
+
+ case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
+ mCallbacks.onUserContextChanged();
+ } break;
+
+ case MESSAGE_NOTIFY_ROTATION_CHANGED: {
+ final int rotation = message.arg1;
+ mCallbacks.onRotationChanged(rotation);
+ } break;
+
+ case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
+ synchronized (mWindowManagerService.mWindowMap) {
+ if (mMagnifedViewport.isMagnifyingLocked()) {
+ mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
+ mWindowManagerService.scheduleAnimationLocked();
+ }
+ }
+ } break;
+ }
+ }
+ }
+ }
+
+ /**
+ * This class encapsulates the functionality related to computing the windows
+ * reported for accessibility purposes. These windows are all windows a sighted
+ * user can see on the screen.
+ */
+ private static final class WindowsForAccessibilityObserver {
+ private static final String LOG_TAG = "WindowsForAccessibilityObserver";
+
+ private static final boolean DEBUG = false;
+
+ private final SparseArray<WindowState> mTempWindowStates =
+ new SparseArray<WindowState>();
+
+ private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
+
+ private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
+
+ private final RectF mTempRectF = new RectF();
+
+ private final Matrix mTempMatrix = new Matrix();
+
+ private final Point mTempPoint = new Point();
+
+ private final Rect mTempRect = new Rect();
+
+ private final Region mTempRegion = new Region();
+
+ private final Region mTempRegion1 = new Region();
+
+ private final Context mContext;
+
+ private final WindowManagerService mWindowManagerService;
+
+ private final Handler mHandler;
+
+ private final WindowsForAccessibilityCallback mCallback;
+
+ public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
+ WindowsForAccessibilityCallback callback) {
+ mContext = windowManagerService.mContext;
+ mWindowManagerService = windowManagerService;
+ mCallback = callback;
+ mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
+ computeChangedWindows();
+ }
+
+ public void computeChangedWindows() {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "computeChangedWindows()");
+ }
+
+ synchronized (mWindowManagerService.mWindowMap) {
+ WindowManager windowManager = (WindowManager)
+ mContext.getSystemService(Context.WINDOW_SERVICE);
+ windowManager.getDefaultDisplay().getRealSize(mTempPoint);
+ final int screenWidth = mTempPoint.x;
+ final int screenHeight = mTempPoint.y;
+
+ Region unaccountedSpace = mTempRegion;
+ unaccountedSpace.set(0, 0, screenWidth, screenHeight);
+
+ SparseArray<WindowState> visibleWindows = mTempWindowStates;
+ populateVisibleWindowsOnScreenLocked(visibleWindows);
+
+ List<WindowInfo> windows = new ArrayList<WindowInfo>();
+
+ Set<IBinder> addedWindows = mTempBinderSet;
+ addedWindows.clear();
+
+ final int visibleWindowCount = visibleWindows.size();
+ for (int i = visibleWindowCount - 1; i >= 0; i--) {
+ WindowState windowState = visibleWindows.valueAt(i);
+ // Compute the window touchable frame as shown on the screen.
+
+ // Get the touchable frame.
+ Region touchableRegion = mTempRegion1;
+ windowState.getTouchableRegion(touchableRegion);
+ Rect touchableFrame = mTempRect;
+ touchableRegion.getBounds(touchableFrame);
+
+ // Move to origin as all transforms are captured by the matrix.
+ RectF windowFrame = mTempRectF;
+ windowFrame.set(touchableFrame);
+ windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+
+ // Map the frame to get what appears on the screen.
+ Matrix matrix = mTempMatrix;
+ populateTransformationMatrixLocked(windowState, matrix);
+ matrix.mapRect(windowFrame);
+
+ // Got the bounds.
+ Rect boundsInScreen = mTempRect;
+ boundsInScreen.set((int) windowFrame.left, (int) windowFrame.top,
+ (int) windowFrame.right, (int) windowFrame.bottom);
+
+ final int flags = windowState.mAttrs.flags;
+
+ // If the window is not touchable, do not report it but take into account
+ // the space it takes since the content behind it cannot be touched.
+ if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 1) {
+ unaccountedSpace.op(boundsInScreen, unaccountedSpace,
+ Region.Op.DIFFERENCE);
+ continue;
+ }
+
+ // If the window is completely covered by other windows - ignore.
+ if (unaccountedSpace.quickReject(boundsInScreen)) {
+ continue;
+ }
+
+ // Add windows of certain types not covered by modal windows.
+ if (isReportedWindowType(windowState.mAttrs.type)) {
+ // Add the window to the ones to be reported.
+ WindowInfo window = WindowInfo.obtain();
+ window.type = windowState.mAttrs.type;
+ window.layer = windowState.mLayer;
+ window.token = windowState.mClient.asBinder();
+
+ addedWindows.add(window.token);
+
+ WindowState attachedWindow = windowState.mAttachedWindow;
+ if (attachedWindow != null) {
+ window.parentToken = attachedWindow.mClient.asBinder();
+ }
+
+ window.focused = windowState.isFocused();
+ window.boundsInScreen.set(boundsInScreen);
+
+ final int childCount = windowState.mChildWindows.size();
+ if (childCount > 0) {
+ if (window.childTokens == null) {
+ window.childTokens = new ArrayList<IBinder>();
+ }
+ for (int j = 0; j < childCount; j++) {
+ WindowState child = windowState.mChildWindows.get(j);
+ window.childTokens.add(child.mClient.asBinder());
+ }
+ }
+
+ windows.add(window);
+ }
+
+ // Account for the space this window takes.
+ unaccountedSpace.op(boundsInScreen, unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+
+ // We figured out what is touchable for the entire screen - done.
+ if (unaccountedSpace.isEmpty()) {
+ break;
+ }
+
+ // If a window is modal, no other below can be touched - done.
+ if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+ break;
+ }
+ }
+
+ // Remove child/parent references to windows that were not added.
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowInfo window = windows.get(i);
+ if (!addedWindows.contains(window.parentToken)) {
+ window.parentToken = null;
+ }
+ if (window.childTokens != null) {
+ final int childTokenCount = window.childTokens.size();
+ for (int j = childTokenCount - 1; j >= 0; j--) {
+ if (!addedWindows.contains(window.childTokens.get(j))) {
+ window.childTokens.remove(j);
+ }
+ }
+ // Leave the child token list if empty.
+ }
+ }
+
+ visibleWindows.clear();
+ addedWindows.clear();
+
+ // We computed the windows and if they changed notify the client.
+ boolean windowsChanged = false;
+ if (mOldWindows.size() != windows.size()) {
+ // Different size means something changed.
+ windowsChanged = true;
+ } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
+ // Since we always traverse windows from high to low layer
+ // the old and new windows at the same index should be the
+ // same, otherwise something changed.
+ for (int i = 0; i < windowCount; i++) {
+ WindowInfo oldWindow = mOldWindows.get(i);
+ WindowInfo newWindow = windows.get(i);
+ // We do not care for layer changes given the window
+ // order does not change. This brings no new information
+ // to the clients.
+ if (windowChangedNoLayer(oldWindow, newWindow)) {
+ windowsChanged = true;
+ break;
+ }
+ }
+ }
+
+ if (windowsChanged) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Windows changed:" + windows);
+ }
+ // Remember the old windows to detect changes.
+ cacheWindows(windows);
+ // Announce the change.
+ mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED,
+ windows).sendToTarget();
+ } else {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "No windows changed.");
+ }
+ // Recycle the nodes as we do not need them.
+ clearAndRecycleWindows(windows);
+ }
+ }
+ }
+
+ private void cacheWindows(List<WindowInfo> windows) {
+ final int oldWindowCount = mOldWindows.size();
+ for (int i = oldWindowCount - 1; i >= 0; i--) {
+ mOldWindows.remove(i).recycle();
+ }
+ final int newWindowCount = windows.size();
+ for (int i = 0; i < newWindowCount; i++) {
+ WindowInfo newWindow = windows.get(i);
+ mOldWindows.add(WindowInfo.obtain(newWindow));
+ }
+ }
+
+ private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
+ if (oldWindow == newWindow) {
+ return false;
+ }
+ if (oldWindow == null && newWindow != null) {
+ return true;
+ }
+ if (oldWindow != null && newWindow == null) {
+ return true;
+ }
+ if (oldWindow.type != newWindow.type) {
+ return true;
+ }
+ if (oldWindow.focused != newWindow.focused) {
+ return true;
+ }
+ if (oldWindow.token == null) {
+ if (newWindow.token != null) {
+ return true;
+ }
+ } else if (!oldWindow.token.equals(newWindow.token)) {
+ return true;
+ }
+ if (oldWindow.parentToken == null) {
+ if (newWindow.parentToken != null) {
+ return true;
+ }
+ } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
+ return true;
+ }
+ if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
+ return true;
+ }
+ if (oldWindow.childTokens != null && newWindow.childTokens != null
+ && !oldWindow.childTokens.equals(newWindow.childTokens)) {
+ return true;
+ }
+ return false;
+ }
+
+ private void clearAndRecycleWindows(List<WindowInfo> windows) {
+ final int windowCount = windows.size();
+ for (int i = windowCount - 1; i >= 0; i--) {
+ windows.remove(i).recycle();
+ }
+ }
+
+ private static boolean isReportedWindowType(int windowType) {
+ return (windowType != WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM
+ && windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
+ && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
+ && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_DRAG
+ && windowType != WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER
+ && windowType != WindowManager.LayoutParams.TYPE_POINTER
+ && windowType != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
+ && windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
+ }
+
+ private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+ DisplayContent displayContent = mWindowManagerService
+ .getDefaultDisplayContentLocked();
+ WindowList windowList = displayContent.getWindowList();
+ final int windowCount = windowList.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowState windowState = windowList.get(i);
+ if (windowState.isVisibleLw()) {
+ outWindows.put(windowState.mLayer, windowState);
+ }
+ }
+ }
+
+ private class MyHandler extends Handler {
+ public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 1;
+
+ public MyHandler(Looper looper) {
+ super(looper, null, false);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MESSAGE_NOTIFY_WINDOWS_CHANGED: {
+ List<WindowInfo> windows = (List<WindowInfo>) message.obj;
+ mCallback.onWindowsForAccessibilityChanged(windows);
+ clearAndRecycleWindows(windows);
+ } break;
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 756e06a..7d8b5af 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -19,18 +19,22 @@ package com.android.server.wm;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Debug;
import android.os.Handler;
import android.os.IRemoteCallback;
+import android.os.SystemProperties;
import android.util.Slog;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
+import android.view.animation.ClipRectAnimation;
import android.view.animation.Interpolator;
import android.view.animation.ScaleAnimation;
+import android.view.animation.TranslateAnimation;
import com.android.internal.util.DumpUtils.Dump;
import com.android.server.AttributeCache;
import com.android.server.wm.WindowManagerService.H;
@@ -125,6 +129,12 @@ public class AppTransition implements Dump {
private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4;
private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
+ // These are the possible states for the enter/exit activities during a thumbnail transition
+ private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
+ private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
+ private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
+ private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
+
private String mNextAppTransitionPackage;
private Bitmap mNextAppTransitionThumbnail;
// Used for thumbnail transitions. True if we're scaling up, false if scaling down
@@ -148,10 +158,13 @@ public class AppTransition implements Dump {
private final Interpolator mThumbnailFadeoutInterpolator;
private int mCurrentUserId = 0;
+ private boolean mUseAlternateThumbnailAnimation;
AppTransition(Context context, Handler h) {
mContext = context;
mH = h;
+ mUseAlternateThumbnailAnimation =
+ SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false);
mConfigShortAnimTime = context.getResources().getInteger(
com.android.internal.R.integer.config_shortAnimTime);
mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
@@ -384,54 +397,203 @@ public class AppTransition implements Dump {
return a;
}
- Animation createThumbnailAnimationLocked(int transit, boolean enter, boolean thumb,
- int appWidth, int appHeight) {
+ /**
+ * Prepares the specified animation with a standard duration, interpolator, etc.
+ */
+ Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
+ // Pick the desired duration. If this is an inter-activity transition,
+ // it is the standard duration for that. Otherwise we use the longer
+ // task transition duration.
+ final long duration;
+ switch (transit) {
+ case TRANSIT_ACTIVITY_OPEN:
+ case TRANSIT_ACTIVITY_CLOSE:
+ duration = mConfigShortAnimTime;
+ break;
+ default:
+ duration = DEFAULT_APP_TRANSITION_DURATION;
+ break;
+ }
+ a.setDuration(duration);
+ a.setFillAfter(true);
+ a.setInterpolator(mDecelerateInterpolator);
+ a.initialize(appWidth, appHeight, appWidth, appHeight);
+ return a;
+ }
+
+ /**
+ * Return the current thumbnail transition state.
+ */
+ int getThumbnailTransitionState(boolean enter) {
+ if (enter) {
+ if (mNextAppTransitionScaleUp) {
+ return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
+ } else {
+ return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
+ }
+ } else {
+ if (mNextAppTransitionScaleUp) {
+ return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
+ } else {
+ return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
+ }
+ }
+ }
+
+ /**
+ * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
+ * when a thumbnail is specified with the activity options.
+ */
+ Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) {
Animation a;
final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
- if (thumb) {
- // Animation for zooming thumbnail from its initial size to
- // filling the screen.
- if (mNextAppTransitionScaleUp) {
- float scaleW = appWidth / thumbWidth;
- float scaleH = appHeight / thumbHeight;
- Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
- computePivot(mNextAppTransitionStartX, 1 / scaleW),
- computePivot(mNextAppTransitionStartY, 1 / scaleH));
- scale.setInterpolator(mDecelerateInterpolator);
- Animation alpha = new AlphaAnimation(1, 0);
- alpha.setInterpolator(mThumbnailFadeoutInterpolator);
+ if (mNextAppTransitionScaleUp) {
+ // Animation for the thumbnail zooming from its initial size to the full screen
+ float scaleW = appWidth / thumbWidth;
+ float scaleH = appHeight / thumbHeight;
+ Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
+ computePivot(mNextAppTransitionStartX, 1 / scaleW),
+ computePivot(mNextAppTransitionStartY, 1 / scaleH));
+ scale.setInterpolator(mDecelerateInterpolator);
- // This AnimationSet uses the Interpolators assigned above.
- AnimationSet set = new AnimationSet(false);
- set.addAnimation(scale);
- set.addAnimation(alpha);
+ Animation alpha = new AlphaAnimation(1, 0);
+ alpha.setInterpolator(mThumbnailFadeoutInterpolator);
+
+ // This AnimationSet uses the Interpolators assigned above.
+ AnimationSet set = new AnimationSet(false);
+ set.addAnimation(scale);
+ set.addAnimation(alpha);
+ a = set;
+ } else {
+ // Animation for the thumbnail zooming down from the full screen to its final size
+ float scaleW = appWidth / thumbWidth;
+ float scaleH = appHeight / thumbHeight;
+ a = new ScaleAnimation(scaleW, 1, scaleH, 1,
+ computePivot(mNextAppTransitionStartX, 1 / scaleW),
+ computePivot(mNextAppTransitionStartY, 1 / scaleH));
+ }
+
+ return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+ }
+
+ /**
+ * This alternate animation is created when we are doing a thumbnail transition, for the
+ * activity that is leaving, and the activity that is entering.
+ */
+ Animation createAlternateThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
+ int appHeight, int transit,
+ Rect containingFrame, Rect contentInsets) {
+ Animation a;
+ final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+ final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+ final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+ final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+ switch (thumbTransitState) {
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
+ // Entering app scales up with the thumbnail
+ float scale = thumbWidth / appWidth;
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
+ int scaledTopDecor = (int) (scale * contentInsets.top);
+ Rect fromClipRect = new Rect(containingFrame);
+ fromClipRect.bottom = (fromClipRect.top + unscaledThumbHeight);
+ Rect toClipRect = new Rect(containingFrame);
+
+ Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
+ computePivot(mNextAppTransitionStartX, scale),
+ computePivot(mNextAppTransitionStartY, scale));
+ Animation alphaAnim = new AlphaAnimation(1, 1);
+ Animation clipAnim = new ClipRectAnimation(fromClipRect, toClipRect);
+ Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
+
+ AnimationSet set = new AnimationSet(true);
+ set.addAnimation(alphaAnim);
+ set.addAnimation(clipAnim);
+ set.addAnimation(scaleAnim);
+ set.addAnimation(translateAnim);
a = set;
- } else {
- float scaleW = appWidth / thumbWidth;
- float scaleH = appHeight / thumbHeight;
- a = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mNextAppTransitionStartX, 1 / scaleW),
- computePivot(mNextAppTransitionStartY, 1 / scaleH));
+ break;
}
- } else if (enter) {
- // Entering app zooms out from the center of the thumbnail.
- if (mNextAppTransitionScaleUp) {
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
+ // Exiting app while the thumbnail is scaling up should fade
+ if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
+ // Fade out while bringing up selected activity. This keeps the
+ // current activity from showing through a launching wallpaper
+ // activity.
+ a = new AlphaAnimation(1, 0);
+ } else {
+ // noop animation
+ a = new AlphaAnimation(1, 1);
+ }
+ break;
+ }
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
+ // Entering the other app, it should just be visible while we scale the thumbnail
+ // down above it
+ a = new AlphaAnimation(1, 1);
+ break;
+ }
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
+ // Exiting the current app, the app should scale down with the thumbnail
+ float scale = thumbWidth / appWidth;
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
+ int scaledTopDecor = (int) (scale * contentInsets.top);
+ Rect fromClipRect = new Rect(containingFrame);
+ Rect toClipRect = new Rect(containingFrame);
+ toClipRect.bottom = (toClipRect.top + unscaledThumbHeight);
+
+ Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
+ computePivot(mNextAppTransitionStartX, scale),
+ computePivot(mNextAppTransitionStartY, scale));
+ Animation alphaAnim = new AlphaAnimation(1, 1);
+ Animation clipAnim = new ClipRectAnimation(fromClipRect, toClipRect);
+ Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
+
+ AnimationSet set = new AnimationSet(true);
+ set.addAnimation(alphaAnim);
+ set.addAnimation(clipAnim);
+ set.addAnimation(scaleAnim);
+ set.addAnimation(translateAnim);
+
+ a = set;
+ a.setZAdjustment(Animation.ZORDER_TOP);
+ break;
+ }
+ default:
+ throw new RuntimeException("Invalid thumbnail transition state");
+ }
+
+ return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+ }
+
+ /**
+ * This animation is created when we are doing a thumbnail transition, for the activity that is
+ * leaving, and the activity that is entering.
+ */
+ Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
+ int appHeight, int transit) {
+ Animation a;
+ final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+ final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+ final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+ final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+ switch (thumbTransitState) {
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
+ // Entering app scales up with the thumbnail
float scaleW = thumbWidth / appWidth;
float scaleH = thumbHeight / appHeight;
a = new ScaleAnimation(scaleW, 1, scaleH, 1,
computePivot(mNextAppTransitionStartX, scaleW),
computePivot(mNextAppTransitionStartY, scaleH));
- } else {
- // noop animation
- a = new AlphaAnimation(1, 1);
+ break;
}
- } else {
- // Exiting app
- if (mNextAppTransitionScaleUp) {
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
+ // Exiting app while the thumbnail is scaling up should fade or stay in place
if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
// Fade out while bringing up selected activity. This keeps the
// current activity from showing through a launching wallpaper
@@ -441,7 +603,16 @@ public class AppTransition implements Dump {
// noop animation
a = new AlphaAnimation(1, 1);
}
- } else {
+ break;
+ }
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
+ // Entering the other app, it should just be visible while we scale the thumbnail
+ // down above it
+ a = new AlphaAnimation(1, 1);
+ break;
+ }
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
+ // Exiting the current app, the app should scale down with the thumbnail
float scaleW = thumbWidth / appWidth;
float scaleH = thumbHeight / appHeight;
Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
@@ -455,32 +626,18 @@ public class AppTransition implements Dump {
set.addAnimation(alpha);
set.setZAdjustment(Animation.ZORDER_TOP);
a = set;
- }
- }
-
- // Pick the desired duration. If this is an inter-activity transition,
- // it is the standard duration for that. Otherwise we use the longer
- // task transition duration.
- final long duration;
- switch (transit) {
- case TRANSIT_ACTIVITY_OPEN:
- case TRANSIT_ACTIVITY_CLOSE:
- duration = mConfigShortAnimTime;
break;
+ }
default:
- duration = DEFAULT_APP_TRANSITION_DURATION;
- break;
+ throw new RuntimeException("Invalid thumbnail transition state");
}
- a.setDuration(duration);
- a.setFillAfter(true);
- a.setInterpolator(mDecelerateInterpolator);
- a.initialize(appWidth, appHeight, appWidth, appHeight);
- return a;
+
+ return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
}
Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- int appWidth, int appHeight) {
+ int appWidth, int appHeight, Rect containingFrame, Rect contentInsets) {
Animation a;
if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
a = loadAnimation(mNextAppTransitionPackage, enter ?
@@ -501,7 +658,14 @@ public class AppTransition implements Dump {
mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
- a = createThumbnailAnimationLocked(transit, enter, false, appWidth, appHeight);
+ if (mUseAlternateThumbnailAnimation) {
+ a = createAlternateThumbnailEnterExitAnimationLocked(
+ getThumbnailTransitionState(enter), appWidth, appHeight, transit,
+ containingFrame, contentInsets);
+ } else {
+ a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
+ appWidth, appHeight, transit);
+ }
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
String animName = mNextAppTransitionScaleUp ?
"ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
diff --git a/services/core/java/com/android/server/wm/DisplayMagnifier.java b/services/core/java/com/android/server/wm/DisplayMagnifier.java
deleted file mode 100644
index 382d7b4..0000000
--- a/services/core/java/com/android/server/wm/DisplayMagnifier.java
+++ /dev/null
@@ -1,756 +0,0 @@
-/*
- * Copyright (C) 2012 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.wm;
-
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.app.Service;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Pools.SimplePool;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.TypedValue;
-import android.view.IMagnificationCallbacks;
-import android.view.MagnificationSpec;
-import android.view.Surface;
-import android.view.Surface.OutOfResourcesException;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-import com.android.internal.R;
-import com.android.internal.os.SomeArgs;
-
-/**
- * This class is a part of the window manager and encapsulates the
- * functionality related to display magnification.
- */
-final class DisplayMagnifier {
- private static final String LOG_TAG = DisplayMagnifier.class.getSimpleName();
-
- private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
- private static final boolean DEBUG_ROTATION = false;
- private static final boolean DEBUG_LAYERS = false;
- private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
- private static final boolean DEBUG_VIEWPORT_WINDOW = false;
-
- private final Rect mTempRect1 = new Rect();
- private final Rect mTempRect2 = new Rect();
-
- private final Region mTempRegion1 = new Region();
- private final Region mTempRegion2 = new Region();
- private final Region mTempRegion3 = new Region();
- private final Region mTempRegion4 = new Region();
-
- private final Context mContext;
- private final WindowManagerService mWindowManagerService;
- private final MagnifiedViewport mMagnifedViewport;
- private final Handler mHandler;
-
- private final IMagnificationCallbacks mCallbacks;
-
- private final long mLongAnimationDuration;
-
- public DisplayMagnifier(WindowManagerService windowManagerService,
- IMagnificationCallbacks callbacks) {
- mContext = windowManagerService.mContext;
- mWindowManagerService = windowManagerService;
- mCallbacks = callbacks;
- mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
- mMagnifedViewport = new MagnifiedViewport();
- mLongAnimationDuration = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_longAnimTime);
- }
-
- public void setMagnificationSpecLocked(MagnificationSpec spec) {
- mMagnifedViewport.updateMagnificationSpecLocked(spec);
- mMagnifedViewport.recomputeBoundsLocked();
- mWindowManagerService.scheduleAnimationLocked();
- }
-
- public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
- if (DEBUG_RECTANGLE_REQUESTED) {
- Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
- }
- if (!mMagnifedViewport.isMagnifyingLocked()) {
- return;
- }
- Rect magnifiedRegionBounds = mTempRect2;
- mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
- if (magnifiedRegionBounds.contains(rectangle)) {
- return;
- }
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = rectangle.left;
- args.argi2 = rectangle.top;
- args.argi3 = rectangle.right;
- args.argi4 = rectangle.bottom;
- mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
- args).sendToTarget();
- }
-
- public void onWindowLayersChangedLocked() {
- if (DEBUG_LAYERS) {
- Slog.i(LOG_TAG, "Layers changed.");
- }
- mMagnifedViewport.recomputeBoundsLocked();
- mWindowManagerService.scheduleAnimationLocked();
- }
-
- public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
- if (DEBUG_ROTATION) {
- Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
- + " displayId: " + displayContent.getDisplayId());
- }
- mMagnifedViewport.onRotationChangedLocked();
- mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
- }
-
- public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
- if (DEBUG_WINDOW_TRANSITIONS) {
- Slog.i(LOG_TAG, "Window transition: "
- + AppTransition.appTransitionToString(transition)
- + " displayId: " + windowState.getDisplayId());
- }
- final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
- if (magnifying) {
- switch (transition) {
- case AppTransition.TRANSIT_ACTIVITY_OPEN:
- case AppTransition.TRANSIT_TASK_OPEN:
- case AppTransition.TRANSIT_TASK_TO_FRONT:
- case AppTransition.TRANSIT_WALLPAPER_OPEN:
- case AppTransition.TRANSIT_WALLPAPER_CLOSE:
- case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
- mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
- }
- }
- }
- }
-
- public void onWindowTransitionLocked(WindowState windowState, int transition) {
- if (DEBUG_WINDOW_TRANSITIONS) {
- Slog.i(LOG_TAG, "Window transition: "
- + AppTransition.appTransitionToString(transition)
- + " displayId: " + windowState.getDisplayId());
- }
- final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
- final int type = windowState.mAttrs.type;
- switch (transition) {
- case WindowManagerPolicy.TRANSIT_ENTER:
- case WindowManagerPolicy.TRANSIT_SHOW: {
- if (!magnifying) {
- break;
- }
- switch (type) {
- case WindowManager.LayoutParams.TYPE_APPLICATION:
- case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
- case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
- case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
- case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
- case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
- case WindowManager.LayoutParams.TYPE_PHONE:
- case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
- case WindowManager.LayoutParams.TYPE_TOAST:
- case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
- case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
- case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
- case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
- case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
- case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
- case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
- case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
- Rect magnifiedRegionBounds = mTempRect2;
- mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
- magnifiedRegionBounds);
- Rect touchableRegionBounds = mTempRect1;
- windowState.getTouchableRegion(mTempRegion1);
- mTempRegion1.getBounds(touchableRegionBounds);
- if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
- try {
- mCallbacks.onRectangleOnScreenRequested(
- touchableRegionBounds.left,
- touchableRegionBounds.top,
- touchableRegionBounds.right,
- touchableRegionBounds.bottom);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- } break;
- } break;
- }
- }
- }
-
- public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
- MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
- if (spec != null && !spec.isNop()) {
- WindowManagerPolicy policy = mWindowManagerService.mPolicy;
- final int windowType = windowState.mAttrs.type;
- if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null
- && !policy.canMagnifyWindow(windowType)) {
- return null;
- }
- if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
- return null;
- }
- }
- return spec;
- }
-
- public void destroyLocked() {
- mMagnifedViewport.destroyWindow();
- }
-
- /** NOTE: This has to be called within a surface transaction. */
- public void drawMagnifiedRegionBorderIfNeededLocked() {
- mMagnifedViewport.drawWindowIfNeededLocked();
- }
-
- private final class MagnifiedViewport {
-
- private static final int DEFAUTLT_BORDER_WIDTH_DIP = 5;
-
- private final SparseArray<WindowStateInfo> mTempWindowStateInfos =
- new SparseArray<WindowStateInfo>();
-
- private final float[] mTempFloats = new float[9];
-
- private final RectF mTempRectF = new RectF();
-
- private final Point mTempPoint = new Point();
-
- private final Matrix mTempMatrix = new Matrix();
-
- private final Region mMagnifiedBounds = new Region();
- private final Region mOldMagnifiedBounds = new Region();
-
- private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
-
- private final WindowManager mWindowManager;
-
- private final int mBorderWidth;
- private final int mHalfBorderWidth;
-
- private final ViewportWindow mWindow;
-
- private boolean mFullRedrawNeeded;
-
- public MagnifiedViewport() {
- mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
- mBorderWidth = (int) TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, DEFAUTLT_BORDER_WIDTH_DIP,
- mContext.getResources().getDisplayMetrics());
- mHalfBorderWidth = (int) (mBorderWidth + 0.5) / 2;
- mWindow = new ViewportWindow(mContext);
- recomputeBoundsLocked();
- }
-
- public void updateMagnificationSpecLocked(MagnificationSpec spec) {
- if (spec != null) {
- mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
- } else {
- mMagnificationSpec.clear();
- }
- // If this message is pending we are in a rotation animation and do not want
- // to show the border. We will do so when the pending message is handled.
- if (!mHandler.hasMessages(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
- setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
- }
- }
-
- public void recomputeBoundsLocked() {
- mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- final int screenWidth = mTempPoint.x;
- final int screenHeight = mTempPoint.y;
-
- Region magnifiedBounds = mMagnifiedBounds;
- magnifiedBounds.set(0, 0, 0, 0);
-
- Region availableBounds = mTempRegion1;
- availableBounds.set(0, 0, screenWidth, screenHeight);
-
- Region nonMagnifiedBounds = mTempRegion4;
- nonMagnifiedBounds.set(0, 0, 0, 0);
-
- SparseArray<WindowStateInfo> visibleWindows = mTempWindowStateInfos;
- visibleWindows.clear();
- getWindowsOnScreenLocked(visibleWindows);
-
- final int visibleWindowCount = visibleWindows.size();
- for (int i = visibleWindowCount - 1; i >= 0; i--) {
- WindowStateInfo info = visibleWindows.valueAt(i);
- if (info.mWindowState.mAttrs.type == WindowManager
- .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
- continue;
- }
-
- Region windowBounds = mTempRegion2;
- Matrix matrix = mTempMatrix;
- populateTransformationMatrix(info.mWindowState, matrix);
- RectF windowFrame = mTempRectF;
-
- if (mWindowManagerService.mPolicy.canMagnifyWindow(info.mWindowState.mAttrs.type)) {
- windowFrame.set(info.mWindowState.mFrame);
- windowFrame.offset(-windowFrame.left, -windowFrame.top);
- matrix.mapRect(windowFrame);
- windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
- (int) windowFrame.right, (int) windowFrame.bottom);
- magnifiedBounds.op(windowBounds, Region.Op.UNION);
- magnifiedBounds.op(availableBounds, Region.Op.INTERSECT);
- } else {
- windowFrame.set(info.mTouchableRegion);
- windowFrame.offset(-info.mWindowState.mFrame.left,
- -info.mWindowState.mFrame.top);
- matrix.mapRect(windowFrame);
- windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
- (int) windowFrame.right, (int) windowFrame.bottom);
- nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
- windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE);
- availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
- }
-
- Region accountedBounds = mTempRegion2;
- accountedBounds.set(magnifiedBounds);
- accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
- accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
-
- if (accountedBounds.isRect()) {
- Rect accountedFrame = mTempRect1;
- accountedBounds.getBounds(accountedFrame);
- if (accountedFrame.width() == screenWidth
- && accountedFrame.height() == screenHeight) {
- break;
- }
- }
- }
-
- for (int i = visibleWindowCount - 1; i >= 0; i--) {
- WindowStateInfo info = visibleWindows.valueAt(i);
- info.recycle();
- visibleWindows.removeAt(i);
- }
-
- magnifiedBounds.op(mHalfBorderWidth, mHalfBorderWidth,
- screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth,
- Region.Op.INTERSECT);
-
- if (!mOldMagnifiedBounds.equals(magnifiedBounds)) {
- Region bounds = Region.obtain();
- bounds.set(magnifiedBounds);
- mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED,
- bounds).sendToTarget();
-
- mWindow.setBounds(magnifiedBounds);
- Rect dirtyRect = mTempRect1;
- if (mFullRedrawNeeded) {
- mFullRedrawNeeded = false;
- dirtyRect.set(mHalfBorderWidth, mHalfBorderWidth,
- screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth);
- mWindow.invalidate(dirtyRect);
- } else {
- Region dirtyRegion = mTempRegion3;
- dirtyRegion.set(magnifiedBounds);
- dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
- dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
- dirtyRegion.getBounds(dirtyRect);
- mWindow.invalidate(dirtyRect);
- }
-
- mOldMagnifiedBounds.set(magnifiedBounds);
- }
- }
-
- private void populateTransformationMatrix(WindowState windowState, Matrix outMatrix) {
- mTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
- mTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
- mTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
- mTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
- mTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left;
- mTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top;
- mTempFloats[Matrix.MPERSP_0] = 0;
- mTempFloats[Matrix.MPERSP_1] = 0;
- mTempFloats[Matrix.MPERSP_2] = 1;
- outMatrix.setValues(mTempFloats);
- }
-
- private void getWindowsOnScreenLocked(SparseArray<WindowStateInfo> outWindowStates) {
- DisplayContent displayContent = mWindowManagerService.getDefaultDisplayContentLocked();
- WindowList windowList = displayContent.getWindowList();
- final int windowCount = windowList.size();
- for (int i = 0; i < windowCount; i++) {
- WindowState windowState = windowList.get(i);
- if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager
- .LayoutParams.TYPE_UNIVERSE_BACKGROUND)
- && !windowState.mWinAnimator.mEnterAnimationPending) {
- outWindowStates.put(windowState.mLayer, WindowStateInfo.obtain(windowState));
- }
- }
- }
-
- public void onRotationChangedLocked() {
- // If we are magnifying, hide the magnified border window immediately so
- // the user does not see strange artifacts during rotation. The screenshot
- // used for rotation has already the border. After the rotation is complete
- // we will show the border.
- if (isMagnifyingLocked()) {
- setMagnifiedRegionBorderShownLocked(false, false);
- final long delay = (long) (mLongAnimationDuration
- * mWindowManagerService.mWindowAnimationScale);
- Message message = mHandler.obtainMessage(
- MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
- mHandler.sendMessageDelayed(message, delay);
- }
- recomputeBoundsLocked();
- mWindow.updateSize();
- }
-
- public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
- if (shown) {
- mFullRedrawNeeded = true;
- mOldMagnifiedBounds.set(0, 0, 0, 0);
- }
- mWindow.setShown(shown, animate);
- }
-
- public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
- MagnificationSpec spec = mMagnificationSpec;
- mMagnifiedBounds.getBounds(rect);
- rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
- rect.scale(1.0f / spec.scale);
- }
-
- public boolean isMagnifyingLocked() {
- return mMagnificationSpec.scale > 1.0f;
- }
-
- public MagnificationSpec getMagnificationSpecLocked() {
- return mMagnificationSpec;
- }
-
- /** NOTE: This has to be called within a surface transaction. */
- public void drawWindowIfNeededLocked() {
- recomputeBoundsLocked();
- mWindow.drawIfNeeded();
- }
-
- public void destroyWindow() {
- mWindow.releaseSurface();
- }
-
- private final class ViewportWindow {
- private static final String SURFACE_TITLE = "Magnification Overlay";
-
- private static final String PROPERTY_NAME_ALPHA = "alpha";
-
- private static final int MIN_ALPHA = 0;
- private static final int MAX_ALPHA = 255;
-
- private final Region mBounds = new Region();
- private final Rect mDirtyRect = new Rect();
- private final Paint mPaint = new Paint();
-
- private final ValueAnimator mShowHideFrameAnimator;
- private final SurfaceControl mSurfaceControl;
- private final Surface mSurface = new Surface();
-
- private boolean mShown;
- private int mAlpha;
-
- private boolean mInvalidated;
-
- public ViewportWindow(Context context) {
- SurfaceControl surfaceControl = null;
- try {
- mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession, SURFACE_TITLE,
- mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
- } catch (OutOfResourcesException oore) {
- /* ignore */
- }
- mSurfaceControl = surfaceControl;
- mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay().getLayerStack());
- mSurfaceControl.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw(
- WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
- * WindowManagerService.TYPE_LAYER_MULTIPLIER);
- mSurfaceControl.setPosition(0, 0);
- mSurface.copyFrom(mSurfaceControl);
-
- TypedValue typedValue = new TypedValue();
- context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
- typedValue, true);
- final int borderColor = context.getResources().getColor(typedValue.resourceId);
-
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeWidth(mBorderWidth);
- mPaint.setColor(borderColor);
-
- Interpolator interpolator = new DecelerateInterpolator(2.5f);
- final long longAnimationDuration = context.getResources().getInteger(
- com.android.internal.R.integer.config_longAnimTime);
-
- mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA,
- MIN_ALPHA, MAX_ALPHA);
- mShowHideFrameAnimator.setInterpolator(interpolator);
- mShowHideFrameAnimator.setDuration(longAnimationDuration);
- mInvalidated = true;
- }
-
- public void setShown(boolean shown, boolean animate) {
- synchronized (mWindowManagerService.mWindowMap) {
- if (mShown == shown) {
- return;
- }
- mShown = shown;
- if (animate) {
- if (mShowHideFrameAnimator.isRunning()) {
- mShowHideFrameAnimator.reverse();
- } else {
- if (shown) {
- mShowHideFrameAnimator.start();
- } else {
- mShowHideFrameAnimator.reverse();
- }
- }
- } else {
- mShowHideFrameAnimator.cancel();
- if (shown) {
- setAlpha(MAX_ALPHA);
- } else {
- setAlpha(MIN_ALPHA);
- }
- }
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
- }
- }
- }
-
- @SuppressWarnings("unused")
- // Called reflectively from an animator.
- public int getAlpha() {
- synchronized (mWindowManagerService.mWindowMap) {
- return mAlpha;
- }
- }
-
- public void setAlpha(int alpha) {
- synchronized (mWindowManagerService.mWindowMap) {
- if (mAlpha == alpha) {
- return;
- }
- mAlpha = alpha;
- invalidate(null);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
- }
- }
- }
-
- public void setBounds(Region bounds) {
- synchronized (mWindowManagerService.mWindowMap) {
- if (mBounds.equals(bounds)) {
- return;
- }
- mBounds.set(bounds);
- invalidate(mDirtyRect);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
- }
- }
- }
-
- public void updateSize() {
- synchronized (mWindowManagerService.mWindowMap) {
- mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
- invalidate(mDirtyRect);
- }
- }
-
- public void invalidate(Rect dirtyRect) {
- if (dirtyRect != null) {
- mDirtyRect.set(dirtyRect);
- } else {
- mDirtyRect.setEmpty();
- }
- mInvalidated = true;
- mWindowManagerService.scheduleAnimationLocked();
- }
-
- /** NOTE: This has to be called within a surface transaction. */
- public void drawIfNeeded() {
- synchronized (mWindowManagerService.mWindowMap) {
- if (!mInvalidated) {
- return;
- }
- mInvalidated = false;
- Canvas canvas = null;
- try {
- // Empty dirty rectangle means unspecified.
- if (mDirtyRect.isEmpty()) {
- mBounds.getBounds(mDirtyRect);
- }
- mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
- canvas = mSurface.lockCanvas(mDirtyRect);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
- }
- } catch (IllegalArgumentException iae) {
- /* ignore */
- } catch (Surface.OutOfResourcesException oore) {
- /* ignore */
- }
- if (canvas == null) {
- return;
- }
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "Bounds: " + mBounds);
- }
- canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
- mPaint.setAlpha(mAlpha);
- Path path = mBounds.getBoundaryPath();
- canvas.drawPath(path, mPaint);
-
- mSurface.unlockCanvasAndPost(canvas);
-
- if (mAlpha > 0) {
- mSurfaceControl.show();
- } else {
- mSurfaceControl.hide();
- }
- }
- }
-
- public void releaseSurface() {
- mSurfaceControl.release();
- mSurface.release();
- }
- }
- }
-
- private static final class WindowStateInfo {
- private static final int MAX_POOL_SIZE = 30;
-
- private static final SimplePool<WindowStateInfo> sPool =
- new SimplePool<WindowStateInfo>(MAX_POOL_SIZE);
-
- private static final Region mTempRegion = new Region();
-
- public WindowState mWindowState;
- public final Rect mTouchableRegion = new Rect();
-
- public static WindowStateInfo obtain(WindowState windowState) {
- WindowStateInfo info = sPool.acquire();
- if (info == null) {
- info = new WindowStateInfo();
- }
- info.mWindowState = windowState;
- windowState.getTouchableRegion(mTempRegion);
- mTempRegion.getBounds(info.mTouchableRegion);
- return info;
- }
-
- public void recycle() {
- mWindowState = null;
- mTouchableRegion.setEmpty();
- sPool.release(this);
- }
- }
-
- private class MyHandler extends Handler {
- public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1;
- public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
- public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
- public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
- public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
-
- public MyHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: {
- Region bounds = (Region) message.obj;
- try {
- mCallbacks.onMagnifedBoundsChanged(bounds);
- } catch (RemoteException re) {
- /* ignore */
- } finally {
- bounds.recycle();
- }
- } break;
- case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
- SomeArgs args = (SomeArgs) message.obj;
- final int left = args.argi1;
- final int top = args.argi2;
- final int right = args.argi3;
- final int bottom = args.argi4;
- try {
- mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
- } catch (RemoteException re) {
- /* ignore */
- } finally {
- args.recycle();
- }
- } break;
- case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
- try {
- mCallbacks.onUserContextChanged();
- } catch (RemoteException re) {
- /* ignore */
- }
- } break;
- case MESSAGE_NOTIFY_ROTATION_CHANGED: {
- final int rotation = message.arg1;
- try {
- mCallbacks.onRotationChanged(rotation);
- } catch (RemoteException re) {
- /* ignore */
- }
- } break;
- case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
- synchronized (mWindowManagerService.mWindowMap) {
- if (mMagnifedViewport.isMagnifyingLocked()) {
- mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
- mWindowManagerService.scheduleAnimationLocked();
- }
- }
- } break;
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 0c68258..266527d 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -529,8 +529,9 @@ public class WindowAnimator {
mAnimating |= mService.getDisplayContentLocked(displayId).animateDimLayers();
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) {
- mService.mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+ if (mService.mAccessibilityController != null
+ && displayId == Display.DEFAULT_DISPLAY) {
+ mService.mAccessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a4f960e..4f80f1f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -103,7 +103,6 @@ import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IApplicationToken;
import android.view.IInputFilter;
-import android.view.IMagnificationCallbacks;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindow;
@@ -418,7 +417,7 @@ public class WindowManagerService extends IWindowManager.Stub
IInputMethodManager mInputMethodManager;
- DisplayMagnifier mDisplayMagnifier;
+ AccessibilityController mAccessibilityController;
final SurfaceSession mFxSession;
Watermark mWatermark;
@@ -2439,9 +2438,9 @@ public class WindowManagerService extends IWindowManager.Stub
win.mExiting = true;
}
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mDisplayMagnifier != null
+ if (mAccessibilityController != null
&& win.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onWindowTransitionLocked(win, transit);
+ mAccessibilityController.onWindowTransitionLocked(win, transit);
}
}
if (win.mExiting || win.mWinAnimator.isAnimating()) {
@@ -2759,11 +2758,12 @@ public class WindowManagerService extends IWindowManager.Stub
public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) {
synchronized (mWindowMap) {
- if (mDisplayMagnifier != null) {
+ if (mAccessibilityController != null) {
WindowState window = mWindowMap.get(token);
//TODO (multidisplay): Magnification is supported only for the default display.
if (window != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate);
+ mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle,
+ immediate);
}
}
}
@@ -2998,9 +2998,9 @@ public class WindowManagerService extends IWindowManager.Stub
winAnimator.destroySurfaceLocked();
}
//TODO (multidisplay): Magnification is supported only for the default
- if (mDisplayMagnifier != null
+ if (mAccessibilityController != null
&& win.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onWindowTransitionLocked(win, transit);
+ mAccessibilityController.onWindowTransitionLocked(win, transit);
}
}
}
@@ -3143,86 +3143,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- @Override
- public void getWindowFrame(IBinder token, Rect outBounds) {
- if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
- "getWindowInfo()")) {
- throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
- }
- synchronized (mWindowMap) {
- WindowState windowState = mWindowMap.get(token);
- if (windowState != null) {
- outBounds.set(windowState.mFrame);
- } else {
- outBounds.setEmpty();
- }
- }
- }
-
- @Override
- public void setMagnificationSpec(MagnificationSpec spec) {
- if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY,
- "setMagnificationSpec()")) {
- throw new SecurityException("Requires MAGNIFY_DISPLAY permission.");
- }
- synchronized (mWindowMap) {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.setMagnificationSpecLocked(spec);
- } else {
- throw new IllegalStateException("Magnification callbacks not set!");
- }
- }
- if (Binder.getCallingPid() != android.os.Process.myPid()) {
- spec.recycle();
- }
- }
-
- @Override
- public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
- if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY,
- "getCompatibleMagnificationSpecForWindow()")) {
- throw new SecurityException("Requires MAGNIFY_DISPLAY permission.");
- }
- synchronized (mWindowMap) {
- WindowState windowState = mWindowMap.get(windowToken);
- if (windowState == null) {
- return null;
- }
- MagnificationSpec spec = null;
- if (mDisplayMagnifier != null) {
- spec = mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
- }
- if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
- return null;
- }
- spec = (spec == null) ? MagnificationSpec.obtain() : MagnificationSpec.obtain(spec);
- spec.scale *= windowState.mGlobalScale;
- return spec;
- }
- }
-
- @Override
- public void setMagnificationCallbacks(IMagnificationCallbacks callbacks) {
- if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY,
- "setMagnificationCallbacks()")) {
- throw new SecurityException("Requires MAGNIFY_DISPLAY permission.");
- }
- synchronized (mWindowMap) {
- if (mDisplayMagnifier == null) {
- mDisplayMagnifier = new DisplayMagnifier(this, callbacks);
- } else {
- if (callbacks == null) {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.destroyLocked();
- mDisplayMagnifier = null;
- }
- } else {
- throw new IllegalStateException("Magnification callbacks already set!");
- }
- }
- }
- }
-
private boolean applyAnimationLocked(AppWindowToken atoken,
WindowManager.LayoutParams lp, int transit, boolean enter) {
// Only apply an animation if the display isn't frozen. If it is
@@ -3235,7 +3155,22 @@ public class WindowManagerService extends IWindowManager.Stub
final int height = displayInfo.appHeight;
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation: atoken="
+ atoken);
- Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height);
+
+ // Determine the visible rect to calculate the thumbnail clip
+ WindowState win = atoken.findMainWindow();
+ Rect containingFrame = new Rect(0, 0, width, height);
+ Rect contentInsets = new Rect();
+ if (win != null) {
+ if (win.mContainingFrame != null) {
+ containingFrame.set(win.mContainingFrame);
+ }
+ if (win.mContentInsets != null) {
+ contentInsets.set(win.mContentInsets);
+ }
+ }
+
+ Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
+ containingFrame, contentInsets);
if (a != null) {
if (DEBUG_ANIM) {
RuntimeException e = null;
@@ -3402,8 +3337,8 @@ public class WindowManagerService extends IWindowManager.Stub
win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT,
false);
//TODO (multidisplay): Magnification is supported only for the default
- if (mDisplayMagnifier != null && win.isDefaultDisplay()) {
- mDisplayMagnifier.onWindowTransitionLocked(win,
+ if (mAccessibilityController != null && win.isDefaultDisplay()) {
+ mAccessibilityController.onWindowTransitionLocked(win,
WindowManagerPolicy.TRANSIT_EXIT);
}
changed = true;
@@ -4259,9 +4194,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
WindowState window = wtoken.findMainWindow();
//TODO (multidisplay): Magnification is supported only for the default display.
- if (window != null && mDisplayMagnifier != null
+ if (window != null && mAccessibilityController != null
&& window.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onAppWindowTransitionLocked(window, transit);
+ mAccessibilityController.onAppWindowTransitionLocked(window, transit);
}
changed = true;
}
@@ -4281,9 +4216,9 @@ public class WindowManagerService extends IWindowManager.Stub
win.mWinAnimator.applyAnimationLocked(
WindowManagerPolicy.TRANSIT_ENTER, true);
//TODO (multidisplay): Magnification is supported only for the default
- if (mDisplayMagnifier != null
+ if (mAccessibilityController != null
&& win.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onWindowTransitionLocked(win,
+ mAccessibilityController.onWindowTransitionLocked(win,
WindowManagerPolicy.TRANSIT_ENTER);
}
}
@@ -4298,9 +4233,9 @@ public class WindowManagerService extends IWindowManager.Stub
win.mWinAnimator.applyAnimationLocked(
WindowManagerPolicy.TRANSIT_EXIT, false);
//TODO (multidisplay): Magnification is supported only for the default
- if (mDisplayMagnifier != null
+ if (mAccessibilityController != null
&& win.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onWindowTransitionLocked(win,
+ mAccessibilityController.onWindowTransitionLocked(win,
WindowManagerPolicy.TRANSIT_EXIT);
}
}
@@ -5278,19 +5213,6 @@ public class WindowManagerService extends IWindowManager.Stub
ShutdownThread.rebootSafeMode(mContext, confirm);
}
- @Override
- public void setInputFilter(IInputFilter filter) {
- if (!checkCallingPermission(android.Manifest.permission.FILTER_EVENTS, "setInputFilter()")) {
- throw new SecurityException("Requires FILTER_EVENTS permission");
- }
- mInputManager.setInputFilter(filter);
- }
-
- @Override
- public void setTouchExplorationEnabled(boolean enabled) {
- mPolicy.setTouchExplorationEnabled(enabled);
- }
-
public void updateRelatedUserIds(final int[] relatedUserIds) {
synchronized (mWindowMap) {
mRelatedUserIds = relatedUserIds;
@@ -6106,9 +6028,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mDisplayMagnifier != null
+ if (mAccessibilityController != null
&& displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation);
+ mAccessibilityController.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation);
}
return true;
@@ -7015,21 +6937,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- @Override
- public IBinder getFocusedWindowToken() {
- if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
- "getFocusedWindowToken()")) {
- throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
- }
- synchronized (mWindowMap) {
- WindowState windowState = getFocusedWindowLocked();
- if (windowState != null) {
- return windowState.mClient.asBinder();
- }
- return null;
- }
- }
-
private WindowState getFocusedWindow() {
synchronized (mWindowMap) {
return getFocusedWindowLocked();
@@ -8209,9 +8116,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mDisplayMagnifier != null && anyLayerChanged
+ if (mAccessibilityController != null && anyLayerChanged
&& windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onWindowLayersChangedLocked();
+ mAccessibilityController.onWindowLayersChangedLocked();
}
}
@@ -8716,11 +8623,13 @@ public class WindowManagerService extends IWindowManager.Stub
wtoken.deferClearAllDrawn = false;
}
+ boolean useAlternateThumbnailAnimation =
+ SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false);
AppWindowAnimator appAnimator =
topOpeningApp == null ? null : topOpeningApp.mAppAnimator;
Bitmap nextAppTransitionThumbnail = mAppTransition.getNextAppTransitionThumbnail();
- if (nextAppTransitionThumbnail != null && appAnimator != null
- && appAnimator.animation != null) {
+ if (!useAlternateThumbnailAnimation && nextAppTransitionThumbnail != null
+ && appAnimator != null && appAnimator.animation != null) {
// This thumbnail animation is very special, we need to have
// an extra surface with the thumbnail included with the animation.
Rect dirty = new Rect(0, 0, nextAppTransitionThumbnail.getWidth(),
@@ -8744,8 +8653,8 @@ public class WindowManagerService extends IWindowManager.Stub
drawSurface.release();
appAnimator.thumbnailLayer = topOpeningLayer;
DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
- Animation anim = mAppTransition.createThumbnailAnimationLocked(
- transit, true, true, displayInfo.appWidth, displayInfo.appHeight);
+ Animation anim = mAppTransition.createThumbnailScaleAnimationLocked(
+ displayInfo.appWidth, displayInfo.appHeight, transit);
appAnimator.thumbnailAnimation = anim;
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mTransitionAnimationScale);
@@ -9844,6 +9753,11 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowState oldFocus = mCurrentFocus;
mCurrentFocus = newFocus;
mLosingFocus.remove(newFocus);
+
+ if (mAccessibilityController != null) {
+ mAccessibilityController.onWindowFocusChangedLocked();
+ }
+
int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
if (imWindowChanged && oldFocus != mInputMethodWindow) {
@@ -10957,5 +10871,100 @@ public class WindowManagerService extends IWindowManager.Stub
public void requestTraversalFromDisplayManager() {
requestTraversal();
}
+
+ @Override
+ public void setMagnificationSpec(MagnificationSpec spec) {
+ synchronized (mWindowMap) {
+ if (mAccessibilityController != null) {
+ mAccessibilityController.setMagnificationSpecLocked(spec);
+ } else {
+ throw new IllegalStateException("Magnification callbacks not set!");
+ }
+ }
+ if (Binder.getCallingPid() != android.os.Process.myPid()) {
+ spec.recycle();
+ }
+ }
+
+ @Override
+ public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
+ synchronized (mWindowMap) {
+ WindowState windowState = mWindowMap.get(windowToken);
+ if (windowState == null) {
+ return null;
+ }
+ MagnificationSpec spec = null;
+ if (mAccessibilityController != null) {
+ spec = mAccessibilityController.getMagnificationSpecForWindowLocked(windowState);
+ }
+ if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
+ return null;
+ }
+ spec = (spec == null) ? MagnificationSpec.obtain() : MagnificationSpec.obtain(spec);
+ spec.scale *= windowState.mGlobalScale;
+ return spec;
+ }
+ }
+
+ @Override
+ public void setMagnificationCallbacks(MagnificationCallbacks callbacks) {
+ synchronized (mWindowMap) {
+ if (mAccessibilityController == null) {
+ mAccessibilityController = new AccessibilityController(
+ WindowManagerService.this);
+ }
+ mAccessibilityController.setMagnificationCallbacksLocked(callbacks);
+ if (!mAccessibilityController.hasCallbacksLocked()) {
+ mAccessibilityController = null;
+ }
+ }
+ }
+
+ @Override
+ public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
+ synchronized (mWindowMap) {
+ if (mAccessibilityController == null) {
+ mAccessibilityController = new AccessibilityController(
+ WindowManagerService.this);
+ }
+ mAccessibilityController.setWindowsForAccessibilityCallback(callback);
+ if (!mAccessibilityController.hasCallbacksLocked()) {
+ mAccessibilityController = null;
+ }
+ }
+ }
+
+ @Override
+ public void setInputFilter(IInputFilter filter) {
+ mInputManager.setInputFilter(filter);
+ }
+
+ @Override
+ public IBinder getFocusedWindowToken() {
+ synchronized (mWindowMap) {
+ WindowState windowState = getFocusedWindowLocked();
+ if (windowState != null) {
+ return windowState.mClient.asBinder();
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public boolean isKeyguardLocked() {
+ return isKeyguardLocked();
+ }
+
+ @Override
+ public void getWindowFrame(IBinder token, Rect outBounds) {
+ synchronized (mWindowMap) {
+ WindowState windowState = mWindowMap.get(token);
+ if (windowState != null) {
+ outBounds.set(windowState.mFrame);
+ } else {
+ outBounds.setEmpty();
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 93f6d22..6b3c368 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -113,6 +113,11 @@ class WindowStateAnimator {
float mAlpha = 0;
float mLastAlpha = 0;
+ boolean mHasClipRect;
+ Rect mClipRect = new Rect();
+ Rect mTmpClipRect = new Rect();
+ Rect mLastClipRect = new Rect();
+
// Used to save animation distances between the time they are calculated and when they are
// used.
int mAnimDw;
@@ -951,9 +956,10 @@ class WindowStateAnimator {
if (screenAnimation) {
tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
}
+
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) {
- MagnificationSpec spec = mService.mDisplayMagnifier
+ if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
+ MagnificationSpec spec = mService.mAccessibilityController
.getMagnificationSpecForWindowLocked(mWin);
if (spec != null && !spec.isNop()) {
tmpMatrix.postScale(spec.scale, spec.scale);
@@ -985,6 +991,7 @@ class WindowStateAnimator {
// transforming since it is more important to have that
// animation be smooth.
mShownAlpha = mAlpha;
+ mHasClipRect = false;
if (!mService.mLimitedAlphaCompositing
|| (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
|| (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
@@ -998,6 +1005,10 @@ class WindowStateAnimator {
}
if (appTransformation != null) {
mShownAlpha *= appTransformation.getAlpha();
+ if (appTransformation.hasClipRect()) {
+ mClipRect.set(appTransformation.getClipRect());
+ mHasClipRect = true;
+ }
}
if (mAnimator.mUniverseBackground != null) {
mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha();
@@ -1032,8 +1043,8 @@ class WindowStateAnimator {
&& mWin.mBaseLayer < mAnimator.mAboveUniverseLayer);
MagnificationSpec spec = null;
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) {
- spec = mService.mDisplayMagnifier.getMagnificationSpecForWindowLocked(mWin);
+ if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
+ spec = mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
}
if (applyUniverseTransformation || spec != null) {
final Rect frame = mWin.mFrame;
@@ -1149,15 +1160,32 @@ class WindowStateAnimator {
applyDecorRect(w.mDecorFrame);
}
- if (!w.mSystemDecorRect.equals(w.mLastSystemDecorRect)) {
- w.mLastSystemDecorRect.set(w.mSystemDecorRect);
+ // By default, the clip rect is the system decor rect
+ Rect clipRect = w.mSystemDecorRect;
+ if (mHasClipRect) {
+
+ // If we have an animated clip rect, intersect it with the system decor rect
+ // NOTE: We are adding a temporary workaround due to the status bar not always reporting
+ // the correct system decor rect. In such cases, we take into account the specified
+ // content insets as well.
+ int offsetTop = Math.max(w.mSystemDecorRect.top, w.mContentInsets.top);
+ mTmpClipRect.set(w.mSystemDecorRect);
+ mTmpClipRect.offset(0, -offsetTop);
+ mTmpClipRect.intersect(mClipRect);
+ mTmpClipRect.offset(0, offsetTop);
+ clipRect = mTmpClipRect;
+
+ }
+
+ if (!clipRect.equals(mLastClipRect)) {
+ mLastClipRect.set(clipRect);
try {
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
- "CROP " + w.mSystemDecorRect.toShortString(), null);
- mSurfaceControl.setWindowCrop(w.mSystemDecorRect);
+ "CROP " + clipRect.toShortString(), null);
+ mSurfaceControl.setWindowCrop(clipRect);
} catch (RuntimeException e) {
Slog.w(TAG, "Error setting crop surface of " + w
- + " crop=" + w.mSystemDecorRect.toShortString(), e);
+ + " crop=" + clipRect.toShortString(), e);
if (!recoveringMemory) {
mService.reclaimSomeSurfaceMemoryLocked(this, "crop", true);
}
@@ -1302,8 +1330,8 @@ class WindowStateAnimator {
mSurfaceLayer = mAnimLayer;
mSurfaceControl.setLayer(mAnimLayer);
mSurfaceControl.setMatrix(
- mDsDx*w.mHScale, mDtDx*w.mVScale,
- mDsDy*w.mHScale, mDtDy*w.mVScale);
+ mDsDx * w.mHScale, mDtDx * w.mVScale,
+ mDsDy * w.mHScale, mDtDy * w.mVScale);
if (mLastHidden && mDrawState == HAS_DRAWN) {
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
@@ -1565,9 +1593,9 @@ class WindowStateAnimator {
}
applyAnimationLocked(transit, true);
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mDisplayMagnifier != null
+ if (mService.mAccessibilityController != null
&& mWin.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mService.mDisplayMagnifier.onWindowTransitionLocked(mWin, transit);
+ mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
index 7a30d31..0d5daa5 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
@@ -141,14 +141,21 @@ public class NetworkManagementServiceTest extends AndroidTestCase {
/**
* Interface class activity.
*/
+
sendMessage("613 IfaceClass active rmnet0");
- expectSoon(observer).interfaceClassDataActivityChanged("rmnet0", true);
+ expectSoon(observer).interfaceClassDataActivityChanged("rmnet0", true, 0);
+
+ sendMessage("613 IfaceClass active rmnet0 1234");
+ expectSoon(observer).interfaceClassDataActivityChanged("rmnet0", true, 1234);
sendMessage("613 IfaceClass idle eth0");
- expectSoon(observer).interfaceClassDataActivityChanged("eth0", false);
+ expectSoon(observer).interfaceClassDataActivityChanged("eth0", false, 0);
+
+ sendMessage("613 IfaceClass idle eth0 1234");
+ expectSoon(observer).interfaceClassDataActivityChanged("eth0", false, 1234);
- sendMessage("613 IfaceClass reallyactive rmnet0");
- expectSoon(observer).interfaceClassDataActivityChanged("rmnet0", false);
+ sendMessage("613 IfaceClass reallyactive rmnet0 1234");
+ expectSoon(observer).interfaceClassDataActivityChanged("rmnet0", false, 1234);
sendMessage("613 InterfaceClass reallyactive rmnet0");
// Invalid group.
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
index 90b6abc..b12ed94 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
@@ -18,6 +18,7 @@ package com.android.framework.permission.tests;
import junit.framework.TestCase;
+import android.media.AudioManager;
import android.os.Binder;
import android.os.IVibratorService;
import android.os.Process;
@@ -47,7 +48,8 @@ public class VibratorServicePermissionTest extends TestCase {
*/
public void testVibrate() throws RemoteException {
try {
- mVibratorService.vibrate(Process.myUid(), null, 2000, new Binder());
+ mVibratorService.vibrate(Process.myUid(), null, 2000, AudioManager.STREAM_ALARM,
+ new Binder());
fail("vibrate did not throw SecurityException as expected");
} catch (SecurityException e) {
// expected
@@ -63,7 +65,8 @@ public class VibratorServicePermissionTest extends TestCase {
*/
public void testVibratePattern() throws RemoteException {
try {
- mVibratorService.vibratePattern(Process.myUid(), null, new long[] {0}, 0, new Binder());
+ mVibratorService.vibratePattern(Process.myUid(), null, new long[] {0}, 0,
+ AudioManager.STREAM_ALARM, new Binder());
fail("vibratePattern did not throw SecurityException as expected");
} catch (SecurityException e) {
// expected
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index dd2cbc1..743a26c 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -458,44 +458,8 @@ public class IWindowManagerImpl implements IWindowManager {
}
@Override
- public IBinder getFocusedWindowToken() {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public void setInputFilter(IInputFilter filter) throws RemoteException {
- // TODO Auto-generated method stub
- }
-
- @Override
- public void getWindowFrame(IBinder token, Rect outFrame) {
- // TODO Auto-generated method stub
- }
-
- @Override
- public void setMagnificationCallbacks(IMagnificationCallbacks callbacks) {
- // TODO Auto-generated method stub
- }
-
- @Override
- public void setMagnificationSpec(MagnificationSpec spec) {
- // TODO Auto-generated method stub
- }
-
- @Override
- public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
public boolean isRotationFrozen() throws RemoteException {
// TODO Auto-generated method stub
return false;
}
-
- @Override
- public void setTouchExplorationEnabled(boolean enabled) {
- }
}