summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk7
-rw-r--r--CleanSpec.mk1
-rw-r--r--api/current.txt618
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java361
-rw-r--r--cmds/app_process/Android.mk2
-rw-r--r--cmds/bootanimation/Android.mk1
-rw-r--r--cmds/input/src/com/android/commands/input/Input.java10
-rw-r--r--cmds/media/Android.mk15
-rw-r--r--cmds/media/MODULE_LICENSE_APACHE20
-rw-r--r--cmds/media/NOTICE190
-rwxr-xr-xcmds/media/media6
-rw-r--r--cmds/media/src/com/android/commands/media/Media.java220
-rw-r--r--cmds/system_server/Android.mk4
-rw-r--r--cmds/system_server/library/Android.mk5
-rw-r--r--cmds/wm/src/com/android/commands/wm/Wm.java114
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java88
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl3
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl117
-rw-r--r--core/java/android/accounts/CantAddAccountActivity.java40
-rw-r--r--core/java/android/animation/Animatable.java72
-rw-r--r--core/java/android/animation/Animator.java52
-rw-r--r--core/java/android/animation/AnimatorSet.java31
-rw-r--r--core/java/android/animation/RectEvaluator.java2
-rw-r--r--core/java/android/animation/ValueAnimator.java24
-rw-r--r--core/java/android/app/ActionBar.java80
-rw-r--r--core/java/android/app/Activity.java14
-rw-r--r--core/java/android/app/ActivityManagerNative.java43
-rw-r--r--core/java/android/app/ActivityThread.java14
-rw-r--r--core/java/android/app/ApplicationLoaders.java10
-rw-r--r--core/java/android/app/ContextImpl.java9
-rw-r--r--core/java/android/app/IActivityManager.java8
-rw-r--r--core/java/android/app/INotificationManager.aidl12
-rw-r--r--core/java/android/app/Instrumentation.java8
-rw-r--r--core/java/android/app/NativeActivity.java39
-rw-r--r--core/java/android/app/Notification.java2
-rw-r--r--core/java/android/app/UiAutomation.java25
-rw-r--r--core/java/android/app/WallpaperManager.java6
-rw-r--r--core/java/android/bluetooth/BluetoothGattServer.java4
-rw-r--r--core/java/android/content/Context.java6
-rw-r--r--core/java/android/content/SharedPreferences.java8
-rw-r--r--core/java/android/content/pm/ActivityInfo.java32
-rw-r--r--core/java/android/content/pm/PackageInfo.java7
-rw-r--r--core/java/android/content/pm/PackageManager.java1
-rw-r--r--core/java/android/content/pm/PackageParser.java87
-rw-r--r--core/java/android/content/res/AssetManager.java8
-rw-r--r--core/java/android/content/res/Resources.java29
-rw-r--r--core/java/android/hardware/Sensor.java106
-rw-r--r--core/java/android/hardware/SensorEvent.java167
-rw-r--r--core/java/android/hardware/SensorManager.java89
-rw-r--r--core/java/android/hardware/SystemSensorManager.java394
-rw-r--r--core/java/android/hardware/TriggerEvent.java62
-rw-r--r--core/java/android/hardware/TriggerEventListener.java78
-rw-r--r--core/java/android/hardware/location/GeofenceHardware.java439
-rw-r--r--core/java/android/hardware/location/GeofenceHardwareCallback.java100
-rw-r--r--core/java/android/hardware/location/GeofenceHardwareImpl.java599
-rw-r--r--core/java/android/hardware/location/GeofenceHardwareService.java134
-rw-r--r--core/java/android/hardware/location/IGeofenceHardware.aidl36
-rw-r--r--core/java/android/hardware/location/IGeofenceHardwareCallback.aidl30
-rw-r--r--core/java/android/net/ConnectivityManager.java2
-rw-r--r--core/java/android/net/RouteInfo.java7
-rw-r--r--core/java/android/os/Binder.java10
-rw-r--r--core/java/android/os/LatencyTimer.java94
-rw-r--r--core/java/android/os/Looper.java20
-rw-r--r--core/java/android/os/MessageQueue.java11
-rw-r--r--core/java/android/os/Process.java5
-rw-r--r--core/java/android/os/Trace.java139
-rw-r--r--core/java/android/os/UserManager.java32
-rw-r--r--core/java/android/provider/ContactsContract.java109
-rw-r--r--core/java/android/provider/Settings.java30
-rw-r--r--core/java/android/security/IKeystoreService.java16
-rw-r--r--core/java/android/service/notification/INotificationListener.aidl (renamed from core/java/android/app/INotificationListener.aidl)4
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java138
-rw-r--r--core/java/android/service/notification/StatusBarNotification.aidl (renamed from core/java/com/android/internal/statusbar/StatusBarNotification.aidl)2
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java (renamed from core/java/com/android/internal/statusbar/StatusBarNotification.java)37
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java2
-rw-r--r--core/java/android/text/bidi/BidiFormatter.java8
-rw-r--r--core/java/android/view/HardwareRenderer.java17
-rw-r--r--core/java/android/view/InputChannel.java12
-rw-r--r--core/java/android/view/InputDevice.java23
-rw-r--r--core/java/android/view/LayoutInflater.java11
-rw-r--r--core/java/android/view/SimulatedDpad.java298
-rw-r--r--core/java/android/view/SurfaceView.java1
-rw-r--r--core/java/android/view/View.java85
-rw-r--r--core/java/android/view/ViewGroup.java80
-rw-r--r--core/java/android/view/ViewGroupOverlay.java (renamed from core/java/android/view/Overlay.java)60
-rw-r--r--core/java/android/view/ViewOverlay.java393
-rw-r--r--core/java/android/view/ViewPropertyAnimator.java10
-rw-r--r--core/java/android/view/ViewRootImpl.java2243
-rw-r--r--core/java/android/view/WindowManager.java6
-rw-r--r--core/java/android/view/WindowManagerGlobal.java6
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java329
-rw-r--r--core/java/android/webkit/HTML5Audio.java9
-rw-r--r--core/java/android/webkit/WebChromeClient.java7
-rw-r--r--core/java/android/webkit/WebSettings.java4
-rw-r--r--core/java/android/webkit/WebView.java12
-rw-r--r--core/java/android/webkit/WebViewDatabase.java6
-rw-r--r--core/java/android/webkit/WebViewFactory.java30
-rw-r--r--core/java/android/widget/AbsListView.java2
-rw-r--r--core/java/android/widget/AppSecurityPermissions.java63
-rw-r--r--core/java/android/widget/ImageView.java2
-rw-r--r--core/java/android/widget/ListView.java2
-rw-r--r--core/java/android/widget/RelativeLayout.java30
-rw-r--r--core/java/android/widget/Spinner.java29
-rw-r--r--core/java/android/widget/TextView.java7
-rw-r--r--core/java/com/android/internal/app/ActionBarImpl.java20
-rw-r--r--core/java/com/android/internal/app/LocalePicker.java45
-rw-r--r--core/java/com/android/internal/os/BaseCommand.java146
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl2
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl2
-rw-r--r--core/java/com/android/internal/view/InputBindResult.java2
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuItem.java2
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java86
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java47
-rw-r--r--core/java/com/android/internal/widget/ScrollingTabContainerView.java3
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/android/graphics/Canvas.cpp9
-rw-r--r--core/jni/android_hardware_SensorManager.cpp20
-rw-r--r--core/jni/android_os_Trace.cpp54
-rw-r--r--core/jni/android_util_AssetManager.cpp2
-rw-r--r--core/jni/android_view_InputChannel.cpp11
-rw-r--r--core/jni/android_view_InputDevice.cpp6
-rw-r--r--core/res/AndroidManifest.xml33
-rw-r--r--core/res/res/anim/rotation_animation_xfade_exit.xml2
-rw-r--r--core/res/res/drawable-hdpi/menu_popup_panel_holo_dark.9.pngbin0 -> 1312 bytes
-rw-r--r--core/res/res/drawable-hdpi/menu_popup_panel_holo_light.9.pngbin0 -> 1305 bytes
-rw-r--r--core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_dark.9.pngbin0 -> 778 bytes
-rw-r--r--core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_light.9.pngbin0 -> 687 bytes
-rw-r--r--core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_dark.9.pngbin0 -> 411 bytes
-rw-r--r--core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_light.9.pngbin0 -> 438 bytes
-rw-r--r--core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_dark.9.pngbin0 -> 565 bytes
-rw-r--r--core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_light.9.pngbin0 -> 536 bytes
-rw-r--r--core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_dark.9.pngbin0 -> 459 bytes
-rw-r--r--core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_light.9.pngbin0 -> 429 bytes
-rw-r--r--core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_dark.9.pngbin0 -> 274 bytes
-rw-r--r--core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_light.9.pngbin0 -> 270 bytes
-rw-r--r--core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_dark.9.pngbin0 -> 332 bytes
-rw-r--r--core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_light.9.pngbin0 -> 328 bytes
-rw-r--r--core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_dark.9.pngbin0 -> 807 bytes
-rw-r--r--core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_light.9.pngbin0 -> 713 bytes
-rw-r--r--core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_dark.9.pngbin0 -> 422 bytes
-rw-r--r--core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_light.9.pngbin0 -> 456 bytes
-rw-r--r--core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_dark.9.pngbin0 -> 496 bytes
-rw-r--r--core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_light.9.pngbin0 -> 489 bytes
-rw-r--r--core/res/res/drawable-mdpi/menu_popup_panel_holo_dark.9.pngbin0 -> 824 bytes
-rw-r--r--core/res/res/drawable-mdpi/menu_popup_panel_holo_light.9.pngbin0 -> 830 bytes
-rw-r--r--core/res/res/drawable-xhdpi/menu_popup_panel_holo_dark.9.pngbin0 -> 1901 bytes
-rw-r--r--core/res/res/drawable-xhdpi/menu_popup_panel_holo_light.9.pngbin0 -> 1880 bytes
-rw-r--r--core/res/res/drawable/menu_panel_holo_dark.xml20
-rw-r--r--core/res/res/drawable/menu_panel_holo_light.xml20
-rw-r--r--core/res/res/layout/select_dialog.xml3
-rw-r--r--core/res/res/layout/select_dialog_holo.xml3
-rw-r--r--core/res/res/values-af/strings.xml19
-rw-r--r--core/res/res/values-am/strings.xml23
-rw-r--r--core/res/res/values-ar/strings.xml19
-rw-r--r--core/res/res/values-be/strings.xml10
-rw-r--r--core/res/res/values-bg/strings.xml10
-rw-r--r--core/res/res/values-ca/strings.xml10
-rw-r--r--core/res/res/values-cs/strings.xml10
-rw-r--r--core/res/res/values-da/strings.xml19
-rw-r--r--core/res/res/values-de/strings.xml14
-rw-r--r--core/res/res/values-el/strings.xml10
-rw-r--r--core/res/res/values-en-rGB/strings.xml10
-rw-r--r--core/res/res/values-es-rUS/strings.xml19
-rw-r--r--core/res/res/values-es/strings.xml35
-rw-r--r--core/res/res/values-et/strings.xml12
-rw-r--r--core/res/res/values-fa/strings.xml19
-rw-r--r--core/res/res/values-fi/strings.xml10
-rw-r--r--core/res/res/values-fr/strings.xml10
-rw-r--r--core/res/res/values-hi/strings.xml19
-rw-r--r--core/res/res/values-hr/strings.xml10
-rw-r--r--core/res/res/values-hu/strings.xml10
-rw-r--r--core/res/res/values-in/strings.xml10
-rw-r--r--core/res/res/values-it/strings.xml10
-rw-r--r--core/res/res/values-iw/strings.xml10
-rw-r--r--core/res/res/values-ja/strings.xml10
-rw-r--r--core/res/res/values-ko/strings.xml10
-rw-r--r--core/res/res/values-lt/strings.xml10
-rw-r--r--core/res/res/values-lv/strings.xml10
-rw-r--r--core/res/res/values-ms/strings.xml10
-rw-r--r--core/res/res/values-nb/strings.xml10
-rw-r--r--core/res/res/values-nl/strings.xml10
-rw-r--r--core/res/res/values-pl/strings.xml12
-rw-r--r--core/res/res/values-pt-rPT/strings.xml10
-rw-r--r--core/res/res/values-pt/strings.xml12
-rw-r--r--core/res/res/values-rm/strings.xml16
-rw-r--r--core/res/res/values-ro/strings.xml10
-rw-r--r--core/res/res/values-ru/strings.xml10
-rw-r--r--core/res/res/values-sk/strings.xml28
-rw-r--r--core/res/res/values-sl/strings.xml10
-rw-r--r--core/res/res/values-sr/strings.xml10
-rw-r--r--core/res/res/values-sv/strings.xml10
-rw-r--r--core/res/res/values-sw/strings.xml23
-rw-r--r--core/res/res/values-th/strings.xml25
-rw-r--r--core/res/res/values-tl/strings.xml10
-rw-r--r--core/res/res/values-tr/strings.xml19
-rw-r--r--core/res/res/values-uk/strings.xml10
-rw-r--r--core/res/res/values-vi/strings.xml10
-rw-r--r--core/res/res/values-zh-rCN/strings.xml10
-rw-r--r--core/res/res/values-zh-rTW/strings.xml12
-rw-r--r--core/res/res/values-zu/strings.xml19
-rw-r--r--core/res/res/values/arrays.xml4
-rw-r--r--core/res/res/values/attrs_manifest.xml31
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--core/res/res/values/strings.xml26
-rw-r--r--core/res/res/values/styles.xml4
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--core/res/res/xml/storage_list.xml22
-rw-r--r--core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java12
-rw-r--r--data/fonts/DroidSerif-Bold.ttfbin185228 -> 249880 bytes
-rw-r--r--data/fonts/DroidSerif-BoldItalic.ttfbin190304 -> 265008 bytes
-rw-r--r--data/fonts/DroidSerif-Italic.ttfbin177560 -> 251944 bytes
-rw-r--r--data/fonts/DroidSerif-Regular.ttfbin172916 -> 248904 bytes
-rw-r--r--data/fonts/Roboto-Bold.ttfbin79620 -> 117240 bytes
-rw-r--r--data/fonts/Roboto-BoldItalic.ttfbin82880 -> 120668 bytes
-rw-r--r--data/fonts/Roboto-Italic.ttfbin82580 -> 101180 bytes
-rw-r--r--data/fonts/Roboto-Light.ttfbin108216 -> 115200 bytes
-rw-r--r--data/fonts/Roboto-LightItalic.ttfbin111440 -> 118728 bytes
-rw-r--r--data/fonts/Roboto-Regular.ttfbin79396 -> 114976 bytes
-rw-r--r--data/fonts/Roboto-Thin.ttfbin113996 -> 115632 bytes
-rw-r--r--data/fonts/Roboto-ThinItalic.ttfbin117512 -> 119376 bytes
-rw-r--r--data/fonts/RobotoCondensed-Bold.ttfbin105684 -> 115036 bytes
-rw-r--r--data/fonts/RobotoCondensed-BoldItalic.ttfbin109028 -> 118580 bytes
-rw-r--r--data/fonts/RobotoCondensed-Italic.ttfbin108428 -> 118064 bytes
-rw-r--r--data/fonts/RobotoCondensed-Regular.ttfbin105140 -> 114568 bytes
-rw-r--r--docs/html/_redirects.yaml10
-rw-r--r--docs/html/about/versions/android-4.0.jd54
-rw-r--r--docs/html/design/building-blocks/buttons.jd2
-rw-r--r--docs/html/design/building-blocks/grid-lists.jd2
-rw-r--r--docs/html/design/building-blocks/progress.jd2
-rw-r--r--docs/html/design/building-blocks/spinners.jd2
-rw-r--r--docs/html/design/building-blocks/switches.jd2
-rw-r--r--docs/html/design/building-blocks/tabs.jd2
-rw-r--r--docs/html/design/building-blocks/text-fields.jd2
-rw-r--r--docs/html/design/patterns/accessibility.jd2
-rw-r--r--docs/html/design/patterns/app-structure.jd2
-rw-r--r--docs/html/design/patterns/compatibility.jd1
-rw-r--r--docs/html/design/patterns/confirming-acknowledging.jd2
-rw-r--r--docs/html/design/patterns/gestures.jd2
-rw-r--r--docs/html/design/patterns/help.jd1
-rw-r--r--docs/html/design/patterns/navigation.jd2
-rw-r--r--docs/html/design/patterns/selection.jd2
-rw-r--r--docs/html/design/patterns/settings.jd2
-rw-r--r--docs/html/design/patterns/swipe-views.jd2
-rw-r--r--docs/html/design/patterns/widgets.jd2
-rw-r--r--docs/html/design/style/iconography.jd1
-rw-r--r--docs/html/design/style/metrics-grids.jd1
-rw-r--r--docs/html/design/style/touch-feedback.jd1
-rw-r--r--docs/html/design/style/typography.jd1
-rw-r--r--docs/html/design/style/writing.jd1
-rw-r--r--docs/html/distribute/distribute_toc.cs123
-rw-r--r--docs/html/distribute/googleplay/policies/ads.jd352
-rw-r--r--docs/html/distribute/googleplay/policies/index.jd59
-rw-r--r--docs/html/distribute/googleplay/policies/ip.jd345
-rw-r--r--docs/html/distribute/googleplay/policies/spam.jd421
-rw-r--r--docs/html/google/play/billing/v2/api.jd1
-rw-r--r--docs/html/google/play/billing/v2/billing_integrate.jd1
-rw-r--r--docs/html/google/play/billing/v2/billing_reference.jd1
-rw-r--r--docs/html/google/play/billing/v2/billing_subscriptions.jd1
-rw-r--r--docs/html/guide/basics/appmodel.jd261
-rw-r--r--docs/html/guide/basics/building-blocks.jd76
-rw-r--r--docs/html/guide/basics/fixme-gs-core-packages.jd92
-rw-r--r--docs/html/guide/basics/index.html8
-rw-r--r--docs/html/guide/components/processes-and-threads.jd2
-rw-r--r--docs/html/guide/topics/admin/device-admin.jd1
-rw-r--r--docs/html/guide/topics/admin/keychain.jd4
-rw-r--r--docs/html/guide/topics/appwidgets/index.jd1
-rw-r--r--docs/html/guide/topics/connectivity/bluetooth.jd1
-rw-r--r--docs/html/guide/topics/connectivity/sip.jd1
-rw-r--r--docs/html/guide/topics/connectivity/wifip2p.jd1
-rw-r--r--docs/html/guide/topics/data/data-storage.jd1
-rw-r--r--docs/html/guide/topics/data/install-location.jd1
-rw-r--r--docs/html/guide/topics/graphics/opengl.jd12
-rw-r--r--docs/html/guide/topics/graphics/prop-animation.jd3
-rw-r--r--docs/html/guide/topics/graphics/renderscript/graphics.jd994
-rw-r--r--docs/html/guide/topics/location/strategies.jd3
-rw-r--r--docs/html/guide/topics/media/audio-capture.jd3
-rw-r--r--docs/html/guide/topics/media/camera.jd3
-rw-r--r--docs/html/guide/topics/media/mediaplayer.jd3
-rw-r--r--docs/html/guide/topics/resources/runtime-changes.jd3
-rw-r--r--docs/html/guide/topics/search/adding-custom-suggestions.jd3
-rw-r--r--docs/html/guide/topics/search/adding-recent-query-suggestions.jd4
-rw-r--r--docs/html/guide/topics/search/search-dialog.jd3
-rw-r--r--docs/html/guide/topics/search/searchable-config.jd3
-rw-r--r--docs/html/guide/topics/security/index.jd65
-rw-r--r--docs/html/guide/topics/security/security.jd767
-rw-r--r--docs/html/guide/topics/sensors/sensors_motion.jd3
-rw-r--r--docs/html/guide/topics/sensors/sensors_position.jd3
-rw-r--r--docs/html/guide/topics/text/copy-paste.jd1
-rw-r--r--docs/html/guide/topics/text/creating-input-method.jd2
-rw-r--r--docs/html/guide/topics/text/spell-checker-framework.jd2
-rw-r--r--docs/html/guide/topics/ui/controls/button.jd3
-rw-r--r--docs/html/guide/topics/ui/controls/checkbox.jd3
-rw-r--r--docs/html/guide/topics/ui/controls/pickers.jd5
-rw-r--r--docs/html/guide/topics/ui/controls/radiobutton.jd3
-rw-r--r--docs/html/guide/topics/ui/controls/spinner.jd5
-rw-r--r--docs/html/guide/topics/ui/controls/text.jd3
-rw-r--r--docs/html/guide/topics/ui/controls/togglebutton.jd3
-rw-r--r--docs/html/guide/topics/ui/custom-components.jd3
-rw-r--r--docs/html/guide/topics/ui/declaring-layout.jd3
-rw-r--r--docs/html/guide/topics/ui/dialogs.jd2
-rw-r--r--docs/html/guide/topics/ui/drag-drop.jd3
-rw-r--r--docs/html/guide/topics/ui/layout-objects.jd6
-rw-r--r--docs/html/guide/topics/ui/layout/gridview.jd3
-rw-r--r--docs/html/guide/topics/ui/layout/linear.jd3
-rw-r--r--docs/html/guide/topics/ui/layout/listview.jd3
-rw-r--r--docs/html/guide/topics/ui/layout/relative.jd3
-rw-r--r--docs/html/guide/topics/ui/notifiers/index.jd92
-rw-r--r--docs/html/guide/topics/ui/settings.jd2
-rw-r--r--docs/html/images/example-bad.pngbin0 -> 501 bytes
-rw-r--r--docs/html/images/example-good.pngbin0 -> 834 bytes
-rw-r--r--docs/html/images/gp-policy-ads-eula-violation.pngbin0 -> 111656 bytes
-rw-r--r--docs/html/images/gp-policy-ads-eula.pngbin0 -> 163101 bytes
-rw-r--r--docs/html/images/gp-policy-ads-impersonate-violation.pngbin0 -> 65883 bytes
-rw-r--r--docs/html/images/gp-policy-ads-maturity-violation.pngbin0 -> 81774 bytes
-rw-r--r--docs/html/images/gp-policy-ads-notif-attr-violation.pngbin0 -> 18974 bytes
-rw-r--r--docs/html/images/gp-policy-ads-notif-attr.pngbin0 -> 15726 bytes
-rw-r--r--docs/html/images/gp-policy-ads-paywall-violation.pngbin0 -> 129229 bytes
-rw-r--r--docs/html/images/gp-policy-ads-paywall.pngbin0 -> 121501 bytes
-rw-r--r--docs/html/images/gp-policy-ads-terms.pngbin0 -> 161466 bytes
-rw-r--r--docs/html/images/gp-policy-ip-copyright-violation.pngbin0 -> 178784 bytes
-rw-r--r--docs/html/images/gp-policy-ip-impersonation-violation.pngbin0 -> 117027 bytes
-rw-r--r--docs/html/images/gp-policy-ip-trademark-violation.pngbin0 -> 117292 bytes
-rw-r--r--docs/html/images/gp-policy-spam-negreview.pngbin0 -> 43371 bytes
-rw-r--r--docs/html/images/gp-policy-spam-reqrating.pngbin0 -> 50100 bytes
-rw-r--r--docs/html/sdk/1.0_r1/index.jd1
-rw-r--r--docs/html/sdk/1.0_r1/upgrading.jd1
-rw-r--r--docs/html/sdk/1.0_r2/index.jd1
-rw-r--r--docs/html/sdk/1.0_r2/upgrading.jd1
-rw-r--r--docs/html/sdk/1.1_r1/index.jd1
-rw-r--r--docs/html/sdk/1.1_r1/upgrading.jd1
-rw-r--r--docs/html/sdk/1.5_r1/index.jd1
-rw-r--r--docs/html/sdk/1.5_r1/upgrading.jd1
-rw-r--r--docs/html/sdk/1.5_r2/index.jd1
-rw-r--r--docs/html/sdk/1.5_r2/upgrading.jd1
-rw-r--r--docs/html/sdk/1.5_r3/index.jd1
-rw-r--r--docs/html/sdk/1.5_r3/upgrading.jd1
-rw-r--r--docs/html/sdk/1.6_r1/index.jd1
-rw-r--r--docs/html/sdk/1.6_r1/upgrading.jd1
-rw-r--r--docs/html/sdk/installing/next.jd51
-rw-r--r--docs/html/tools/debugging/debugging-ui.jd4
-rw-r--r--docs/html/tools/extras/oem-usb.jd7
-rw-r--r--docs/html/tools/sdk/OLD_RELEASENOTES.jd1
-rw-r--r--docs/html/tools/sdk/RELEASENOTES.jd1
-rw-r--r--docs/html/tools/sdk/addons.jd9
-rw-r--r--docs/html/tools/sdk/adt-notes.jd5
-rw-r--r--docs/html/tools/sdk/adt_download.html10
-rw-r--r--docs/html/tools/sdk/libraries.jd9
-rw-r--r--docs/html/tools/sdk/ndk/1.5_r1/index.jd1
-rw-r--r--docs/html/tools/sdk/ndk/1.6_r1/index.jd1
-rw-r--r--docs/html/tools/sdk/ndk/overview.jd588
-rw-r--r--docs/html/tools/sdk/older_releases.jd1
-rw-r--r--docs/html/tools/sdk/platforms.jd9
-rw-r--r--docs/html/tools/sdk/tools-notes.jd1
-rw-r--r--docs/html/tools/sdk/usb-drivers.jd9
-rw-r--r--docs/html/training/accessibility/index.jd3
-rw-r--r--docs/html/training/articles/perf-anr.jd2
-rw-r--r--docs/html/training/articles/perf-jni.jd2
-rw-r--r--docs/html/training/articles/security-ssl.jd2
-rw-r--r--docs/html/training/articles/smp.jd2
-rw-r--r--docs/html/training/backward-compatible-ui/index.jd3
-rw-r--r--docs/html/training/basics/firstapp/building-ui.jd2
-rw-r--r--docs/html/training/basics/location/index.jd3
-rw-r--r--docs/html/training/custom-views/index.jd3
-rw-r--r--docs/html/training/design-navigation/index.jd2
-rw-r--r--docs/html/training/enterprise/index.jd1
-rw-r--r--docs/html/training/gestures/index.jd4
-rw-r--r--docs/html/training/id-auth/index.jd3
-rw-r--r--docs/html/training/implementing-navigation/index.jd3
-rw-r--r--docs/html/training/improving-layouts/index.jd1
-rw-r--r--docs/html/training/in-app-billing/index.jd1
-rw-r--r--docs/html/training/keyboard-input/index.jd1
-rw-r--r--docs/html/training/load-data-background/index.jd2
-rw-r--r--docs/html/training/monitoring-device-state/index.jd3
-rw-r--r--docs/html/training/multiple-apks/index.jd3
-rw-r--r--docs/html/training/multiple-threads/index.jd1
-rw-r--r--docs/html/training/multiscreen/index.jd3
-rw-r--r--docs/html/training/notify-user/index.jd4
-rw-r--r--docs/html/training/run-background-service/index.jd4
-rw-r--r--docs/html/training/search/index.jd4
-rw-r--r--docs/html/training/tv/index.jd3
-rw-r--r--drm/jni/Android.mk2
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java26
-rw-r--r--graphics/java/android/graphics/drawable/DrawableContainer.java2
-rw-r--r--graphics/java/android/renderscript/Allocation.java256
-rw-r--r--graphics/java/android/renderscript/FieldPacker.java247
-rw-r--r--graphics/java/android/renderscript/FileA3D.java1
-rw-r--r--graphics/java/android/renderscript/Font.java5
-rw-r--r--graphics/java/android/renderscript/Mesh.java1
-rw-r--r--graphics/java/android/renderscript/Program.java1
-rw-r--r--graphics/java/android/renderscript/ProgramFragment.java1
-rw-r--r--graphics/java/android/renderscript/ProgramFragmentFixedFunction.java1
-rw-r--r--graphics/java/android/renderscript/ProgramRaster.java1
-rw-r--r--graphics/java/android/renderscript/ProgramStore.java1
-rw-r--r--graphics/java/android/renderscript/ProgramVertex.java21
-rw-r--r--graphics/java/android/renderscript/ProgramVertexFixedFunction.java1
-rw-r--r--graphics/java/android/renderscript/RSSurfaceView.java1
-rw-r--r--graphics/java/android/renderscript/RSTextureView.java1
-rw-r--r--graphics/java/android/renderscript/RenderScript.java115
-rw-r--r--graphics/java/android/renderscript/RenderScriptGL.java1
-rw-r--r--graphics/java/android/renderscript/Script.java21
-rw-r--r--graphics/java/android/renderscript/ScriptC.java14
-rw-r--r--graphics/java/android/renderscript/ScriptIntrinsic3DLUT.java6
-rw-r--r--graphics/java/android/renderscript/Type.java6
-rw-r--r--graphics/java/android/renderscript/package.html80
-rw-r--r--graphics/jni/Android.mk1
-rw-r--r--graphics/jni/android_renderscript_RenderScript.cpp130
-rw-r--r--include/androidfw/InputDevice.h3
-rw-r--r--include/androidfw/InputTransport.h3
-rw-r--r--keystore/java/android/security/AndroidKeyPairGenerator.java17
-rw-r--r--keystore/java/android/security/AndroidKeyPairGeneratorSpec.java103
-rw-r--r--keystore/java/android/security/AndroidKeyStore.java57
-rw-r--r--keystore/java/android/security/AndroidKeyStoreParameter.java123
-rw-r--r--keystore/java/android/security/AndroidKeyStoreProvider.java3
-rw-r--r--keystore/java/android/security/KeyStore.java39
-rw-r--r--keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java22
-rw-r--r--keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java150
-rw-r--r--keystore/tests/src/android/security/AndroidKeyStoreTest.java538
-rw-r--r--keystore/tests/src/android/security/KeyStoreTest.java123
-rw-r--r--libs/androidfw/AssetManager.cpp14
-rw-r--r--libs/androidfw/InputDevice.cpp4
-rw-r--r--libs/androidfw/InputTransport.cpp5
-rw-r--r--libs/hwui/Android.mk3
-rw-r--r--libs/hwui/Caches.cpp23
-rw-r--r--libs/hwui/Caches.h11
-rw-r--r--libs/hwui/Debug.h3
-rw-r--r--libs/hwui/DeferredDisplayList.cpp262
-rw-r--r--libs/hwui/DeferredDisplayList.h32
-rw-r--r--libs/hwui/DisplayList.cpp2
-rw-r--r--libs/hwui/DisplayListOp.h249
-rw-r--r--libs/hwui/DisplayListRenderer.cpp13
-rw-r--r--libs/hwui/DisplayListRenderer.h5
-rw-r--r--libs/hwui/Dither.cpp50
-rw-r--r--libs/hwui/Dither.h12
-rw-r--r--libs/hwui/FontRenderer.cpp104
-rw-r--r--libs/hwui/FontRenderer.h18
-rw-r--r--libs/hwui/GammaFontRenderer.cpp20
-rw-r--r--libs/hwui/GammaFontRenderer.h8
-rw-r--r--libs/hwui/GradientCache.cpp147
-rw-r--r--libs/hwui/GradientCache.h25
-rw-r--r--libs/hwui/Layer.cpp5
-rw-r--r--libs/hwui/Layer.h7
-rw-r--r--libs/hwui/LayerRenderer.cpp2
-rw-r--r--libs/hwui/OpenGLRenderer.cpp115
-rw-r--r--libs/hwui/OpenGLRenderer.h59
-rw-r--r--libs/hwui/PixelBuffer.cpp163
-rw-r--r--libs/hwui/PixelBuffer.h180
-rw-r--r--libs/hwui/ProgramCache.cpp88
-rw-r--r--libs/hwui/ProgramCache.h2
-rw-r--r--libs/hwui/Properties.h50
-rw-r--r--libs/hwui/TextDropShadowCache.cpp4
-rw-r--r--libs/hwui/font/CacheTexture.cpp53
-rw-r--r--libs/hwui/font/CacheTexture.h33
-rw-r--r--libs/hwui/font/Font.cpp37
-rw-r--r--libs/hwui/utils/TinyHashMap.h72
-rw-r--r--location/java/android/location/IGeofenceProvider.aidl28
-rw-r--r--location/java/android/location/IGpsGeofenceHardware.aidl33
-rw-r--r--location/lib/java/com/android/location/provider/GeofenceProvider.java67
-rw-r--r--media/java/android/media/AudioManager.java4
-rw-r--r--media/java/android/media/AudioService.java107
-rw-r--r--media/java/android/media/AudioTrack.java2
-rw-r--r--media/java/android/media/IAudioService.aidl2
-rw-r--r--media/java/android/media/MediaCodec.java22
-rw-r--r--media/java/android/media/MediaDrm.java60
-rw-r--r--media/java/android/media/MediaExtractor.java48
-rw-r--r--media/java/android/media/MediaMuxer.java6
-rw-r--r--media/java/android/media/MediaPlayer.java3
-rw-r--r--media/java/android/media/MediaRouter.java17
-rw-r--r--media/java/android/media/RemoteControlClient.java38
-rw-r--r--media/jni/Android.mk1
-rw-r--r--media/jni/android_media_MediaCodec.cpp6
-rw-r--r--media/jni/android_media_MediaDrm.cpp145
-rw-r--r--media/jni/android_media_MediaDrm.h19
-rw-r--r--media/jni/android_media_MediaExtractor.cpp35
-rw-r--r--media/jni/android_media_MediaExtractor.h2
-rw-r--r--media/jni/audioeffect/Android.mk1
-rw-r--r--media/jni/mediaeditor/Android.mk1
-rw-r--r--media/jni/soundpool/Android.mk1
-rw-r--r--media/libdrm/mobile1/Android.mk6
-rw-r--r--media/mca/filterfw/Android.mk3
-rw-r--r--media/mca/filterpacks/Android.mk4
-rw-r--r--media/tests/omxjpegdecoder/Android.mk1
-rw-r--r--media/tests/players/Android.mk3
-rw-r--r--native/android/Android.mk1
-rw-r--r--opengl/java/android/opengl/GLSurfaceView.java1038
-rw-r--r--packages/DefaultContainerService/jni/Android.mk5
-rw-r--r--packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/UniverseBackground.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/FirstFrameAnimatorHelper.java129
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java2
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindowManager.java34
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java12
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java70
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java16
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java7
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java11
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/PagedView.java2
-rw-r--r--services/common_time/Android.mk3
-rw-r--r--services/input/Android.mk1
-rw-r--r--services/input/EventHub.cpp49
-rw-r--r--services/input/EventHub.h17
-rw-r--r--services/input/InputReader.cpp123
-rw-r--r--services/input/InputReader.h32
-rw-r--r--services/input/tests/Android.mk1
-rw-r--r--services/java/com/android/server/ConnectivityService.java94
-rw-r--r--services/java/com/android/server/EventLogTags.logtags5
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java25
-rw-r--r--services/java/com/android/server/LocationManagerService.java66
-rw-r--r--services/java/com/android/server/NotificationManagerService.java368
-rw-r--r--services/java/com/android/server/StatusBarManagerService.java2
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityInputFilter.java59
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java318
-rw-r--r--services/java/com/android/server/accessibility/EventStreamTransformation.java2
-rw-r--r--services/java/com/android/server/accounts/AccountManagerService.java9
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java54
-rw-r--r--services/java/com/android/server/am/ActivityStack.java10
-rw-r--r--services/java/com/android/server/am/NativeCrashListener.java2
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java62
-rw-r--r--services/java/com/android/server/content/ContentService.java2
-rw-r--r--services/java/com/android/server/content/SyncManager.java38
-rw-r--r--services/java/com/android/server/firewall/AndFilter.java7
-rw-r--r--services/java/com/android/server/firewall/CategoryFilter.java2
-rw-r--r--services/java/com/android/server/firewall/Filter.java11
-rw-r--r--services/java/com/android/server/firewall/IntentFirewall.java116
-rw-r--r--services/java/com/android/server/firewall/NotFilter.java7
-rw-r--r--services/java/com/android/server/firewall/OrFilter.java7
-rw-r--r--services/java/com/android/server/firewall/PortFilter.java3
-rw-r--r--services/java/com/android/server/firewall/SenderFilter.java12
-rw-r--r--services/java/com/android/server/firewall/SenderPermissionFilter.java3
-rw-r--r--services/java/com/android/server/firewall/StringFilter.java57
-rw-r--r--services/java/com/android/server/location/GeofenceProxy.java154
-rw-r--r--services/java/com/android/server/location/GpsLocationProvider.java111
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java141
-rw-r--r--services/java/com/android/server/pm/UserManagerService.java11
-rw-r--r--services/java/com/android/server/power/PowerManagerService.java2
-rw-r--r--services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java12
-rw-r--r--services/java/com/android/server/updates/IntentFirewallInstallReceiver.java27
-rw-r--r--services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java113
-rw-r--r--services/java/com/android/server/wifi/WifiService.java2
-rw-r--r--services/java/com/android/server/wm/AppWindowAnimator.java10
-rw-r--r--services/java/com/android/server/wm/WindowAnimator.java1
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java83
-rw-r--r--services/java/com/android/server/wm/WindowState.java12
-rw-r--r--services/java/com/android/server/wm/WindowStateAnimator.java17
-rw-r--r--services/jni/Android.mk1
-rw-r--r--services/jni/com_android_server_location_GpsLocationProvider.cpp179
-rw-r--r--telephony/java/android/telephony/CellIdentityCdma.java4
-rw-r--r--telephony/java/android/telephony/CellIdentityGsm.java6
-rw-r--r--telephony/java/android/telephony/CellIdentityLte.java4
-rw-r--r--telephony/java/android/telephony/CellInfo.java2
-rw-r--r--telephony/java/android/telephony/CellInfoCdma.java7
-rw-r--r--telephony/java/android/telephony/CellInfoGsm.java7
-rw-r--r--telephony/java/android/telephony/CellInfoLte.java7
-rw-r--r--telephony/java/android/telephony/SignalStrength.java27
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java23
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl5
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java3
-rw-r--r--tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java4
-rw-r--r--tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java2
-rw-r--r--tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java2
-rw-r--r--tests/RenderScriptTests/FBOTest/Android.mk2
-rw-r--r--tests/RenderScriptTests/Fountain/Android.mk3
-rw-r--r--tests/RenderScriptTests/HelloWorld/Android.mk2
-rw-r--r--tests/RenderScriptTests/MiscSamples/Android.mk2
-rw-r--r--tests/RenderScriptTests/ModelViewer/Android.mk2
-rw-r--r--tests/RenderScriptTests/PerfTest/Android.mk2
-rw-r--r--tests/RenderScriptTests/SceneGraph/Android.mk2
-rw-r--r--tests/RenderScriptTests/ShadersTest/Android.mk2
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java28
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java3
-rw-r--r--tools/aapt/Android.mk4
-rw-r--r--tools/aapt/Command.cpp8
-rw-r--r--tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java2
-rw-r--r--tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java29
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java2
-rw-r--r--tools/obbtool/Android.mk3
-rw-r--r--tools/validatekeymaps/Android.mk3
-rw-r--r--wifi/java/android/net/wifi/WifiConfigStore.java30
-rw-r--r--wifi/java/android/net/wifi/WifiEnterpriseConfig.java14
-rw-r--r--wifi/java/android/net/wifi/WifiStateMachine.java52
588 files changed, 15117 insertions, 8815 deletions
diff --git a/Android.mk b/Android.mk
index 2ad7a72..8115472 100644
--- a/Android.mk
+++ b/Android.mk
@@ -69,7 +69,6 @@ LOCAL_SRC_FILES += \
core/java/android/app/IAlarmManager.aidl \
core/java/android/app/IBackupAgent.aidl \
core/java/android/app/IInstrumentationWatcher.aidl \
- core/java/android/app/INotificationListener.aidl \
core/java/android/app/INotificationManager.aidl \
core/java/android/app/IProcessObserver.aidl \
core/java/android/app/ISearchManager.aidl \
@@ -125,6 +124,8 @@ LOCAL_SRC_FILES += \
core/java/android/hardware/display/IDisplayManagerCallback.aidl \
core/java/android/hardware/input/IInputManager.aidl \
core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
+ core/java/android/hardware/location/IGeofenceHardware.aidl \
+ core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \
core/java/android/hardware/usb/IUsbManager.aidl \
core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/INetworkManagementEventObserver.aidl \
@@ -148,6 +149,7 @@ LOCAL_SRC_FILES += \
core/java/android/os/IUpdateLock.aidl \
core/java/android/os/IUserManager.aidl \
core/java/android/os/IVibratorService.aidl \
+ core/java/android/service/notification/INotificationListener.aidl \
core/java/android/service/dreams/IDreamManager.aidl \
core/java/android/service/dreams/IDreamService.aidl \
core/java/android/service/wallpaper/IWallpaperConnection.aidl \
@@ -207,10 +209,12 @@ LOCAL_SRC_FILES += \
location/java/android/location/ICountryDetector.aidl \
location/java/android/location/ICountryListener.aidl \
location/java/android/location/IGeocodeProvider.aidl \
+ location/java/android/location/IGeofenceProvider.aidl \
location/java/android/location/IGpsStatusListener.aidl \
location/java/android/location/IGpsStatusProvider.aidl \
location/java/android/location/ILocationListener.aidl \
location/java/android/location/ILocationManager.aidl \
+ location/java/android/location/IGpsGeofenceHardware.aidl \
location/java/android/location/INetInitiatedListener.aidl \
location/java/com/android/internal/location/ILocationProvider.aidl \
media/java/android/media/IAudioService.aidl \
@@ -438,6 +442,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \
-since $(SRC_API_DIR)/15.txt 15 \
-since $(SRC_API_DIR)/16.txt 16 \
-since $(SRC_API_DIR)/17.txt 17 \
+ -since $(SRC_API_DIR)/18.txt 18 \
-werror -hide 113 \
-overview $(LOCAL_PATH)/core/java/overview.html
diff --git a/CleanSpec.mk b/CleanSpec.mk
index fc63866..4debdc2 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -159,6 +159,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodCallback.*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodSession.*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodCallback.*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/api/current.txt b/api/current.txt
index 653e25a..d1be295 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22,6 +22,7 @@ package android {
field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
+ field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
@@ -68,6 +69,7 @@ package android {
field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
field public static final java.lang.String INTERNET = "android.permission.INTERNET";
field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
+ field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS";
field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
@@ -850,6 +852,7 @@ package android {
field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
field public static final int required = 16843406; // 0x101028e
+ field public static final int requiredAccountType = 16843734; // 0x10103d6
field public static final int requiredForAllUsers = 16843728; // 0x10103d0
field public static final int requiresFadingEdge = 16843685; // 0x10103a5
field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
@@ -2073,6 +2076,7 @@ package android.accessibilityservice {
method public final android.os.IBinder onBind(android.content.Intent);
method protected boolean onGesture(int);
method public abstract void onInterrupt();
+ method protected boolean onKeyEvent(android.view.KeyEvent);
method protected void onServiceConnected();
method public final boolean performGlobalAction(int);
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
@@ -2302,22 +2306,28 @@ package android.accounts {
package android.animation {
- public abstract class Animator implements java.lang.Cloneable {
+ public abstract interface Animatable {
+ method public abstract long getDuration();
+ method public abstract android.animation.TimeInterpolator getInterpolator();
+ method public abstract long getStartDelay();
+ method public abstract android.animation.Animatable setDuration(long);
+ method public abstract void setInterpolator(android.animation.TimeInterpolator);
+ method public abstract void setStartDelay(long);
+ }
+
+ public abstract class Animator implements android.animation.Animatable java.lang.Cloneable {
ctor public Animator();
method public void addListener(android.animation.Animator.AnimatorListener);
method public void cancel();
method public android.animation.Animator clone();
method public void end();
- method public abstract long getDuration();
+ method public android.animation.TimeInterpolator getInterpolator();
method public java.util.ArrayList<android.animation.Animator.AnimatorListener> getListeners();
- method public abstract long getStartDelay();
method public abstract boolean isRunning();
method public boolean isStarted();
method public void removeAllListeners();
method public void removeListener(android.animation.Animator.AnimatorListener);
method public abstract android.animation.Animator setDuration(long);
- method public abstract void setInterpolator(android.animation.TimeInterpolator);
- method public abstract void setStartDelay(long);
method public void setTarget(java.lang.Object);
method public void setupEndValues();
method public void setupStartValues();
@@ -2508,7 +2518,6 @@ package android.animation {
method public long getCurrentPlayTime();
method public long getDuration();
method public static long getFrameDelay();
- method public android.animation.TimeInterpolator getInterpolator();
method public int getRepeatCount();
method public int getRepeatMode();
method public long getStartDelay();
@@ -2594,6 +2603,10 @@ package android.app {
method public abstract void setDisplayShowHomeEnabled(boolean);
method public abstract void setDisplayShowTitleEnabled(boolean);
method public abstract void setDisplayUseLogoEnabled(boolean);
+ method public void setHomeActionContentDescription(java.lang.CharSequence);
+ method public void setHomeActionContentDescription(int);
+ method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+ method public void setHomeAsUpIndicator(int);
method public void setHomeButtonEnabled(boolean);
method public abstract void setIcon(int);
method public abstract void setIcon(android.graphics.drawable.Drawable);
@@ -4183,6 +4196,7 @@ package android.app {
method public final boolean performGlobalAction(int);
method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
method public boolean setRotation(int);
+ method public void setRunAsMonkey(boolean);
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
method public android.graphics.Bitmap takeScreenshot();
method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException;
@@ -5012,6 +5026,7 @@ package android.bluetooth {
method public boolean addService(android.bluetooth.BluetoothGattService);
method public void cancelConnection(android.bluetooth.BluetoothDevice);
method public void clearServices();
+ method public void close();
method public boolean connect(android.bluetooth.BluetoothDevice, boolean);
method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method public int getConnectionState(android.bluetooth.BluetoothDevice);
@@ -6643,7 +6658,9 @@ package android.content.pm {
field public static final int LAUNCH_SINGLE_TOP = 1; // 0x1
field public static final int SCREEN_ORIENTATION_BEHIND = 3; // 0x3
field public static final int SCREEN_ORIENTATION_FULL_SENSOR = 10; // 0xa
+ field public static final int SCREEN_ORIENTATION_FULL_USER = 13; // 0xd
field public static final int SCREEN_ORIENTATION_LANDSCAPE = 0; // 0x0
+ field public static final int SCREEN_ORIENTATION_LOCKED = 14; // 0xe
field public static final int SCREEN_ORIENTATION_NOSENSOR = 5; // 0x5
field public static final int SCREEN_ORIENTATION_PORTRAIT = 1; // 0x1
field public static final int SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8; // 0x8
@@ -6653,6 +6670,8 @@ package android.content.pm {
field public static final int SCREEN_ORIENTATION_SENSOR_PORTRAIT = 7; // 0x7
field public static final int SCREEN_ORIENTATION_UNSPECIFIED = -1; // 0xffffffff
field public static final int SCREEN_ORIENTATION_USER = 2; // 0x2
+ field public static final int SCREEN_ORIENTATION_USER_LANDSCAPE = 11; // 0xb
+ field public static final int SCREEN_ORIENTATION_USER_PORTRAIT = 12; // 0xc
field public static final int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 1; // 0x1
field public int configChanges;
field public int flags;
@@ -6858,7 +6877,6 @@ package android.content.pm {
method public abstract boolean addPermission(android.content.pm.PermissionInfo);
method public abstract boolean addPermissionAsync(android.content.pm.PermissionInfo);
method public abstract deprecated void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName);
- method public android.content.Intent buildPermissionRequestIntent(java.lang.String...);
method public abstract java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]);
method public abstract int checkPermission(java.lang.String, java.lang.String);
method public abstract int checkSignatures(java.lang.String, java.lang.String);
@@ -10289,16 +10307,20 @@ package android.hardware {
field public static final int TYPE_ACCELEROMETER = 1; // 0x1
field public static final int TYPE_ALL = -1; // 0xffffffff
field public static final int TYPE_AMBIENT_TEMPERATURE = 13; // 0xd
+ field public static final int TYPE_GAME_ROTATION_VECTOR = 15; // 0xf
field public static final int TYPE_GRAVITY = 9; // 0x9
field public static final int TYPE_GYROSCOPE = 4; // 0x4
+ field public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16; // 0x10
field public static final int TYPE_LIGHT = 5; // 0x5
field public static final int TYPE_LINEAR_ACCELERATION = 10; // 0xa
field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
+ field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
field public static final deprecated int TYPE_ORIENTATION = 3; // 0x3
field public static final int TYPE_PRESSURE = 6; // 0x6
field public static final int TYPE_PROXIMITY = 8; // 0x8
field public static final int TYPE_RELATIVE_HUMIDITY = 12; // 0xc
field public static final int TYPE_ROTATION_VECTOR = 11; // 0xb
+ field public static final int TYPE_SIGNIFICANT_MOTION = 17; // 0x11
field public static final deprecated int TYPE_TEMPERATURE = 7; // 0x7
}
@@ -10320,6 +10342,7 @@ package android.hardware {
}
public abstract class SensorManager {
+ method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
method public static float getAltitude(float, float);
method public static void getAngleChange(float[], float[], float[]);
method public android.hardware.Sensor getDefaultSensor(int);
@@ -10335,6 +10358,7 @@ package android.hardware {
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, android.os.Handler);
method public static boolean remapCoordinateSystem(float[], int, int, float[]);
+ method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
method public deprecated void unregisterListener(android.hardware.SensorListener);
method public deprecated void unregisterListener(android.hardware.SensorListener, int);
method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -10398,6 +10422,17 @@ package android.hardware {
field public static final float STANDARD_GRAVITY = 9.80665f;
}
+ public final class TriggerEvent {
+ field public android.hardware.Sensor sensor;
+ field public long timestamp;
+ field public final float[] values;
+ }
+
+ public abstract class TriggerEventListener {
+ ctor public TriggerEventListener();
+ method public abstract void onTrigger(android.hardware.TriggerEvent);
+ }
+
}
package android.hardware.display {
@@ -10438,6 +10473,43 @@ package android.hardware.input {
}
+package android.hardware.location {
+
+ public final class GeofenceHardware {
+ method public boolean addCircularFence(int, double, double, double, int, int, int, int, int, android.hardware.location.GeofenceHardwareCallback);
+ method public int[] getMonitoringTypesAndStatus();
+ method public boolean pauseGeofence(int, int);
+ method public boolean registerForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareCallback);
+ method public boolean removeGeofence(int, int);
+ method public boolean resumeGeofence(int, int, int);
+ method public boolean unregisterForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareCallback);
+ field public static final int GEOFENCE_ENTERED = 1; // 0x1
+ field public static final int GEOFENCE_ERROR_ID_EXISTS = 2; // 0x2
+ field public static final int GEOFENCE_ERROR_ID_UNKNOWN = 3; // 0x3
+ field public static final int GEOFENCE_ERROR_INVALID_TRANSITION = 4; // 0x4
+ field public static final int GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 1; // 0x1
+ field public static final int GEOFENCE_EXITED = 2; // 0x2
+ field public static final int GEOFENCE_FAILURE = 5; // 0x5
+ field public static final int GEOFENCE_SUCCESS = 0; // 0x0
+ field public static final int GEOFENCE_UNCERTAIN = 4; // 0x4
+ field public static final int MONITORING_TYPE_GPS_HARDWARE = 0; // 0x0
+ field public static final int MONITOR_CURRENTLY_AVAILABLE = 0; // 0x0
+ field public static final int MONITOR_CURRENTLY_UNAVAILABLE = 1; // 0x1
+ field public static final int MONITOR_UNSUPPORTED = 2; // 0x2
+ }
+
+ public abstract class GeofenceHardwareCallback {
+ ctor public GeofenceHardwareCallback();
+ method public void onGeofenceAdd(int, int);
+ method public void onGeofenceChange(int, int, android.location.Location, long, int);
+ method public void onGeofencePause(int, int);
+ method public void onGeofenceRemove(int, int);
+ method public void onGeofenceResume(int, int);
+ method public void onMonitoringSystemChange(int, boolean, android.location.Location);
+ }
+
+}
+
package android.hardware.usb {
public class UsbAccessory implements android.os.Parcelable {
@@ -11745,10 +11817,10 @@ package android.media {
method public void seekTo(long, int);
method public void selectTrack(int);
method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
- method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>);
- method public final void setDataSource(java.lang.String);
- method public final void setDataSource(java.io.FileDescriptor);
- method public final void setDataSource(java.io.FileDescriptor, long, long);
+ method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
+ method public final void setDataSource(java.lang.String) throws java.io.IOException;
+ method public final void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
+ method public final void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
method public void unselectTrack(int);
field public static final int SAMPLE_FLAG_ENCRYPTED = 2; // 0x2
field public static final int SAMPLE_FLAG_SYNC = 1; // 0x1
@@ -12195,13 +12267,17 @@ package android.media {
ctor public RemoteControlClient(android.app.PendingIntent);
ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
method public android.media.RemoteControlClient.MetadataEditor editMetadata(boolean);
+ method public void setOnGetPlaybackPositionListener(android.media.RemoteControlClient.OnGetPlaybackPositionListener);
+ method public void setPlaybackPositionUpdateListener(android.media.RemoteControlClient.OnPlaybackPositionUpdateListener);
method public void setPlaybackState(int);
+ method public void setPlaybackState(int, long, float);
method public void setTransportControlFlags(int);
field public static final int FLAG_KEY_MEDIA_FAST_FORWARD = 64; // 0x40
field public static final int FLAG_KEY_MEDIA_NEXT = 128; // 0x80
field public static final int FLAG_KEY_MEDIA_PAUSE = 16; // 0x10
field public static final int FLAG_KEY_MEDIA_PLAY = 4; // 0x4
field public static final int FLAG_KEY_MEDIA_PLAY_PAUSE = 8; // 0x8
+ field public static final int FLAG_KEY_MEDIA_POSITION_UPDATE = 256; // 0x100
field public static final int FLAG_KEY_MEDIA_PREVIOUS = 1; // 0x1
field public static final int FLAG_KEY_MEDIA_REWIND = 2; // 0x2
field public static final int FLAG_KEY_MEDIA_STOP = 32; // 0x20
@@ -12225,6 +12301,14 @@ package android.media {
field public static final int BITMAP_KEY_ARTWORK = 100; // 0x64
}
+ public static abstract interface RemoteControlClient.OnGetPlaybackPositionListener {
+ method public abstract long onGetPlaybackPosition();
+ }
+
+ public static abstract interface RemoteControlClient.OnPlaybackPositionUpdateListener {
+ method public abstract void onPlaybackPositionUpdate(long);
+ }
+
public class Ringtone {
method public int getStreamType();
method public java.lang.String getTitle(android.content.Context);
@@ -16830,7 +16914,6 @@ package android.os {
method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
method public static final deprecated boolean supportsProcesses();
- field public static final int BLUETOOTH_GID = 2000; // 0x7d0
field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710
field public static final int LAST_APPLICATION_UID = 19999; // 0x4e1f
field public static final int PHONE_UID = 1001; // 0x3e9
@@ -16984,6 +17067,11 @@ package android.os {
method public abstract void released();
}
+ public final class Trace {
+ method public static void beginSection(java.lang.String);
+ method public static void endSection();
+ }
+
public class TransactionTooLargeException extends android.os.RemoteException {
ctor public TransactionTooLargeException();
}
@@ -17005,18 +17093,20 @@ package android.os {
method public java.lang.String getUserName();
method public android.os.Bundle getUserRestrictions();
method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
+ method public boolean isLinkedUser();
method public boolean isUserAGoat();
- method public boolean isUserRestricted();
method public boolean isUserRunning(android.os.UserHandle);
method public boolean isUserRunningOrStopping(android.os.UserHandle);
method public void setUserRestriction(java.lang.String, boolean);
method public void setUserRestrictions(android.os.Bundle);
method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
+ field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps";
field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
+ field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user";
field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -18437,6 +18527,7 @@ package android.provider {
}
protected static abstract interface ContactsContract.ContactsColumns {
+ field public static final java.lang.String CONTACT_LAST_UPDATED_TIMESTAMP = "contact_last_updated_timestamp";
field public static final java.lang.String DISPLAY_NAME = "display_name";
field public static final java.lang.String HAS_PHONE_NUMBER = "has_phone_number";
field public static final java.lang.String IN_VISIBLE_GROUP = "in_visible_group";
@@ -18499,10 +18590,16 @@ package android.provider {
protected static abstract interface ContactsContract.DataUsageStatColumns {
field public static final java.lang.String LAST_TIME_USED = "last_time_used";
field public static final java.lang.String TIMES_USED = "times_used";
- field public static final java.lang.String USAGE_TYPE = "usage_type";
- field public static final int USAGE_TYPE_CALL = 0; // 0x0
- field public static final int USAGE_TYPE_LONG_TEXT = 1; // 0x1
- field public static final int USAGE_TYPE_SHORT_TEXT = 2; // 0x2
+ }
+
+ public static final class ContactsContract.DeletedContacts implements android.provider.ContactsContract.DeletedContactsColumns {
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final long DAYS_KEPT_MILLISECONDS = 2592000000L; // 0x9a7ec800L
+ }
+
+ protected static abstract interface ContactsContract.DeletedContactsColumns {
+ field public static final java.lang.String CONTACT_DELETED_TIMESTAMP = "contact_deleted_timestamp";
+ field public static final java.lang.String CONTACT_ID = "contact_id";
}
public static final class ContactsContract.Directory implements android.provider.BaseColumns {
@@ -18584,6 +18681,7 @@ package android.provider {
public static final class ContactsContract.Intents {
ctor public ContactsContract.Intents();
field public static final java.lang.String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE";
+ field public static final java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
field public static final java.lang.String EXTRA_CREATE_DESCRIPTION = "com.android.contacts.action.CREATE_DESCRIPTION";
field public static final java.lang.String EXTRA_FORCE_CREATE = "com.android.contacts.action.FORCE_CREATE";
field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT";
@@ -19648,10 +19746,10 @@ package android.renderscript {
method public int getUsage();
method public void ioReceive();
method public void ioSend();
- method public synchronized void resize(int);
- method public synchronized void resize(int, int);
+ method public deprecated synchronized void resize(int);
method public void setFromFieldPacker(int, android.renderscript.FieldPacker);
method public void setFromFieldPacker(int, int, android.renderscript.FieldPacker);
+ method public void setIoInputNotificationHandler(android.renderscript.Allocation.IoInputNotifier);
method public void setSurface(android.view.Surface);
method public void syncAll(int);
field public static final int USAGE_GRAPHICS_CONSTANTS = 8; // 0x8
@@ -19664,6 +19762,10 @@ package android.renderscript {
field public static final int USAGE_SHARED = 128; // 0x80
}
+ public static abstract interface Allocation.IoInputNotifier {
+ method public abstract void onBufferAvailable(android.renderscript.Allocation);
+ }
+
public static final class Allocation.MipmapControl extends java.lang.Enum {
method public static android.renderscript.Allocation.MipmapControl valueOf(java.lang.String);
method public static final android.renderscript.Allocation.MipmapControl[] values();
@@ -19920,29 +20022,35 @@ package android.renderscript {
method public void reset();
method public void reset(int);
method public void skip(int);
- }
-
- public deprecated class FileA3D extends android.renderscript.BaseObj {
- method public static deprecated android.renderscript.FileA3D createFromAsset(android.renderscript.RenderScript, android.content.res.AssetManager, java.lang.String);
- method public static deprecated android.renderscript.FileA3D createFromFile(android.renderscript.RenderScript, java.lang.String);
- method public static deprecated android.renderscript.FileA3D createFromFile(android.renderscript.RenderScript, java.io.File);
- method public static deprecated android.renderscript.FileA3D createFromResource(android.renderscript.RenderScript, android.content.res.Resources, int);
- method public deprecated android.renderscript.FileA3D.IndexEntry getIndexEntry(int);
- method public deprecated int getIndexEntryCount();
- }
-
- public static final deprecated class FileA3D.EntryType extends java.lang.Enum {
- method public static android.renderscript.FileA3D.EntryType valueOf(java.lang.String);
- method public static final android.renderscript.FileA3D.EntryType[] values();
- enum_constant public static final deprecated android.renderscript.FileA3D.EntryType MESH;
- enum_constant public static final deprecated android.renderscript.FileA3D.EntryType UNKNOWN;
- }
-
- public static deprecated class FileA3D.IndexEntry {
- method public deprecated android.renderscript.FileA3D.EntryType getEntryType();
- method public deprecated android.renderscript.Mesh getMesh();
- method public deprecated java.lang.String getName();
- method public deprecated android.renderscript.BaseObj getObject();
+ method public boolean subBoolean();
+ method public android.renderscript.Byte2 subByte2();
+ method public android.renderscript.Byte3 subByte3();
+ method public android.renderscript.Byte4 subByte4();
+ method public android.renderscript.Double2 subDouble2();
+ method public android.renderscript.Double3 subDouble3();
+ method public android.renderscript.Double4 subDouble4();
+ method public float subF32();
+ method public double subF64();
+ method public android.renderscript.Float2 subFloat2();
+ method public android.renderscript.Float3 subFloat3();
+ method public android.renderscript.Float4 subFloat4();
+ method public short subI16();
+ method public int subI32();
+ method public long subI64();
+ method public byte subI8();
+ method public android.renderscript.Int2 subInt2();
+ method public android.renderscript.Int3 subInt3();
+ method public android.renderscript.Int4 subInt4();
+ method public android.renderscript.Long2 subLong2();
+ method public android.renderscript.Long3 subLong3();
+ method public android.renderscript.Long4 subLong4();
+ method public android.renderscript.Matrix2f subMatrix2f();
+ method public android.renderscript.Matrix3f subMatrix3f();
+ method public android.renderscript.Matrix4f subMatrix4f();
+ method public android.renderscript.Short2 subShort2();
+ method public android.renderscript.Short3 subShort3();
+ method public android.renderscript.Short4 subShort4();
+ method public void subalign(int);
}
public class Float2 {
@@ -19969,23 +20077,6 @@ package android.renderscript {
field public float z;
}
- public deprecated class Font extends android.renderscript.BaseObj {
- method public static deprecated android.renderscript.Font create(android.renderscript.RenderScript, android.content.res.Resources, java.lang.String, android.renderscript.Font.Style, float);
- method public static deprecated android.renderscript.Font createFromAsset(android.renderscript.RenderScript, android.content.res.Resources, java.lang.String, float);
- method public static deprecated android.renderscript.Font createFromFile(android.renderscript.RenderScript, android.content.res.Resources, java.lang.String, float);
- method public static deprecated android.renderscript.Font createFromFile(android.renderscript.RenderScript, android.content.res.Resources, java.io.File, float);
- method public static deprecated android.renderscript.Font createFromResource(android.renderscript.RenderScript, android.content.res.Resources, int, float);
- }
-
- public static final deprecated class Font.Style extends java.lang.Enum {
- method public static android.renderscript.Font.Style valueOf(java.lang.String);
- method public static final android.renderscript.Font.Style[] values();
- enum_constant public static final deprecated android.renderscript.Font.Style BOLD;
- enum_constant public static final deprecated android.renderscript.Font.Style BOLD_ITALIC;
- enum_constant public static final deprecated android.renderscript.Font.Style ITALIC;
- enum_constant public static final deprecated android.renderscript.Font.Style NORMAL;
- }
-
public class Int2 {
ctor public Int2();
ctor public Int2(int, int);
@@ -20100,245 +20191,6 @@ package android.renderscript {
method public void transpose();
}
- public deprecated class Mesh extends android.renderscript.BaseObj {
- method public deprecated android.renderscript.Allocation getIndexSetAllocation(int);
- method public deprecated android.renderscript.Mesh.Primitive getPrimitive(int);
- method public deprecated int getPrimitiveCount();
- method public deprecated android.renderscript.Allocation getVertexAllocation(int);
- method public deprecated int getVertexAllocationCount();
- }
-
- public static deprecated class Mesh.AllocationBuilder {
- ctor public deprecated Mesh.AllocationBuilder(android.renderscript.RenderScript);
- method public deprecated android.renderscript.Mesh.AllocationBuilder addIndexSetAllocation(android.renderscript.Allocation, android.renderscript.Mesh.Primitive);
- method public deprecated android.renderscript.Mesh.AllocationBuilder addIndexSetType(android.renderscript.Mesh.Primitive);
- method public deprecated android.renderscript.Mesh.AllocationBuilder addVertexAllocation(android.renderscript.Allocation) throws java.lang.IllegalStateException;
- method public deprecated android.renderscript.Mesh create();
- method public deprecated int getCurrentIndexSetIndex();
- method public deprecated int getCurrentVertexTypeIndex();
- }
-
- public static deprecated class Mesh.Builder {
- ctor public deprecated Mesh.Builder(android.renderscript.RenderScript, int);
- method public deprecated android.renderscript.Mesh.Builder addIndexSetType(android.renderscript.Type, android.renderscript.Mesh.Primitive);
- method public deprecated android.renderscript.Mesh.Builder addIndexSetType(android.renderscript.Mesh.Primitive);
- method public deprecated android.renderscript.Mesh.Builder addIndexSetType(android.renderscript.Element, int, android.renderscript.Mesh.Primitive);
- method public deprecated android.renderscript.Mesh.Builder addVertexType(android.renderscript.Type) throws java.lang.IllegalStateException;
- method public deprecated android.renderscript.Mesh.Builder addVertexType(android.renderscript.Element, int) throws java.lang.IllegalStateException;
- method public deprecated android.renderscript.Mesh create();
- method public deprecated int getCurrentIndexSetIndex();
- method public deprecated int getCurrentVertexTypeIndex();
- }
-
- public static final deprecated class Mesh.Primitive extends java.lang.Enum {
- method public static android.renderscript.Mesh.Primitive valueOf(java.lang.String);
- method public static final android.renderscript.Mesh.Primitive[] values();
- enum_constant public static final deprecated android.renderscript.Mesh.Primitive LINE;
- enum_constant public static final deprecated android.renderscript.Mesh.Primitive LINE_STRIP;
- enum_constant public static final deprecated android.renderscript.Mesh.Primitive POINT;
- enum_constant public static final deprecated android.renderscript.Mesh.Primitive TRIANGLE;
- enum_constant public static final deprecated android.renderscript.Mesh.Primitive TRIANGLE_FAN;
- enum_constant public static final deprecated android.renderscript.Mesh.Primitive TRIANGLE_STRIP;
- }
-
- public static deprecated class Mesh.TriangleMeshBuilder {
- ctor public deprecated Mesh.TriangleMeshBuilder(android.renderscript.RenderScript, int, int);
- method public deprecated android.renderscript.Mesh.TriangleMeshBuilder addTriangle(int, int, int);
- method public deprecated android.renderscript.Mesh.TriangleMeshBuilder addVertex(float, float);
- method public deprecated android.renderscript.Mesh.TriangleMeshBuilder addVertex(float, float, float);
- method public deprecated android.renderscript.Mesh create(boolean);
- method public deprecated android.renderscript.Mesh.TriangleMeshBuilder setColor(float, float, float, float);
- method public deprecated android.renderscript.Mesh.TriangleMeshBuilder setNormal(float, float, float);
- method public deprecated android.renderscript.Mesh.TriangleMeshBuilder setTexture(float, float);
- field public static final deprecated int COLOR = 1; // 0x1
- field public static final deprecated int NORMAL = 2; // 0x2
- field public static final deprecated int TEXTURE_0 = 256; // 0x100
- }
-
- public class Program extends android.renderscript.BaseObj {
- method public void bindConstants(android.renderscript.Allocation, int);
- method public void bindSampler(android.renderscript.Sampler, int) throws java.lang.IllegalArgumentException;
- method public void bindTexture(android.renderscript.Allocation, int) throws java.lang.IllegalArgumentException;
- method public android.renderscript.Type getConstant(int);
- method public int getConstantCount();
- method public int getTextureCount();
- method public java.lang.String getTextureName(int);
- method public android.renderscript.Program.TextureType getTextureType(int);
- }
-
- public static class Program.BaseProgramBuilder {
- ctor protected Program.BaseProgramBuilder(android.renderscript.RenderScript);
- method public android.renderscript.Program.BaseProgramBuilder addConstant(android.renderscript.Type) throws java.lang.IllegalStateException;
- method public android.renderscript.Program.BaseProgramBuilder addTexture(android.renderscript.Program.TextureType) throws java.lang.IllegalArgumentException;
- method public android.renderscript.Program.BaseProgramBuilder addTexture(android.renderscript.Program.TextureType, java.lang.String) throws java.lang.IllegalArgumentException;
- method public int getCurrentConstantIndex();
- method public int getCurrentTextureIndex();
- method protected void initProgram(android.renderscript.Program);
- method public android.renderscript.Program.BaseProgramBuilder setShader(java.lang.String);
- method public android.renderscript.Program.BaseProgramBuilder setShader(android.content.res.Resources, int);
- }
-
- public static final class Program.TextureType extends java.lang.Enum {
- method public static android.renderscript.Program.TextureType valueOf(java.lang.String);
- method public static final android.renderscript.Program.TextureType[] values();
- enum_constant public static final android.renderscript.Program.TextureType TEXTURE_2D;
- enum_constant public static final android.renderscript.Program.TextureType TEXTURE_CUBE;
- }
-
- public deprecated class ProgramFragment extends android.renderscript.Program {
- }
-
- public static deprecated class ProgramFragment.Builder extends android.renderscript.Program.BaseProgramBuilder {
- ctor public deprecated ProgramFragment.Builder(android.renderscript.RenderScript);
- method public deprecated android.renderscript.ProgramFragment create();
- }
-
- public deprecated class ProgramFragmentFixedFunction extends android.renderscript.ProgramFragment {
- }
-
- public static deprecated class ProgramFragmentFixedFunction.Builder {
- ctor public deprecated ProgramFragmentFixedFunction.Builder(android.renderscript.RenderScript);
- method public deprecated android.renderscript.ProgramFragmentFixedFunction create();
- method public deprecated android.renderscript.ProgramFragmentFixedFunction.Builder setPointSpriteTexCoordinateReplacement(boolean);
- method public deprecated android.renderscript.ProgramFragmentFixedFunction.Builder setTexture(android.renderscript.ProgramFragmentFixedFunction.Builder.EnvMode, android.renderscript.ProgramFragmentFixedFunction.Builder.Format, int) throws java.lang.IllegalArgumentException;
- method public deprecated android.renderscript.ProgramFragmentFixedFunction.Builder setVaryingColor(boolean);
- field public static final deprecated int MAX_TEXTURE = 2; // 0x2
- }
-
- public static final deprecated class ProgramFragmentFixedFunction.Builder.EnvMode extends java.lang.Enum {
- method public static android.renderscript.ProgramFragmentFixedFunction.Builder.EnvMode valueOf(java.lang.String);
- method public static final android.renderscript.ProgramFragmentFixedFunction.Builder.EnvMode[] values();
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.EnvMode DECAL;
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.EnvMode MODULATE;
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.EnvMode REPLACE;
- }
-
- public static final deprecated class ProgramFragmentFixedFunction.Builder.Format extends java.lang.Enum {
- method public static android.renderscript.ProgramFragmentFixedFunction.Builder.Format valueOf(java.lang.String);
- method public static final android.renderscript.ProgramFragmentFixedFunction.Builder.Format[] values();
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.Format ALPHA;
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.Format LUMINANCE_ALPHA;
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.Format RGB;
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.Format RGBA;
- }
-
- public deprecated class ProgramRaster extends android.renderscript.BaseObj {
- method public static deprecated android.renderscript.ProgramRaster CULL_BACK(android.renderscript.RenderScript);
- method public static deprecated android.renderscript.ProgramRaster CULL_FRONT(android.renderscript.RenderScript);
- method public static deprecated android.renderscript.ProgramRaster CULL_NONE(android.renderscript.RenderScript);
- method public deprecated android.renderscript.ProgramRaster.CullMode getCullMode();
- method public deprecated boolean isPointSpriteEnabled();
- }
-
- public static deprecated class ProgramRaster.Builder {
- ctor public deprecated ProgramRaster.Builder(android.renderscript.RenderScript);
- method public deprecated android.renderscript.ProgramRaster create();
- method public deprecated android.renderscript.ProgramRaster.Builder setCullMode(android.renderscript.ProgramRaster.CullMode);
- method public deprecated android.renderscript.ProgramRaster.Builder setPointSpriteEnabled(boolean);
- }
-
- public static final deprecated class ProgramRaster.CullMode extends java.lang.Enum {
- method public static android.renderscript.ProgramRaster.CullMode valueOf(java.lang.String);
- method public static final android.renderscript.ProgramRaster.CullMode[] values();
- enum_constant public static final deprecated android.renderscript.ProgramRaster.CullMode BACK;
- enum_constant public static final deprecated android.renderscript.ProgramRaster.CullMode FRONT;
- enum_constant public static final deprecated android.renderscript.ProgramRaster.CullMode NONE;
- }
-
- public class ProgramStore extends android.renderscript.BaseObj {
- method public static android.renderscript.ProgramStore BLEND_ALPHA_DEPTH_NONE(android.renderscript.RenderScript);
- method public static android.renderscript.ProgramStore BLEND_ALPHA_DEPTH_TEST(android.renderscript.RenderScript);
- method public static android.renderscript.ProgramStore BLEND_NONE_DEPTH_NONE(android.renderscript.RenderScript);
- method public static android.renderscript.ProgramStore BLEND_NONE_DEPTH_TEST(android.renderscript.RenderScript);
- method public android.renderscript.ProgramStore.BlendDstFunc getBlendDstFunc();
- method public android.renderscript.ProgramStore.BlendSrcFunc getBlendSrcFunc();
- method public android.renderscript.ProgramStore.DepthFunc getDepthFunc();
- method public boolean isColorMaskAlphaEnabled();
- method public boolean isColorMaskBlueEnabled();
- method public boolean isColorMaskGreenEnabled();
- method public boolean isColorMaskRedEnabled();
- method public boolean isDepthMaskEnabled();
- method public boolean isDitherEnabled();
- }
-
- public static final class ProgramStore.BlendDstFunc extends java.lang.Enum {
- method public static android.renderscript.ProgramStore.BlendDstFunc valueOf(java.lang.String);
- method public static final android.renderscript.ProgramStore.BlendDstFunc[] values();
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc DST_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc ONE;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc ONE_MINUS_DST_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc ONE_MINUS_SRC_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc ONE_MINUS_SRC_COLOR;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc SRC_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc SRC_COLOR;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc ZERO;
- }
-
- public static final class ProgramStore.BlendSrcFunc extends java.lang.Enum {
- method public static android.renderscript.ProgramStore.BlendSrcFunc valueOf(java.lang.String);
- method public static final android.renderscript.ProgramStore.BlendSrcFunc[] values();
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc DST_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc DST_COLOR;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc ONE;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc ONE_MINUS_DST_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc ONE_MINUS_DST_COLOR;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc ONE_MINUS_SRC_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc SRC_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc SRC_ALPHA_SATURATE;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc ZERO;
- }
-
- public static class ProgramStore.Builder {
- ctor public ProgramStore.Builder(android.renderscript.RenderScript);
- method public android.renderscript.ProgramStore create();
- method public android.renderscript.ProgramStore.Builder setBlendFunc(android.renderscript.ProgramStore.BlendSrcFunc, android.renderscript.ProgramStore.BlendDstFunc);
- method public android.renderscript.ProgramStore.Builder setColorMaskEnabled(boolean, boolean, boolean, boolean);
- method public android.renderscript.ProgramStore.Builder setDepthFunc(android.renderscript.ProgramStore.DepthFunc);
- method public android.renderscript.ProgramStore.Builder setDepthMaskEnabled(boolean);
- method public android.renderscript.ProgramStore.Builder setDitherEnabled(boolean);
- }
-
- public static final class ProgramStore.DepthFunc extends java.lang.Enum {
- method public static android.renderscript.ProgramStore.DepthFunc valueOf(java.lang.String);
- method public static final android.renderscript.ProgramStore.DepthFunc[] values();
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc ALWAYS;
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc EQUAL;
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc GREATER;
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc GREATER_OR_EQUAL;
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc LESS;
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc LESS_OR_EQUAL;
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc NOT_EQUAL;
- }
-
- public deprecated class ProgramVertex extends android.renderscript.Program {
- method public deprecated android.renderscript.Element getInput(int);
- method public deprecated int getInputCount();
- }
-
- public static deprecated class ProgramVertex.Builder extends android.renderscript.Program.BaseProgramBuilder {
- ctor public deprecated ProgramVertex.Builder(android.renderscript.RenderScript);
- method public deprecated android.renderscript.ProgramVertex.Builder addInput(android.renderscript.Element) throws java.lang.IllegalStateException;
- method public deprecated android.renderscript.ProgramVertex create();
- }
-
- public deprecated class ProgramVertexFixedFunction extends android.renderscript.ProgramVertex {
- method public deprecated void bindConstants(android.renderscript.ProgramVertexFixedFunction.Constants);
- }
-
- public static deprecated class ProgramVertexFixedFunction.Builder {
- ctor public deprecated ProgramVertexFixedFunction.Builder(android.renderscript.RenderScript);
- method public deprecated android.renderscript.ProgramVertexFixedFunction create();
- method public deprecated android.renderscript.ProgramVertexFixedFunction.Builder setTextureMatrixEnable(boolean);
- }
-
- public static deprecated class ProgramVertexFixedFunction.Constants {
- ctor public deprecated ProgramVertexFixedFunction.Constants(android.renderscript.RenderScript);
- method public deprecated void destroy();
- method public deprecated void setModelview(android.renderscript.Matrix4f);
- method public deprecated void setProjection(android.renderscript.Matrix4f);
- method public deprecated void setTexture(android.renderscript.Matrix4f);
- }
-
public class RSDriverException extends android.renderscript.RSRuntimeException {
ctor public RSDriverException(java.lang.String);
}
@@ -20355,43 +20207,16 @@ package android.renderscript {
ctor public RSRuntimeException(java.lang.String);
}
- public deprecated class RSSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback {
- ctor public deprecated RSSurfaceView(android.content.Context);
- ctor public deprecated RSSurfaceView(android.content.Context, android.util.AttributeSet);
- method public deprecated android.renderscript.RenderScriptGL createRenderScriptGL(android.renderscript.RenderScriptGL.SurfaceConfig);
- method public deprecated void destroyRenderScriptGL();
- method public deprecated android.renderscript.RenderScriptGL getRenderScriptGL();
- method public deprecated void pause();
- method public deprecated void resume();
- method public deprecated void setRenderScriptGL(android.renderscript.RenderScriptGL);
- method public deprecated void surfaceChanged(android.view.SurfaceHolder, int, int, int);
- method public deprecated void surfaceCreated(android.view.SurfaceHolder);
- method public deprecated void surfaceDestroyed(android.view.SurfaceHolder);
- }
-
- public deprecated class RSTextureView extends android.view.TextureView implements android.view.TextureView.SurfaceTextureListener {
- ctor public deprecated RSTextureView(android.content.Context);
- ctor public deprecated RSTextureView(android.content.Context, android.util.AttributeSet);
- method public deprecated android.renderscript.RenderScriptGL createRenderScriptGL(android.renderscript.RenderScriptGL.SurfaceConfig);
- method public deprecated void destroyRenderScriptGL();
- method public deprecated android.renderscript.RenderScriptGL getRenderScriptGL();
- method public deprecated void onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int);
- method public deprecated boolean onSurfaceTextureDestroyed(android.graphics.SurfaceTexture);
- method public deprecated void onSurfaceTextureSizeChanged(android.graphics.SurfaceTexture, int, int);
- method public deprecated void onSurfaceTextureUpdated(android.graphics.SurfaceTexture);
- method public deprecated void pause();
- method public deprecated void resume();
- method public deprecated void setRenderScriptGL(android.renderscript.RenderScriptGL);
- }
-
public class RenderScript {
method public void contextDump();
method public static android.renderscript.RenderScript create(android.content.Context);
+ method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType);
method public void destroy();
method public void finish();
method public final android.content.Context getApplicationContext();
method public android.renderscript.RenderScript.RSErrorHandler getErrorHandler();
method public android.renderscript.RenderScript.RSMessageHandler getMessageHandler();
+ method public void sendMessage(int, int[]);
method public void setErrorHandler(android.renderscript.RenderScript.RSErrorHandler);
method public void setMessageHandler(android.renderscript.RenderScript.RSMessageHandler);
method public void setPriority(android.renderscript.RenderScript.Priority);
@@ -20427,30 +20252,6 @@ package android.renderscript {
field protected int mLength;
}
- public deprecated class RenderScriptGL extends android.renderscript.RenderScript {
- ctor public deprecated RenderScriptGL(android.content.Context, android.renderscript.RenderScriptGL.SurfaceConfig);
- method public deprecated void bindProgramFragment(android.renderscript.ProgramFragment);
- method public deprecated void bindProgramRaster(android.renderscript.ProgramRaster);
- method public deprecated void bindProgramStore(android.renderscript.ProgramStore);
- method public deprecated void bindProgramVertex(android.renderscript.ProgramVertex);
- method public deprecated void bindRootScript(android.renderscript.Script);
- method public deprecated int getHeight();
- method public deprecated int getWidth();
- method public deprecated void pause();
- method public deprecated void resume();
- method public deprecated void setSurface(android.view.SurfaceHolder, int, int);
- method public deprecated void setSurfaceTexture(android.graphics.SurfaceTexture, int, int);
- }
-
- public static deprecated class RenderScriptGL.SurfaceConfig {
- ctor public deprecated RenderScriptGL.SurfaceConfig();
- ctor public deprecated RenderScriptGL.SurfaceConfig(android.renderscript.RenderScriptGL.SurfaceConfig);
- method public deprecated void setAlpha(int, int);
- method public deprecated void setColor(int, int);
- method public deprecated void setDepth(int, int);
- method public deprecated void setSamples(int, int, float);
- }
-
public class Sampler extends android.renderscript.BaseObj {
method public static android.renderscript.Sampler CLAMP_LINEAR(android.renderscript.RenderScript);
method public static android.renderscript.Sampler CLAMP_LINEAR_MIP_LINEAR(android.renderscript.RenderScript);
@@ -20496,6 +20297,12 @@ package android.renderscript {
method protected android.renderscript.Script.KernelID createKernelID(int, int, android.renderscript.Element, android.renderscript.Element);
method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker);
method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
+ method public boolean getVarB(int);
+ method public double getVarD(int);
+ method public float getVarF(int);
+ method public int getVarI(int);
+ method public long getVarJ(int);
+ method public void getVarV(int, android.renderscript.FieldPacker);
method protected void invoke(int);
method protected void invoke(int, android.renderscript.FieldPacker);
method public void setTimeZone(java.lang.String);
@@ -20572,6 +20379,13 @@ package android.renderscript {
public abstract class ScriptIntrinsic extends android.renderscript.Script {
}
+ public final class ScriptIntrinsic3DLUT extends android.renderscript.ScriptIntrinsic {
+ method public static android.renderscript.ScriptIntrinsic3DLUT create(android.renderscript.RenderScript, android.renderscript.Element);
+ method public void forEach(android.renderscript.Allocation, android.renderscript.Allocation);
+ method public android.renderscript.Script.KernelID getKernelID();
+ method public void setLUT(android.renderscript.Allocation);
+ }
+
public class ScriptIntrinsicBlend extends android.renderscript.ScriptIntrinsic {
method public static android.renderscript.ScriptIntrinsicBlend create(android.renderscript.RenderScript, android.renderscript.Element);
method public void forEachAdd(android.renderscript.Allocation, android.renderscript.Allocation);
@@ -20691,6 +20505,7 @@ package android.renderscript {
method public android.renderscript.Element getElement();
method public int getX();
method public int getY();
+ method public int getYuv();
method public int getZ();
method public boolean hasFaces();
method public boolean hasMipmaps();
@@ -20703,6 +20518,7 @@ package android.renderscript {
method public android.renderscript.Type.Builder setMipmaps(boolean);
method public android.renderscript.Type.Builder setX(int);
method public android.renderscript.Type.Builder setY(int);
+ method public android.renderscript.Type.Builder setYuvFormat(int);
method public android.renderscript.Type.Builder setZ(int);
}
@@ -20764,6 +20580,37 @@ package android.sax {
package android.security {
+ public final class AndroidKeyPairGeneratorSpec implements java.security.spec.AlgorithmParameterSpec {
+ method public android.content.Context getContext();
+ method public java.util.Date getEndDate();
+ method public java.lang.String getKeystoreAlias();
+ method public java.math.BigInteger getSerialNumber();
+ method public java.util.Date getStartDate();
+ method public javax.security.auth.x500.X500Principal getSubjectDN();
+ method public boolean isEncryptionRequired();
+ }
+
+ public static final class AndroidKeyPairGeneratorSpec.Builder {
+ ctor public AndroidKeyPairGeneratorSpec.Builder(android.content.Context);
+ method public android.security.AndroidKeyPairGeneratorSpec build();
+ method public android.security.AndroidKeyPairGeneratorSpec.Builder setAlias(java.lang.String);
+ method public android.security.AndroidKeyPairGeneratorSpec.Builder setEncryptionRequired();
+ method public android.security.AndroidKeyPairGeneratorSpec.Builder setEndDate(java.util.Date);
+ method public android.security.AndroidKeyPairGeneratorSpec.Builder setSerialNumber(java.math.BigInteger);
+ method public android.security.AndroidKeyPairGeneratorSpec.Builder setStartDate(java.util.Date);
+ method public android.security.AndroidKeyPairGeneratorSpec.Builder setSubject(javax.security.auth.x500.X500Principal);
+ }
+
+ public final class AndroidKeyStoreParameter implements java.security.KeyStore.ProtectionParameter {
+ method public boolean isEncryptionRequired();
+ }
+
+ public static final class AndroidKeyStoreParameter.Builder {
+ ctor public AndroidKeyStoreParameter.Builder(android.content.Context);
+ method public android.security.AndroidKeyStoreParameter build();
+ method public android.security.AndroidKeyStoreParameter.Builder setEncryptionRequired();
+ }
+
public final class KeyChain {
ctor public KeyChain();
method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String);
@@ -20839,6 +20686,38 @@ package android.service.dreams {
}
+package android.service.notification {
+
+ public abstract class NotificationListenerService extends android.app.Service {
+ ctor public NotificationListenerService();
+ method public final void clearAllNotifications();
+ method public final void clearNotification(java.lang.String, java.lang.String, int);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification);
+ method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
+ }
+
+ public class StatusBarNotification implements android.os.Parcelable {
+ ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
+ ctor public StatusBarNotification(android.os.Parcel);
+ method public android.service.notification.StatusBarNotification clone();
+ method public int describeContents();
+ method public int getUserId();
+ method public boolean isClearable();
+ method public boolean isOngoing();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public final int id;
+ field public final android.app.Notification notification;
+ field public final java.lang.String pkg;
+ field public final long postTime;
+ field public final java.lang.String tag;
+ field public final android.os.UserHandle user;
+ }
+
+}
+
package android.service.textservice {
public abstract class SpellCheckerService extends android.app.Service {
@@ -22781,6 +22660,7 @@ package android.text.bidi {
method public java.lang.String dirAttrValue(java.lang.String, android.text.TextDirectionHeuristic);
method public java.lang.String dirAttrValue(boolean);
method public java.lang.String endEdge();
+ method public static android.text.bidi.BidiFormatter getInstance();
method public static android.text.bidi.BidiFormatter getInstance(boolean);
method public static android.text.bidi.BidiFormatter getInstance(java.util.Locale);
method public boolean getStereoReset();
@@ -24490,6 +24370,7 @@ package android.view {
method public float getMax();
method public float getMin();
method public float getRange();
+ method public float getResolution();
method public int getSource();
method public boolean isFromSource(int);
}
@@ -25247,14 +25128,6 @@ package android.view {
field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
}
- public abstract interface Overlay {
- method public abstract void add(android.graphics.drawable.Drawable);
- method public abstract void add(android.view.View);
- method public abstract void clear();
- method public abstract void remove(android.graphics.drawable.Drawable);
- method public abstract void remove(android.view.View);
- }
-
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
method public float getCurrentSpan();
@@ -25507,6 +25380,7 @@ package android.view {
method protected float getBottomFadingEdgeStrength();
method protected int getBottomPaddingOffset();
method public float getCameraDistance();
+ method public android.graphics.Rect getClipBounds();
method public java.lang.CharSequence getContentDescription();
method public final android.content.Context getContext();
method protected android.view.ContextMenu.ContextMenuInfo getContextMenuInfo();
@@ -25559,7 +25433,7 @@ package android.view {
method public int getNextFocusUpId();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
method public int getOverScrollMode();
- method public android.view.Overlay getOverlay();
+ method public android.view.ViewOverlay getOverlay();
method public int getPaddingBottom();
method public int getPaddingEnd();
method public int getPaddingLeft();
@@ -25761,6 +25635,7 @@ package android.view {
method public final void setBottom(int);
method public void setCameraDistance(float);
method public void setClickable(boolean);
+ method public void setClipBounds(android.graphics.Rect);
method public void setContentDescription(java.lang.CharSequence);
method public void setDrawingCacheBackgroundColor(int);
method public void setDrawingCacheEnabled(boolean);
@@ -26186,6 +26061,7 @@ package android.view {
method public static int getChildMeasureSpec(int, int, int);
method protected boolean getChildStaticTransformation(android.view.View, android.view.animation.Transformation);
method public boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
+ method public boolean getClipChildren();
method public int getDescendantFocusability();
method public android.view.View getFocusedChild();
method public android.view.animation.LayoutAnimationController getLayoutAnimation();
@@ -26300,12 +26176,23 @@ package android.view {
method public abstract void onChildViewRemoved(android.view.View, android.view.View);
}
+ public class ViewGroupOverlay extends android.view.ViewOverlay {
+ method public void add(android.view.View);
+ method public void remove(android.view.View);
+ }
+
public abstract interface ViewManager {
method public abstract void addView(android.view.View, android.view.ViewGroup.LayoutParams);
method public abstract void removeView(android.view.View);
method public abstract void updateViewLayout(android.view.View, android.view.ViewGroup.LayoutParams);
}
+ public class ViewOverlay {
+ method public void add(android.graphics.drawable.Drawable);
+ method public void clear();
+ method public void remove(android.graphics.drawable.Drawable);
+ }
+
public abstract interface ViewParent {
method public abstract void bringChildToFront(android.view.View);
method public abstract void childDrawableStateChanged(android.view.View);
@@ -26336,6 +26223,7 @@ package android.view {
method public android.view.ViewPropertyAnimator alphaBy(float);
method public void cancel();
method public long getDuration();
+ method public android.animation.TimeInterpolator getInterpolator();
method public long getStartDelay();
method public android.view.ViewPropertyAnimator rotation(float);
method public android.view.ViewPropertyAnimator rotationBy(float);
@@ -27801,7 +27689,7 @@ package android.webkit {
method public void onReceivedTouchIconUrl(android.webkit.WebView, java.lang.String, boolean);
method public void onRequestFocus(android.webkit.WebView);
method public void onShowCustomView(android.view.View, android.webkit.WebChromeClient.CustomViewCallback);
- method public void onShowCustomView(android.view.View, int, android.webkit.WebChromeClient.CustomViewCallback);
+ method public deprecated void onShowCustomView(android.view.View, int, android.webkit.WebChromeClient.CustomViewCallback);
}
public static abstract interface WebChromeClient.CustomViewCallback {
@@ -27871,8 +27759,6 @@ package android.webkit {
method public synchronized int getMinimumFontSize();
method public synchronized int getMinimumLogicalFontSize();
method public deprecated synchronized android.webkit.WebSettings.PluginState getPluginState();
- method public deprecated synchronized boolean getPluginsEnabled();
- method public deprecated synchronized java.lang.String getPluginsPath();
method public synchronized java.lang.String getSansSerifFontFamily();
method public boolean getSaveFormData();
method public deprecated boolean getSavePassword();
@@ -27918,8 +27804,6 @@ package android.webkit {
method public synchronized void setMinimumLogicalFontSize(int);
method public void setNeedInitialFocus(boolean);
method public deprecated synchronized void setPluginState(android.webkit.WebSettings.PluginState);
- method public deprecated synchronized void setPluginsEnabled(boolean);
- method public deprecated synchronized void setPluginsPath(java.lang.String);
method public deprecated synchronized void setRenderPriority(android.webkit.WebSettings.RenderPriority);
method public synchronized void setSansSerifFontFamily(java.lang.String);
method public void setSaveFormData(boolean);
@@ -28171,11 +28055,11 @@ package android.webkit {
public class WebViewDatabase {
method public void clearFormData();
method public void clearHttpAuthUsernamePassword();
- method public void clearUsernamePassword();
+ method public deprecated void clearUsernamePassword();
method public static android.webkit.WebViewDatabase getInstance(android.content.Context);
method public boolean hasFormData();
method public boolean hasHttpAuthUsernamePassword();
- method public boolean hasUsernamePassword();
+ method public deprecated boolean hasUsernamePassword();
}
public class WebViewFragment extends android.app.Fragment {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 1c02960..ccb9e1f 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -39,6 +39,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.AndroidException;
import android.view.IWindowManager;
+import com.android.internal.os.BaseCommand;
import java.io.BufferedReader;
import java.io.File;
@@ -50,12 +51,9 @@ import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.List;
-public class Am {
+public class Am extends BaseCommand {
private IActivityManager mAm;
- private String[] mArgs;
- private int mNextArg;
- private String mCurArgData;
private int mStartFlags = 0;
private boolean mWaitOption = false;
@@ -67,33 +65,155 @@ public class Am {
private String mProfileFile;
- // These are magic strings understood by the Eclipse plugin.
- private static final String FATAL_ERROR_CODE = "Error type 1";
- private static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
- private static final String NO_CLASS_ERROR_CODE = "Error type 3";
-
/**
* Command-line entry point.
*
* @param args The command-line arguments
*/
public static void main(String[] args) {
- try {
- (new Am()).run(args);
- } catch (IllegalArgumentException e) {
- showUsage();
- System.err.println("Error: " + e.getMessage());
- } catch (Exception e) {
- e.printStackTrace(System.err);
- System.exit(1);
- }
+ (new Am()).run(args);
}
- private void run(String[] args) throws Exception {
- if (args.length < 1) {
- showUsage();
- return;
- }
+ public void onShowUsage(PrintStream out) {
+ out.println(
+ "usage: am [subcommand] [options]\n" +
+ "usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
+ " [--R COUNT] [-S] [--opengl-trace]\n" +
+ " [--user <USER_ID> | current] <INTENT>\n" +
+ " am startservice [--user <USER_ID> | current] <INTENT>\n" +
+ " am force-stop [--user <USER_ID> | all | current] <PACKAGE>\n" +
+ " am kill [--user <USER_ID> | all | current] <PACKAGE>\n" +
+ " am kill-all\n" +
+ " am broadcast [--user <USER_ID> | all | current] <INTENT>\n" +
+ " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" +
+ " [--user <USER_ID> | current]\n" +
+ " [--no-window-animation] <COMPONENT>\n" +
+ " am profile start [--user <USER_ID> current] <PROCESS> <FILE>\n" +
+ " am profile stop [--user <USER_ID> current] [<PROCESS>]\n" +
+ " am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" +
+ " am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
+ " am clear-debug-app\n" +
+ " am monitor [--gdb <port>]\n" +
+ " am screen-compat [on|off] <PACKAGE>\n" +
+ " am to-uri [INTENT]\n" +
+ " am to-intent-uri [INTENT]\n" +
+ " am switch-user <USER_ID>\n" +
+ " am stop-user <USER_ID>\n" +
+ "\n" +
+ "am start: start an Activity. Options are:\n" +
+ " -D: enable debugging\n" +
+ " -W: wait for launch to complete\n" +
+ " --start-profiler <FILE>: start profiler and send results to <FILE>\n" +
+ " -P <FILE>: like above, but profiling stops when app goes idle\n" +
+ " -R: repeat the activity launch <COUNT> times. Prior to each repeat,\n" +
+ " the top activity will be finished.\n" +
+ " -S: force stop the target app before starting the activity\n" +
+ " --opengl-trace: enable tracing of OpenGL functions\n" +
+ " --user <USER_ID> | current: Specify which user to run as; if not\n" +
+ " specified then run as the current user.\n" +
+ "\n" +
+ "am startservice: start a Service. Options are:\n" +
+ " --user <USER_ID> | current: Specify which user to run as; if not\n" +
+ " specified then run as the current user.\n" +
+ "\n" +
+ "am force-stop: force stop everything associated with <PACKAGE>.\n" +
+ " --user <USER_ID> | all | current: Specify user to force stop;\n" +
+ " all users if not specified.\n" +
+ "\n" +
+ "am kill: Kill all processes associated with <PACKAGE>. Only kills.\n" +
+ " processes that are safe to kill -- that is, will not impact the user\n" +
+ " experience.\n" +
+ " --user <USER_ID> | all | current: Specify user whose processes to kill;\n" +
+ " all users if not specified.\n" +
+ "\n" +
+ "am kill-all: Kill all background processes.\n" +
+ "\n" +
+ "am broadcast: send a broadcast Intent. Options are:\n" +
+ " --user <USER_ID> | all | current: Specify which user to send to; if not\n" +
+ " specified then send to all users.\n" +
+ " --receiver-permission <PERMISSION>: Require receiver to hold permission.\n" +
+ "\n" +
+ "am instrument: start an Instrumentation. Typically this target <COMPONENT>\n" +
+ " is the form <TEST_PACKAGE>/<RUNNER_CLASS>. Options are:\n" +
+ " -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT). Use with\n" +
+ " [-e perf true] to generate raw output for performance measurements.\n" +
+ " -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a\n" +
+ " common form is [-e <testrunner_flag> <value>[,<value>...]].\n" +
+ " -p <FILE>: write profiling data to <FILE>\n" +
+ " -w: wait for instrumentation to finish before returning. Required for\n" +
+ " test runners.\n" +
+ " --user <USER_ID> | current: Specify user instrumentation runs in;\n" +
+ " current user if not specified.\n" +
+ " --no-window-animation: turn off window animations will running.\n" +
+ "\n" +
+ "am profile: start and stop profiler on a process. The given <PROCESS> argument\n" +
+ " may be either a process name or pid. Options are:\n" +
+ " --user <USER_ID> | current: When supplying a process name,\n" +
+ " specify user of process to profile; uses current user if not specified.\n" +
+ "\n" +
+ "am dumpheap: dump the heap of a process. The given <PROCESS> argument may\n" +
+ " be either a process name or pid. Options are:\n" +
+ " -n: dump native heap instead of managed heap\n" +
+ " --user <USER_ID> | current: When supplying a process name,\n" +
+ " specify user of process to dump; uses current user if not specified.\n" +
+ "\n" +
+ "am set-debug-app: set application <PACKAGE> to debug. Options are:\n" +
+ " -w: wait for debugger when application starts\n" +
+ " --persistent: retain this value\n" +
+ "\n" +
+ "am clear-debug-app: clear the previously set-debug-app.\n" +
+ "\n" +
+ "am bug-report: request bug report generation; will launch UI\n" +
+ " when done to select where it should be delivered.\n" +
+ "\n" +
+ "am monitor: start monitoring for crashes or ANRs.\n" +
+ " --gdb: start gdbserv on the given port at crash/ANR\n" +
+ "\n" +
+ "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
+ "\n" +
+ "am to-uri: print the given Intent specification as a URI.\n" +
+ "\n" +
+ "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
+ "\n" +
+ "am switch-user: switch to put USER_ID in the foreground, starting\n" +
+ " execution of that user if it is currently stopped.\n" +
+ "\n" +
+ "am stop-user: stop execution of USER_ID, not allowing it to run any\n" +
+ " code until a later explicit switch to it.\n" +
+ "\n" +
+ "<INTENT> specifications include these flags and arguments:\n" +
+ " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
+ " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
+ " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
+ " [--esn <EXTRA_KEY> ...]\n" +
+ " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
+ " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
+ " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
+ " [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]\n" +
+ " [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" +
+ " [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]\n" +
+ " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
+ " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
+ " [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]\n" +
+ " [-n <COMPONENT>] [-f <FLAGS>]\n" +
+ " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
+ " [--debug-log-resolution] [--exclude-stopped-packages]\n" +
+ " [--include-stopped-packages]\n" +
+ " [--activity-brought-to-front] [--activity-clear-top]\n" +
+ " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" +
+ " [--activity-launched-from-history] [--activity-multiple-task]\n" +
+ " [--activity-no-animation] [--activity-no-history]\n" +
+ " [--activity-no-user-action] [--activity-previous-is-top]\n" +
+ " [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" +
+ " [--activity-single-top] [--activity-clear-task]\n" +
+ " [--activity-task-on-home]\n" +
+ " [--receiver-registered-only] [--receiver-replace-pending]\n" +
+ " [--selector]\n" +
+ " [<URI> | <PACKAGE> | <COMPONENT>]\n"
+ );
+ }
+
+ public void onRun() throws Exception {
mAm = ActivityManagerNative.getDefault();
if (mAm == null) {
@@ -101,9 +221,7 @@ public class Am {
throw new AndroidException("Can't connect to activity manager; is the system running?");
}
- mArgs = args;
- String op = args[0];
- mNextArg = 1;
+ String op = nextArgRequired();
if (op.equals("start")) {
runStart();
@@ -142,7 +260,7 @@ public class Am {
} else if (op.equals("stop-user")) {
runStopUser();
} else {
- throw new IllegalArgumentException("Unknown command: " + op);
+ showError("Error: unknown command '" + op + "'");
}
}
@@ -1303,193 +1421,4 @@ public class Am {
return true;
}
}
-
- private String nextOption() {
- if (mCurArgData != null) {
- String prev = mArgs[mNextArg - 1];
- throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
- }
- if (mNextArg >= mArgs.length) {
- return null;
- }
- String arg = mArgs[mNextArg];
- if (!arg.startsWith("-")) {
- return null;
- }
- mNextArg++;
- if (arg.equals("--")) {
- return null;
- }
- if (arg.length() > 1 && arg.charAt(1) != '-') {
- if (arg.length() > 2) {
- mCurArgData = arg.substring(2);
- return arg.substring(0, 2);
- } else {
- mCurArgData = null;
- return arg;
- }
- }
- mCurArgData = null;
- return arg;
- }
-
- private String nextArg() {
- if (mCurArgData != null) {
- String arg = mCurArgData;
- mCurArgData = null;
- return arg;
- } else if (mNextArg < mArgs.length) {
- return mArgs[mNextArg++];
- } else {
- return null;
- }
- }
-
- private String nextArgRequired() {
- String arg = nextArg();
- if (arg == null) {
- String prev = mArgs[mNextArg - 1];
- throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
- }
- return arg;
- }
-
- private static void showUsage() {
- System.err.println(
- "usage: am [subcommand] [options]\n" +
- "usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
- " [--R COUNT] [-S] [--opengl-trace]\n" +
- " [--user <USER_ID> | current] <INTENT>\n" +
- " am startservice [--user <USER_ID> | current] <INTENT>\n" +
- " am force-stop [--user <USER_ID> | all | current] <PACKAGE>\n" +
- " am kill [--user <USER_ID> | all | current] <PACKAGE>\n" +
- " am kill-all\n" +
- " am broadcast [--user <USER_ID> | all | current] <INTENT>\n" +
- " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" +
- " [--user <USER_ID> | current]\n" +
- " [--no-window-animation] <COMPONENT>\n" +
- " am profile start [--user <USER_ID> current] <PROCESS> <FILE>\n" +
- " am profile stop [--user <USER_ID> current] [<PROCESS>]\n" +
- " am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" +
- " am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
- " am clear-debug-app\n" +
- " am monitor [--gdb <port>]\n" +
- " am screen-compat [on|off] <PACKAGE>\n" +
- " am to-uri [INTENT]\n" +
- " am to-intent-uri [INTENT]\n" +
- " am switch-user <USER_ID>\n" +
- " am stop-user <USER_ID>\n" +
- "\n" +
- "am start: start an Activity. Options are:\n" +
- " -D: enable debugging\n" +
- " -W: wait for launch to complete\n" +
- " --start-profiler <FILE>: start profiler and send results to <FILE>\n" +
- " -P <FILE>: like above, but profiling stops when app goes idle\n" +
- " -R: repeat the activity launch <COUNT> times. Prior to each repeat,\n" +
- " the top activity will be finished.\n" +
- " -S: force stop the target app before starting the activity\n" +
- " --opengl-trace: enable tracing of OpenGL functions\n" +
- " --user <USER_ID> | current: Specify which user to run as; if not\n" +
- " specified then run as the current user.\n" +
- "\n" +
- "am startservice: start a Service. Options are:\n" +
- " --user <USER_ID> | current: Specify which user to run as; if not\n" +
- " specified then run as the current user.\n" +
- "\n" +
- "am force-stop: force stop everything associated with <PACKAGE>.\n" +
- " --user <USER_ID> | all | current: Specify user to force stop;\n" +
- " all users if not specified.\n" +
- "\n" +
- "am kill: Kill all processes associated with <PACKAGE>. Only kills.\n" +
- " processes that are safe to kill -- that is, will not impact the user\n" +
- " experience.\n" +
- " --user <USER_ID> | all | current: Specify user whose processes to kill;\n" +
- " all users if not specified.\n" +
- "\n" +
- "am kill-all: Kill all background processes.\n" +
- "\n" +
- "am broadcast: send a broadcast Intent. Options are:\n" +
- " --user <USER_ID> | all | current: Specify which user to send to; if not\n" +
- " specified then send to all users.\n" +
- " --receiver-permission <PERMISSION>: Require receiver to hold permission.\n" +
- "\n" +
- "am instrument: start an Instrumentation. Typically this target <COMPONENT>\n" +
- " is the form <TEST_PACKAGE>/<RUNNER_CLASS>. Options are:\n" +
- " -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT). Use with\n" +
- " [-e perf true] to generate raw output for performance measurements.\n" +
- " -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a\n" +
- " common form is [-e <testrunner_flag> <value>[,<value>...]].\n" +
- " -p <FILE>: write profiling data to <FILE>\n" +
- " -w: wait for instrumentation to finish before returning. Required for\n" +
- " test runners.\n" +
- " --user <USER_ID> | current: Specify user instrumentation runs in;\n" +
- " current user if not specified.\n" +
- " --no-window-animation: turn off window animations will running.\n" +
- "\n" +
- "am profile: start and stop profiler on a process. The given <PROCESS> argument\n" +
- " may be either a process name or pid. Options are:\n" +
- " --user <USER_ID> | current: When supplying a process name,\n" +
- " specify user of process to profile; uses current user if not specified.\n" +
- "\n" +
- "am dumpheap: dump the heap of a process. The given <PROCESS> argument may\n" +
- " be either a process name or pid. Options are:\n" +
- " -n: dump native heap instead of managed heap\n" +
- " --user <USER_ID> | current: When supplying a process name,\n" +
- " specify user of process to dump; uses current user if not specified.\n" +
- "\n" +
- "am set-debug-app: set application <PACKAGE> to debug. Options are:\n" +
- " -w: wait for debugger when application starts\n" +
- " --persistent: retain this value\n" +
- "\n" +
- "am clear-debug-app: clear the previously set-debug-app.\n" +
- "\n" +
- "am bug-report: request bug report generation; will launch UI\n" +
- " when done to select where it should be delivered.\n" +
- "\n" +
- "am monitor: start monitoring for crashes or ANRs.\n" +
- " --gdb: start gdbserv on the given port at crash/ANR\n" +
- "\n" +
- "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
- "\n" +
- "am to-uri: print the given Intent specification as a URI.\n" +
- "\n" +
- "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
- "\n" +
- "am switch-user: switch to put USER_ID in the foreground, starting\n" +
- " execution of that user if it is currently stopped.\n" +
- "\n" +
- "am stop-user: stop execution of USER_ID, not allowing it to run any\n" +
- " code until a later explicit switch to it.\n" +
- "\n" +
- "<INTENT> specifications include these flags and arguments:\n" +
- " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
- " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
- " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
- " [--esn <EXTRA_KEY> ...]\n" +
- " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
- " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
- " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
- " [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]\n" +
- " [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" +
- " [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]\n" +
- " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
- " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
- " [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]\n" +
- " [-n <COMPONENT>] [-f <FLAGS>]\n" +
- " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
- " [--debug-log-resolution] [--exclude-stopped-packages]\n" +
- " [--include-stopped-packages]\n" +
- " [--activity-brought-to-front] [--activity-clear-top]\n" +
- " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" +
- " [--activity-launched-from-history] [--activity-multiple-task]\n" +
- " [--activity-no-animation] [--activity-no-history]\n" +
- " [--activity-no-user-action] [--activity-previous-is-top]\n" +
- " [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" +
- " [--activity-single-top] [--activity-clear-task]\n" +
- " [--activity-task-on-home]\n" +
- " [--receiver-registered-only] [--receiver-replace-pending]\n" +
- " [--selector]\n" +
- " [<URI> | <PACKAGE> | <COMPONENT>]\n"
- );
- }
}
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index b39c335..b9afe40 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -7,6 +7,7 @@ LOCAL_SRC_FILES:= \
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
+ liblog \
libbinder \
libandroid_runtime
@@ -27,6 +28,7 @@ LOCAL_SRC_FILES:= \
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
+ liblog \
libbinder \
libandroid_runtime
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 8c46b21..d5ff84e 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -9,6 +9,7 @@ LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_SHARED_LIBRARIES := \
libcutils \
+ liblog \
libandroidfw \
libutils \
libbinder \
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index cdbc405..e43501c 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -75,11 +75,14 @@ public class Input {
Float.parseFloat(args[3]), Float.parseFloat(args[4]), -1);
return;
}
- } else if (command.equals("touchscreen") || command.equals("touchpad")) {
+ } else if (command.equals("touchscreen") || command.equals("touchpad")
+ || command.equals("touchnavigation")) {
// determine input source
int inputSource = InputDevice.SOURCE_TOUCHSCREEN;
if (command.equals("touchpad")) {
inputSource = InputDevice.SOURCE_TOUCHPAD;
+ } else if (command.equals("touchnavigation")) {
+ inputSource = InputDevice.SOURCE_TOUCH_NAVIGATION;
}
// determine subcommand
if (args.length > 1) {
@@ -247,8 +250,9 @@ public class Input {
System.err.println("usage: input ...");
System.err.println(" input text <string>");
System.err.println(" input keyevent <key code number or name>");
- System.err.println(" input [touchscreen|touchpad] tap <x> <y>");
- System.err.println(" input [touchscreen|touchpad] swipe <x1> <y1> <x2> <y2> [duration(ms)]");
+ System.err.println(" input [touchscreen|touchpad|touchnavigation] tap <x> <y>");
+ System.err.println(" input [touchscreen|touchpad|touchnavigation] swipe "
+ + "<x1> <y1> <x2> <y2> [duration(ms)]");
System.err.println(" input trackball press");
System.err.println(" input trackball roll <dx> <dy>");
}
diff --git a/cmds/media/Android.mk b/cmds/media/Android.mk
new file mode 100644
index 0000000..b9451c5
--- /dev/null
+++ b/cmds/media/Android.mk
@@ -0,0 +1,15 @@
+# Copyright 2013 The Android Open Source Project
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_MODULE := media_cmd
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := media
+LOCAL_SRC_FILES := media
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
diff --git a/cmds/media/MODULE_LICENSE_APACHE2 b/cmds/media/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cmds/media/MODULE_LICENSE_APACHE2
diff --git a/cmds/media/NOTICE b/cmds/media/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/cmds/media/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/cmds/media/media b/cmds/media/media
new file mode 100755
index 0000000..1194442
--- /dev/null
+++ b/cmds/media/media
@@ -0,0 +1,6 @@
+# Script to start "media_cmd" on the device, which has a very rudimentary
+# shell.
+#
+base=/system
+export CLASSPATH=$base/framework/media_cmd.jar
+exec app_process $base/bin com.android.commands.media.Media "$@"
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
new file mode 100644
index 0000000..56af7d6
--- /dev/null
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -0,0 +1,220 @@
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.commands.media;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.media.IAudioService;
+import android.media.IRemoteControlDisplay;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.AndroidException;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import com.android.internal.os.BaseCommand;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+
+public class Media extends BaseCommand {
+
+ private IAudioService mAudioService;
+
+ /**
+ * Command-line entry point.
+ *
+ * @param args The command-line arguments
+ */
+ public static void main(String[] args) {
+ (new Media()).run(args);
+ }
+
+ public void onShowUsage(PrintStream out) {
+ out.println(
+ "usage: media [subcommand] [options]\n" +
+ " media dispatch KEY\n" +
+ " media remote-display\n" +
+ "\n" +
+ "media dispatch: dispatch a media key to the current media client.\n" +
+ " KEY may be: play, pause, play-pause, mute, headsethook,\n" +
+ " stop, next, previous, rewind, recordm fast-forword.\n" +
+ "media remote-display: monitor remote display updates.\n"
+ );
+ }
+
+ public void onRun() throws Exception {
+ mAudioService = IAudioService.Stub.asInterface(ServiceManager.checkService(
+ Context.AUDIO_SERVICE));
+ if (mAudioService == null) {
+ System.err.println(NO_SYSTEM_ERROR_CODE);
+ throw new AndroidException("Can't connect to audio service; is the system running?");
+ }
+
+ String op = nextArgRequired();
+
+ if (op.equals("dispatch")) {
+ runDispatch();
+ } else if (op.equals("remote-display")) {
+ runRemoteDisplay();
+ } else {
+ showError("Error: unknown command '" + op + "'");
+ return;
+ }
+ }
+
+ private void sendMediaKey(KeyEvent event) {
+ try {
+ mAudioService.dispatchMediaKeyEvent(event);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private void runDispatch() throws Exception {
+ String cmd = nextArgRequired();
+ int keycode;
+ if ("play".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_PLAY;
+ } else if ("pause".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_PAUSE;
+ } else if ("play-pause".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
+ } else if ("mute".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MUTE;
+ } else if ("headsethook".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_HEADSETHOOK;
+ } else if ("stop".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_STOP;
+ } else if ("next".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_NEXT;
+ } else if ("previous".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
+ } else if ("rewind".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_REWIND;
+ } else if ("record".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_RECORD;
+ } else if ("fast-forward".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_FAST_FORWARD;
+ } else {
+ showError("Error: unknown dispatch code '" + cmd + "'");
+ return;
+ }
+
+ final long now = SystemClock.uptimeMillis();
+ sendMediaKey(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keycode, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
+ sendMediaKey(new KeyEvent(now, now, KeyEvent.ACTION_UP, keycode, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
+ }
+
+ class RemoteDisplayMonitor extends IRemoteControlDisplay.Stub {
+ RemoteDisplayMonitor() {
+ }
+
+
+ @Override
+ public void setCurrentClientId(int clientGeneration, PendingIntent clientMediaIntent,
+ boolean clearing) {
+ System.out.println("New client: id=" + clientGeneration
+ + " intent=" + clientMediaIntent + " clearing=" + clearing);
+ }
+
+ @Override
+ public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
+ long currentPosMs, float speed) {
+ System.out.println("New state: id=" + generationId + " state=" + state
+ + " time=" + stateChangeTimeMs + " pos=" + currentPosMs + " speed=" + speed);
+ }
+
+ @Override
+ public void setTransportControlInfo(int generationId, int transportControlFlags,
+ int posCapabilities) {
+ System.out.println("New control info: id=" + generationId
+ + " flags=0x" + Integer.toHexString(transportControlFlags)
+ + " cap=0x" + Integer.toHexString(posCapabilities));
+ }
+
+ @Override
+ public void setMetadata(int generationId, Bundle metadata) {
+ System.out.println("New metadata: id=" + generationId
+ + " data=" + metadata);
+ }
+
+ @Override
+ public void setArtwork(int generationId, Bitmap artwork) {
+ System.out.println("New artwork: id=" + generationId
+ + " art=" + artwork);
+ }
+
+ @Override
+ public void setAllMetadata(int generationId, Bundle metadata, Bitmap artwork) {
+ System.out.println("New metadata+artwork: id=" + generationId
+ + " data=" + metadata + " art=" + artwork);
+ }
+
+ void printUsageMessage() {
+ System.out.println("Monitoring remote control displays... available commands:");
+ System.out.println("(q)uit: finish monitoring");
+ }
+
+ void run() throws RemoteException {
+ printUsageMessage();
+
+ mAudioService.registerRemoteControlDisplay(this, 0, 0);
+
+ try {
+ InputStreamReader converter = new InputStreamReader(System.in);
+ BufferedReader in = new BufferedReader(converter);
+ String line;
+
+ while ((line = in.readLine()) != null) {
+ boolean addNewline = true;
+ if (line.length() <= 0) {
+ addNewline = false;
+ } else if ("q".equals(line) || "quit".equals(line)) {
+ break;
+ } else {
+ System.out.println("Invalid command: " + line);
+ }
+
+ synchronized (this) {
+ if (addNewline) {
+ System.out.println("");
+ }
+ printUsageMessage();
+ }
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ mAudioService.unregisterRemoteControlDisplay(this);
+ }
+ }
+ }
+
+ private void runRemoteDisplay() throws Exception {
+ RemoteDisplayMonitor monitor = new RemoteDisplayMonitor();
+ monitor.run();
+ }
+}
diff --git a/cmds/system_server/Android.mk b/cmds/system_server/Android.mk
index ad537977..3083e31 100644
--- a/cmds/system_server/Android.mk
+++ b/cmds/system_server/Android.mk
@@ -7,7 +7,8 @@ LOCAL_SRC_FILES:= \
LOCAL_SHARED_LIBRARIES := \
libutils \
libbinder \
- libsystem_server
+ libsystem_server \
+ liblog
LOCAL_C_INCLUDES := \
$(JNI_H_INCLUDE)
@@ -17,4 +18,3 @@ LOCAL_MODULE:= system_server
include $(BUILD_EXECUTABLE)
include $(LOCAL_PATH)/library/Android.mk
-
diff --git a/cmds/system_server/library/Android.mk b/cmds/system_server/library/Android.mk
index c42424c..d78474e 100644
--- a/cmds/system_server/library/Android.mk
+++ b/cmds/system_server/library/Android.mk
@@ -16,10 +16,11 @@ LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
libsensorservice \
libsurfaceflinger \
- libinput \
+ libinput \
libutils \
libbinder \
- libcutils
+ libcutils \
+ liblog
LOCAL_MODULE:= libsystem_server
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
index 31eba96..815a0ac 100644
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ b/cmds/wm/src/com/android/commands/wm/Wm.java
@@ -26,21 +26,15 @@ import android.os.ServiceManager;
import android.util.AndroidException;
import android.view.Display;
import android.view.IWindowManager;
+import com.android.internal.os.BaseCommand;
+import java.io.PrintStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-public class Wm {
+public class Wm extends BaseCommand {
private IWindowManager mWm;
- private String[] mArgs;
- private int mNextArg;
- private String mCurArgData;
-
- // These are magic strings understood by the Eclipse plugin.
- private static final String FATAL_ERROR_CODE = "Error type 1";
- private static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
- private static final String NO_CLASS_ERROR_CODE = "Error type 3";
/**
* Command-line entry point.
@@ -48,23 +42,25 @@ public class Wm {
* @param args The command-line arguments
*/
public static void main(String[] args) {
- try {
- (new Wm()).run(args);
- } catch (IllegalArgumentException e) {
- showUsage();
- System.err.println("Error: " + e.getMessage());
- } catch (Exception e) {
- e.printStackTrace(System.err);
- System.exit(1);
- }
+ (new Wm()).run(args);
}
- private void run(String[] args) throws Exception {
- if (args.length < 1) {
- showUsage();
- return;
- }
+ public void onShowUsage(PrintStream out) {
+ out.println(
+ "usage: wm [subcommand] [options]\n" +
+ " wm size [reset|WxH]\n" +
+ " wm density [reset|DENSITY]\n" +
+ " wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
+ "\n" +
+ "wm size: return or override display size.\n" +
+ "\n" +
+ "wm density: override display density.\n" +
+ "\n" +
+ "wm overscan: set overscan area for display.\n"
+ );
+ }
+ public void onRun() throws Exception {
mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
Context.WINDOW_SERVICE));
if (mWm == null) {
@@ -72,9 +68,7 @@ public class Wm {
throw new AndroidException("Can't connect to window manager; is the system running?");
}
- mArgs = args;
- String op = args[0];
- mNextArg = 1;
+ String op = nextArgRequired();
if (op.equals("size")) {
runDisplaySize();
@@ -83,7 +77,8 @@ public class Wm {
} else if (op.equals("overscan")) {
runDisplayOverscan();
} else {
- throw new IllegalArgumentException("Unknown command: " + op);
+ showError("Error: unknown command '" + op + "'");
+ return;
}
}
@@ -198,69 +193,4 @@ public class Wm {
} catch (RemoteException e) {
}
}
-
- private String nextOption() {
- if (mCurArgData != null) {
- String prev = mArgs[mNextArg - 1];
- throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
- }
- if (mNextArg >= mArgs.length) {
- return null;
- }
- String arg = mArgs[mNextArg];
- if (!arg.startsWith("-")) {
- return null;
- }
- mNextArg++;
- if (arg.equals("--")) {
- return null;
- }
- if (arg.length() > 1 && arg.charAt(1) != '-') {
- if (arg.length() > 2) {
- mCurArgData = arg.substring(2);
- return arg.substring(0, 2);
- } else {
- mCurArgData = null;
- return arg;
- }
- }
- mCurArgData = null;
- return arg;
- }
-
- private String nextArg() {
- if (mCurArgData != null) {
- String arg = mCurArgData;
- mCurArgData = null;
- return arg;
- } else if (mNextArg < mArgs.length) {
- return mArgs[mNextArg++];
- } else {
- return null;
- }
- }
-
- private String nextArgRequired() {
- String arg = nextArg();
- if (arg == null) {
- String prev = mArgs[mNextArg - 1];
- throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
- }
- return arg;
- }
-
- private static void showUsage() {
- System.err.println(
- "usage: wm [subcommand] [options]\n" +
- " wm size [reset|WxH]\n" +
- " wm density [reset|DENSITY]\n" +
- " wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
- "\n" +
- "wm size: return or override display size.\n" +
- "\n" +
- "wm density: override display density.\n" +
- "\n" +
- "wm overscan: set overscan area for display.\n"
- );
- }
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 811b92a..31de98d 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -24,6 +24,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -348,6 +349,7 @@ public abstract class AccessibilityService extends Service {
public void onServiceConnected();
public void onSetConnectionId(int connectionId);
public boolean onGesture(int gestureId);
+ public boolean onKeyEvent(KeyEvent event);
}
private int mConnectionId;
@@ -413,6 +415,32 @@ public abstract class AccessibilityService extends Service {
}
/**
+ * Callback that allows an accessibility service to observe the key events
+ * before they are passed to the rest of the system. This means that the events
+ * are first delivered here before they are passed to the device policy, the
+ * input method, or applications.
+ * <p>
+ * <strong>Note:</strong> It is important that key events are handled in such
+ * a way that the event stream that would be passed to the rest of the system
+ * is well-formed. For example, handling the down event but not the up event
+ * and vice versa would generate an inconsistent event stream.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> The key events delivered in this method are copies
+ * and modifying them will have no effect on the events that will be passed
+ * to the system. This method is intended to perform purely filtering
+ * functionality.
+ * <p>
+ *
+ * @param event The event to be processed.
+ * @return If true then the event will be consumed and not delivered to
+ * applications, otherwise it will be delivered as usual.
+ */
+ protected boolean onKeyEvent(KeyEvent event) {
+ return false;
+ }
+
+ /**
* Gets the root node in the currently active window if this service
* can retrieve window content.
*
@@ -535,6 +563,11 @@ public abstract class AccessibilityService extends Service {
public boolean onGesture(int gestureId) {
return AccessibilityService.this.onGesture(gestureId);
}
+
+ @Override
+ public boolean onKeyEvent(KeyEvent event) {
+ return AccessibilityService.this.onKeyEvent(event);
+ }
});
}
@@ -554,11 +587,14 @@ public abstract class AccessibilityService extends Service {
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 final HandlerCaller mCaller;
private final Callbacks mCallback;
+ private int mConnectionId;
+
public IAccessibilityServiceClientWrapper(Context context, Looper looper,
Callbacks callback) {
mCallback = callback;
@@ -591,41 +627,65 @@ public abstract class AccessibilityService extends Service {
mCaller.sendMessage(message);
}
+ @Override
+ public void onKeyEvent(KeyEvent event, int sequence) {
+ Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event);
+ mCaller.sendMessage(message);
+ }
+
public void executeMessage(Message message) {
switch (message.what) {
- case DO_ON_ACCESSIBILITY_EVENT :
+ case DO_ON_ACCESSIBILITY_EVENT: {
AccessibilityEvent event = (AccessibilityEvent) message.obj;
if (event != null) {
AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
mCallback.onAccessibilityEvent(event);
event.recycle();
}
- return;
- case DO_ON_INTERRUPT :
+ } return;
+ case DO_ON_INTERRUPT: {
mCallback.onInterrupt();
- return;
- case DO_SET_SET_CONNECTION :
- final int connectionId = message.arg1;
+ } return;
+ case DO_SET_SET_CONNECTION: {
+ mConnectionId = message.arg1;
IAccessibilityServiceConnection connection =
(IAccessibilityServiceConnection) message.obj;
if (connection != null) {
- AccessibilityInteractionClient.getInstance().addConnection(connectionId,
+ AccessibilityInteractionClient.getInstance().addConnection(mConnectionId,
connection);
- mCallback.onSetConnectionId(connectionId);
+ mCallback.onSetConnectionId(mConnectionId);
mCallback.onServiceConnected();
} else {
- AccessibilityInteractionClient.getInstance().removeConnection(connectionId);
+ AccessibilityInteractionClient.getInstance().removeConnection(mConnectionId);
AccessibilityInteractionClient.getInstance().clearCache();
mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID);
}
- return;
- case DO_ON_GESTURE :
+ } return;
+ case DO_ON_GESTURE: {
final int gestureId = message.arg1;
mCallback.onGesture(gestureId);
- return;
- case DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE:
+ } return;
+ case DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: {
AccessibilityInteractionClient.getInstance().clearCache();
- return;
+ } return;
+ case DO_ON_KEY_EVENT: {
+ KeyEvent event = (KeyEvent) message.obj;
+ try {
+ IAccessibilityServiceConnection connection = AccessibilityInteractionClient
+ .getInstance().getConnection(mConnectionId);
+ if (connection != null) {
+ final boolean result = mCallback.onKeyEvent(event);
+ final int sequence = message.arg1;
+ try {
+ connection.setOnKeyEventResult(result, sequence);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ } finally {
+ event.recycle();
+ }
+ } return;
default :
Log.w(LOG_TAG, "Unknown message type " + message.what);
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 5d684e3..c5e3d43a 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.KeyEvent;
/**
* Top-level interface to an accessibility service component.
@@ -35,4 +36,6 @@ import android.view.accessibility.AccessibilityEvent;
void onGesture(int gesture);
void clearAccessibilityNodeInfoCache();
+
+ void onKeyEvent(in KeyEvent event, int sequence);
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 7a29f35..3df06b5 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -31,144 +31,31 @@ interface IAccessibilityServiceConnection {
void setServiceInfo(in AccessibilityServiceInfo info);
- /**
- * Finds an {@link android.view.accessibility.AccessibilityNodeInfo} by accessibility id.
- *
- * @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
- * to query the currently active window.
- * @param accessibilityNodeId A unique view id or virtual descendant id from
- * where to start the search. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
- * to start from the root.
- * @param interactionId The id of the interaction for matching with the callback result.
- * @param callback Callback which to receive the result.
- * @param flags Additional flags.
- * @param threadId The id of the calling thread.
- * @return Whether the call succeeded.
- */
boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, long threadId);
- /**
- * Finds {@link android.view.accessibility.AccessibilityNodeInfo}s by View text.
- * The match is case insensitive containment. The search is performed in the window
- * whose id is specified and starts from the node whose accessibility id is specified.
- *
- * @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
- * to query the currently active window.
- * @param accessibilityNodeId A unique view id or virtual descendant id from
- * where to start the search. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
- * to start from the root.
- * @param text The searched text.
- * @param interactionId The id of the interaction for matching with the callback result.
- * @param callback Callback which to receive the result.
- * @param threadId The id of the calling thread.
- * @return Whether the call succeeded.
- */
boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId,
String text, int interactionId, IAccessibilityInteractionConnectionCallback callback,
long threadId);
- /**
- * Finds an {@link android.view.accessibility.AccessibilityNodeInfo} by View id. The search
- * is performed in the window whose id is specified and starts from the node whose
- * accessibility id is specified.
- *
- * @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
- * to query the currently active window.
- * @param accessibilityNodeId A unique view id or virtual descendant id from
- * where to start the search. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
- * to start from the root.
- * @param viewId The fully qualified resource name of the view id to find.
- * @param interactionId The id of the interaction for matching with the callback result.
- * @param callback Callback which to receive the result.
- * @param threadId The id of the calling thread.
- * @return Whether the call succeeded.
- */
boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
long accessibilityNodeId, String viewId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long threadId);
- /**
- * Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the specified
- * focus type. The search is performed in the window whose id is specified and starts from
- * the node whose accessibility id is specified.
- *
- * @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
- * to query the currently active window.
- * @param accessibilityNodeId A unique view id or virtual descendant id from
- * where to start the search. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
- * to start from the root.
- * @param focusType The type of focus to find.
- * @param interactionId The id of the interaction for matching with the callback result.
- * @param callback Callback which to receive the result.
- * @param threadId The id of the calling thread.
- * @return Whether the call succeeded.
- */
boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId);
- /**
- * Finds an {@link android.view.accessibility.AccessibilityNodeInfo} to take accessibility
- * focus in the given direction. The search is performed in the window whose id is
- * specified and starts from the node whose accessibility id is specified.
- *
- * @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
- * to query the currently active window.
- * @param accessibilityNodeId A unique view id or virtual descendant id from
- * where to start the search. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
- * to start from the root.
- * @param direction The direction in which to search for focusable.
- * @param interactionId The id of the interaction for matching with the callback result.
- * @param callback Callback which to receive the result.
- * @param threadId The id of the calling thread.
- * @return Whether the call succeeded.
- */
boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId);
- /**
- * Performs an accessibility action on an
- * {@link android.view.accessibility.AccessibilityNodeInfo}.
- *
- * @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
- * to query the currently active window.
- * @param accessibilityNodeId A unique view id or virtual descendant id from
- * where to start the search. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
- * to start from the root.
- * @param action The action to perform.
- * @param arguments Optional action arguments.
- * @param interactionId The id of the interaction for matching with the callback result.
- * @param callback Callback which to receive the result.
- * @param threadId The id of the calling thread.
- * @return Whether the action was performed.
- */
boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId,
int action, in Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long threadId);
- /**
- * @return The associated accessibility service info.
- */
AccessibilityServiceInfo getServiceInfo();
- /**
- * Performs a global action, such as going home, going back, etc.
- *
- * @param action The action to perform.
- * @return Whether the action was performed.
- */
boolean performGlobalAction(int action);
+
+ oneway void setOnKeyEventResult(boolean handled, int sequence);
}
diff --git a/core/java/android/accounts/CantAddAccountActivity.java b/core/java/android/accounts/CantAddAccountActivity.java
new file mode 100644
index 0000000..e1717a6
--- /dev/null
+++ b/core/java/android/accounts/CantAddAccountActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+
+import com.android.internal.R;
+
+/**
+ * @hide
+ * Just shows an error message about the account restrictions for the limited user.
+ */
+public class CantAddAccountActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.app_not_authorized);
+ }
+
+ public void onCancelButtonClicked(View view) {
+ onBackPressed();
+ }
+}
diff --git a/core/java/android/animation/Animatable.java b/core/java/android/animation/Animatable.java
new file mode 100644
index 0000000..f9ccb4d
--- /dev/null
+++ b/core/java/android/animation/Animatable.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+/**
+ * This interface is implemented by animation-related classes that expose
+ * the ability to set and get duration, startDelay, and interpolators.
+ */
+public interface Animatable {
+
+ /**
+ * The amount of time, in milliseconds, to delay processing the animation
+ * after the animation is started. The {@link #setDuration(long)} of the
+ * animation will not begin to elapse until after the startDelay has elapsed.
+ *
+ * @return the number of milliseconds to delay running the animation
+ */
+ long getStartDelay();
+
+ /**
+ * The amount of time, in milliseconds, to delay processing the animation
+ * after the animation is started. The {@link #setDuration(long)} of the
+ * animation will not begin to elapse until after the startDelay has elapsed.
+
+ * @param startDelay The amount of the delay, in milliseconds
+ */
+ void setStartDelay(long startDelay);
+
+ /**
+ * Sets the length of the animation.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ */
+ Animatable setDuration(long duration);
+
+ /**
+ * Gets the duration of the animation.
+ *
+ * @return The length of the animation, in milliseconds.
+ */
+ long getDuration();
+
+ /**
+ * The time interpolator used in calculating the elapsed fraction of the
+ * animation. The interpolator determines whether the animation runs with
+ * linear or non-linear motion, such as acceleration and deceleration.
+ *
+ * @param value the interpolator to be used by this animation
+ */
+ void setInterpolator(TimeInterpolator value);
+
+ /**
+ * Returns the timing interpolator that this animation uses.
+ *
+ * @return The timing interpolator for this animation.
+ */
+ public TimeInterpolator getInterpolator();
+}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 788765d..da97d72 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -22,14 +22,21 @@ import java.util.ArrayList;
* This is the superclass for classes which provide basic support for animations which can be
* started, ended, and have <code>AnimatorListeners</code> added to them.
*/
-public abstract class Animator implements Cloneable {
-
+public abstract class Animator implements Cloneable, Animatable {
/**
* The set of listeners to be sent events through the life of an animation.
*/
ArrayList<AnimatorListener> mListeners = null;
+ @Override
+ public abstract Animator setDuration(long duration);
+
+ @Override
+ public TimeInterpolator getInterpolator() {
+ return null;
+ }
+
/**
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
* running after that delay elapses. A non-delayed animation will have its initial
@@ -70,47 +77,6 @@ public abstract class Animator implements Cloneable {
}
/**
- * The amount of time, in milliseconds, to delay starting the animation after
- * {@link #start()} is called.
- *
- * @return the number of milliseconds to delay running the animation
- */
- public abstract long getStartDelay();
-
- /**
- * The amount of time, in milliseconds, to delay starting the animation after
- * {@link #start()} is called.
-
- * @param startDelay The amount of the delay, in milliseconds
- */
- public abstract void setStartDelay(long startDelay);
-
-
- /**
- * Sets the length of the animation.
- *
- * @param duration The length of the animation, in milliseconds.
- */
- public abstract Animator setDuration(long duration);
-
- /**
- * Gets the length of the animation.
- *
- * @return The length of the animation, in milliseconds.
- */
- public abstract long getDuration();
-
- /**
- * The time interpolator used in calculating the elapsed fraction of this animation. The
- * interpolator determines whether the animation runs with linear or non-linear motion,
- * such as acceleration and deceleration. The default value is
- * {@link android.view.animation.AccelerateDecelerateInterpolator}
- *
- * @param value the interpolator to be used by this animation
- */
- public abstract void setInterpolator(TimeInterpolator value);
-
- /**
* Returns whether this Animator is currently running (having been started and gone past any
* initial startDelay period and not yet ended).
*
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index f9fa444..b48853b 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -120,9 +120,19 @@ public final class AnimatorSet extends Animator {
// set, it is passed along to the child animations.
private long mDuration = -1;
+ // Records the interpolator for the set. Null value indicates that no interpolator
+ // was set on this AnimatorSet, so it should not be passed down to the children.
+ private TimeInterpolator mInterpolator = null;
+
/**
* Sets up this AnimatorSet to play all of the supplied animations at the same time.
+ * This is equivalent to calling {@link #play(Animator)} with the first animator in the
+ * set and then {@link Builder#with(Animator)} with each of the other animators. Note that
+ * an Animator with a {@link Animator#setStartDelay(long) startDelay} will not actually
+ * start until that delay elapses, which means that if the first animator in the list
+ * supplied to this constructor has a startDelay, none of the other animators will start
+ * until that first animator's startDelay has elapsed.
*
* @param items The animations that will be started simultaneously.
*/
@@ -230,15 +240,21 @@ public final class AnimatorSet extends Animator {
/**
* Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
- * of this AnimatorSet.
+ * of this AnimatorSet. The default value is null, which means that no interpolator
+ * is set on this AnimatorSet. Setting the interpolator to any non-null value
+ * will cause that interpolator to be set on the child animations
+ * when the set is started.
*
* @param interpolator the interpolator to be used by each child animation of this AnimatorSet
*/
@Override
public void setInterpolator(TimeInterpolator interpolator) {
- for (Node node : mNodes) {
- node.animation.setInterpolator(interpolator);
- }
+ mInterpolator = interpolator;
+ }
+
+ @Override
+ public TimeInterpolator getInterpolator() {
+ return mInterpolator;
}
/**
@@ -460,7 +476,12 @@ public final class AnimatorSet extends Animator {
node.animation.setDuration(mDuration);
}
}
- // First, sort the nodes (if necessary). This will ensure that sortedNodes
+ if (mInterpolator != null) {
+ for (Node node : mNodes) {
+ node.animation.setInterpolator(mInterpolator);
+ }
+ }
+ // First, sort the nodes (if necessary). This will ensure that sortedNodes
// contains the animation nodes in the correct order.
sortNodes();
diff --git a/core/java/android/animation/RectEvaluator.java b/core/java/android/animation/RectEvaluator.java
index 10932bb..28d496b 100644
--- a/core/java/android/animation/RectEvaluator.java
+++ b/core/java/android/animation/RectEvaluator.java
@@ -18,7 +18,7 @@ package android.animation;
import android.graphics.Rect;
/**
- * This evaluator can be used to perform type interpolation between <code>int</code> values.
+ * This evaluator can be used to perform type interpolation between <code>Rect</code> values.
*/
public class RectEvaluator implements TypeEvaluator<Rect> {
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index ea605b9..cb44264 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -16,10 +16,8 @@
package android.animation;
-import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
-import android.os.SystemProperties;
+import android.os.Trace;
import android.util.AndroidRuntimeException;
import android.view.Choreographer;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -48,6 +46,7 @@ import java.util.HashMap;
* Animation</a> developer guide.</p>
* </div>
*/
+@SuppressWarnings("unchecked")
public class ValueAnimator extends Animator {
/**
@@ -340,7 +339,7 @@ public class ValueAnimator extends Animator {
return;
}
if (mValues == null || mValues.length == 0) {
- setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofInt("", values)});
+ setValues(PropertyValuesHolder.ofInt("", values));
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setIntValues(values);
@@ -368,7 +367,7 @@ public class ValueAnimator extends Animator {
return;
}
if (mValues == null || mValues.length == 0) {
- setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofFloat("", values)});
+ setValues(PropertyValuesHolder.ofFloat("", values));
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setFloatValues(values);
@@ -400,8 +399,7 @@ public class ValueAnimator extends Animator {
return;
}
if (mValues == null || mValues.length == 0) {
- setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofObject("",
- (TypeEvaluator)null, values)});
+ setValues(PropertyValuesHolder.ofObject("", null, values));
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setObjectValues(values);
@@ -423,7 +421,7 @@ public class ValueAnimator extends Animator {
mValues = values;
mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
for (int i = 0; i < numValues; ++i) {
- PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i];
+ PropertyValuesHolder valuesHolder = values[i];
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
}
// New property/values/target should cause re-initialization prior to starting
@@ -537,6 +535,7 @@ public class ValueAnimator extends Animator {
*
* @hide
*/
+ @SuppressWarnings("unchecked")
protected static class AnimationHandler implements Runnable {
// The per-thread list of all active animations
/** @hide */
@@ -854,6 +853,7 @@ public class ValueAnimator extends Animator {
*
* @return The timing interpolator for this ValueAnimator.
*/
+ @Override
public TimeInterpolator getInterpolator() {
return mInterpolator;
}
@@ -1024,6 +1024,8 @@ public class ValueAnimator extends Animator {
mStarted = false;
mStartListenersCalled = false;
mPlayingBackwards = false;
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "animator",
+ System.identityHashCode(this));
}
/**
@@ -1031,6 +1033,8 @@ public class ValueAnimator extends Animator {
* called on the UI thread.
*/
private void startAnimation(AnimationHandler handler) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "animator",
+ System.identityHashCode(this));
initAnimation();
handler.mAnimations.add(this);
if (mStartDelay > 0 && mListeners != null) {
@@ -1095,7 +1099,7 @@ public class ValueAnimator extends Animator {
}
}
if (mRepeatMode == REVERSE) {
- mPlayingBackwards = mPlayingBackwards ? false : true;
+ mPlayingBackwards = !mPlayingBackwards;
}
mCurrentIteration += (int)fraction;
fraction = fraction % 1f;
@@ -1252,7 +1256,7 @@ public class ValueAnimator extends Animator {
}
}
- private AnimationHandler getOrCreateAnimationHandler() {
+ private static AnimationHandler getOrCreateAnimationHandler() {
AnimationHandler handler = sAnimationHandler.get();
if (handler == null) {
handler = new AnimationHandler();
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 3602fc4..c4ddf1f 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -695,6 +695,86 @@ public abstract class ActionBar {
public boolean isTitleTruncated() { return false; }
/**
+ * Set an alternate drawable to display next to the icon/logo/title
+ * when {@link #DISPLAY_HOME_AS_UP} is enabled. This can be useful if you are using
+ * this mode to display an alternate selection for up navigation, such as a sliding drawer.
+ *
+ * <p>If you pass <code>null</code> to this method, the default drawable from the theme
+ * will be used.</p>
+ *
+ * <p>If you implement alternate or intermediate behavior around Up, you should also
+ * call {@link #setHomeActionContentDescription(int) setHomeActionContentDescription()}
+ * to provide a correct description of the action for accessibility support.</p>
+ *
+ * @param indicator A drawable to use for the up indicator, or null to use the theme's default
+ *
+ * @see #setDisplayOptions(int, int)
+ * @see #setDisplayHomeAsUpEnabled(boolean)
+ * @see #setHomeActionContentDescription(int)
+ */
+ public void setHomeAsUpIndicator(Drawable indicator) { }
+
+ /**
+ * Set an alternate drawable to display next to the icon/logo/title
+ * when {@link #DISPLAY_HOME_AS_UP} is enabled. This can be useful if you are using
+ * this mode to display an alternate selection for up navigation, such as a sliding drawer.
+ *
+ * <p>If you pass <code>0</code> to this method, the default drawable from the theme
+ * will be used.</p>
+ *
+ * <p>If you implement alternate or intermediate behavior around Up, you should also
+ * call {@link #setHomeActionContentDescription(int) setHomeActionContentDescription()}
+ * to provide a correct description of the action for accessibility support.</p>
+ *
+ * @param resId Resource ID of a drawable to use for the up indicator, or null
+ * to use the theme's default
+ *
+ * @see #setDisplayOptions(int, int)
+ * @see #setDisplayHomeAsUpEnabled(boolean)
+ * @see #setHomeActionContentDescription(int)
+ */
+ public void setHomeAsUpIndicator(int resId) { }
+
+ /**
+ * Set an alternate description for the Home/Up action, when enabled.
+ *
+ * <p>This description is commonly used for accessibility/screen readers when
+ * the Home action is enabled. (See {@link #setDisplayHomeAsUpEnabled(boolean)}.)
+ * Examples of this are, "Navigate Home" or "Navigate Up" depending on the
+ * {@link #DISPLAY_HOME_AS_UP} display option. If you have changed the home-as-up
+ * indicator using {@link #setHomeAsUpIndicator(int)} to indicate more specific
+ * functionality such as a sliding drawer, you should also set this to accurately
+ * describe the action.</p>
+ *
+ * <p>Setting this to <code>null</code> will use the system default description.</p>
+ *
+ * @param description New description for the Home action when enabled
+ * @see #setHomeAsUpIndicator(int)
+ * @see #setHomeAsUpIndicator(android.graphics.drawable.Drawable)
+ */
+ public void setHomeActionContentDescription(CharSequence description) { }
+
+ /**
+ * Set an alternate description for the Home/Up action, when enabled.
+ *
+ * <p>This description is commonly used for accessibility/screen readers when
+ * the Home action is enabled. (See {@link #setDisplayHomeAsUpEnabled(boolean)}.)
+ * Examples of this are, "Navigate Home" or "Navigate Up" depending on the
+ * {@link #DISPLAY_HOME_AS_UP} display option. If you have changed the home-as-up
+ * indicator using {@link #setHomeAsUpIndicator(int)} to indicate more specific
+ * functionality such as a sliding drawer, you should also set this to accurately
+ * describe the action.</p>
+ *
+ * <p>Setting this to <code>0</code> will use the system default description.</p>
+ *
+ * @param resId Resource ID of a string to use as the new description
+ * for the Home action when enabled
+ * @see #setHomeAsUpIndicator(int)
+ * @see #setHomeAsUpIndicator(android.graphics.drawable.Drawable)
+ */
+ public void setHomeActionContentDescription(int resId) { }
+
+ /**
* Listener interface for ActionBar navigation events.
*/
public interface OnNavigationListener {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 31074e2..a26bdbc 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4030,10 +4030,16 @@ public class Activity extends ContextThemeWrapper
* use this information to validate that the recipient is allowed to
* receive the data.
*
- * <p>Note: if the calling activity is not expecting a result (that is it
+ * <p class="note">Note: if the calling activity is not expecting a result (that is it
* did not use the {@link #startActivityForResult}
* form that includes a request code), then the calling package will be
- * null.
+ * null.</p>
+ *
+ * <p class="note">Note: prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
+ * the result from this method was unstable. If the process hosting the calling
+ * package was no longer running, it would return null instead of the proper package
+ * name. You can use {@link #getCallingActivity()} and retrieve the package name
+ * from that instead.</p>
*
* @return The package of the activity that will receive your
* reply, or null if none.
@@ -4052,12 +4058,12 @@ public class Activity extends ContextThemeWrapper
* can use this information to validate that the recipient is allowed to
* receive the data.
*
- * <p>Note: if the calling activity is not expecting a result (that is it
+ * <p class="note">Note: if the calling activity is not expecting a result (that is it
* did not use the {@link #startActivityForResult}
* form that includes a request code), then the calling package will be
* null.
*
- * @return String The full name of the activity that will receive your
+ * @return The ComponentName of the activity that will receive your
* reply, or null if none.
*/
public ComponentName getCallingActivity() {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index aca4f9c..98baa0e 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1413,6 +1413,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case SET_USER_IS_MONKEY_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final boolean monkey = (data.readInt() == 1);
+ setUserIsMonkey(monkey);
+ reply.writeNoException();
+ return true;
+ }
+
case FINISH_HEAVY_WEIGHT_APP_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
finishHeavyWeightApp();
@@ -1853,6 +1861,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case KILL_UID_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int uid = data.readInt();
+ String reason = data.readString();
+ killUid(uid, reason);
+ reply.writeNoException();
+ return true;
+ }
+
}
return super.onTransact(code, data, reply, flags);
@@ -3335,6 +3352,7 @@ class ActivityManagerProxy implements IActivityManager
data.writeString(reason);
data.writeInt(secure ? 1 : 0);
mRemote.transact(KILL_PIDS_TRANSACTION, data, reply, 0);
+ reply.readException();
boolean res = reply.readInt() != 0;
data.recycle();
reply.recycle();
@@ -3623,7 +3641,18 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return res;
}
-
+
+ public void setUserIsMonkey(boolean monkey) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(monkey ? 1 : 0);
+ mRemote.transact(SET_USER_IS_MONKEY_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
public void finishHeavyWeightApp() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -4229,5 +4258,17 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
+ public void killUid(int uid, String reason) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(uid);
+ data.writeString(reason);
+ mRemote.transact(KILL_UID_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ae0671b..d4056c9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -86,6 +86,7 @@ import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.renderscript.RenderScript;
+import android.security.AndroidKeyStoreProvider;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
@@ -101,6 +102,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
+import java.security.Security;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -1593,6 +1595,12 @@ public final class ActivityThread {
public static String currentPackageName() {
ActivityThread am = currentActivityThread();
return (am != null && am.mBoundApplication != null)
+ ? am.mBoundApplication.appInfo.packageName : null;
+ }
+
+ public static String currentProcessName() {
+ ActivityThread am = currentActivityThread();
+ return (am != null && am.mBoundApplication != null)
? am.mBoundApplication.processName : null;
}
@@ -4320,6 +4328,10 @@ public final class ActivityThread {
GLUtils.setTracingLevel(1);
}
+ // Allow application-generated systrace messages if we're debuggable.
+ boolean appTracingAllowed = (data.appInfo.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ Trace.setAppTracingAllowed(appTracingAllowed);
+
/**
* Initialize the default http proxy in this process for the reasons we set the time zone.
*/
@@ -5068,6 +5080,8 @@ public final class ActivityThread {
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
+ Security.addProvider(new AndroidKeyStoreProvider());
+
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index 9e3cd7e..a26b88c 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -16,6 +16,7 @@
package android.app;
+import android.os.Trace;
import dalvik.system.PathClassLoader;
import java.util.HashMap;
@@ -54,14 +55,19 @@ class ApplicationLoaders
return loader;
}
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
PathClassLoader pathClassloader =
new PathClassLoader(zip, libPath, parent);
-
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
mLoaders.put(zip, pathClassloader);
return pathClassloader;
}
- return new PathClassLoader(zip, parent);
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
+ PathClassLoader pathClassloader = new PathClassLoader(zip, parent);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ return pathClassloader;
}
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9bf8830..3fc82fa 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -371,9 +371,9 @@ class ContextImpl extends Context {
return new DisplayManager(ctx.getOuterContext());
}});
- registerService(INPUT_METHOD_SERVICE, new ServiceFetcher() {
- public Object createService(ContextImpl ctx) {
- return InputMethodManager.getInstance(ctx);
+ registerService(INPUT_METHOD_SERVICE, new StaticServiceFetcher() {
+ public Object createStaticService() {
+ return InputMethodManager.getInstance();
}});
registerService(TEXT_SERVICES_MANAGER_SERVICE, new ServiceFetcher() {
@@ -449,7 +449,8 @@ class ContextImpl extends Context {
registerService(SENSOR_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
- return new SystemSensorManager(ctx.mMainThread.getHandler().getLooper());
+ return new SystemSensorManager(ctx.getOuterContext(),
+ ctx.mMainThread.getHandler().getLooper());
}});
registerService(STATUS_BAR_SERVICE, new ServiceFetcher() {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index cf4c729..33a2770 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -285,7 +285,9 @@ public interface IActivityManager extends IInterface {
int enterAnim, int exitAnim) throws RemoteException;
public boolean isUserAMonkey() throws RemoteException;
-
+
+ public void setUserIsMonkey(boolean monkey) throws RemoteException;
+
public void finishHeavyWeightApp() throws RemoteException;
public void setImmersive(IBinder token, boolean immersive) throws RemoteException;
@@ -373,6 +375,8 @@ public interface IActivityManager extends IInterface {
public void reportTopActivityExtras(IBinder token, Bundle extras) throws RemoteException;
+ public void killUid(int uid, String reason) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -632,4 +636,6 @@ public interface IActivityManager extends IInterface {
int GET_TOP_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+161;
int REPORT_TOP_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+162;
int GET_LAUNCHED_FROM_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+163;
+ int KILL_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+164;
+ int SET_USER_IS_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+165;
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 3d9b2ae..92ec3ad 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -17,12 +17,12 @@
package android.app;
-import android.app.INotificationListener;
import android.app.ITransientNotification;
+import android.service.notification.StatusBarNotification;
import android.app.Notification;
+import android.content.ComponentName;
import android.content.Intent;
-
-import com.android.internal.statusbar.StatusBarNotification;
+import android.service.notification.INotificationListener;
/** {@hide} */
interface INotificationManager
@@ -41,7 +41,9 @@ interface INotificationManager
StatusBarNotification[] getActiveNotifications(String callingPkg);
StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count);
- void registerListener(in INotificationListener listener, String pkg, int userid);
+ void registerListener(in INotificationListener listener, in ComponentName component, int userid);
void unregisterListener(in INotificationListener listener, int userid);
-}
+ void clearNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
+ void clearAllNotificationsFromListener(in INotificationListener token);
+} \ No newline at end of file
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e0dfb25..a307a73 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -189,6 +189,10 @@ public class Instrumentation {
if (mPerfMetrics != null) {
results.putAll(mPerfMetrics);
}
+ if (mUiAutomation != null) {
+ mUiAutomation.disconnect();
+ mUiAutomation = null;
+ }
mThread.finishInstrumentation(resultCode, results);
}
@@ -1695,10 +1699,6 @@ public class Instrumentation {
startPerformanceSnapshot();
}
onStart();
- if (mUiAutomation != null) {
- mUiAutomation.disconnect();
- mUiAutomation = null;
- }
}
}
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 51867bc..7d8a36e 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -1,7 +1,20 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package android.app;
-import com.android.internal.view.IInputMethodSession;
-
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
@@ -25,7 +38,6 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.inputmethod.InputMethodManager;
import java.io.File;
-import java.lang.ref.WeakReference;
/**
* Convenience for implementing an activity that will be implemented
@@ -65,7 +77,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
private NativeContentView mNativeContentView;
private InputMethodManager mIMM;
- private InputMethodCallback mInputMethodCallback;
private int mNativeHandle;
@@ -117,22 +128,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
super(context, attrs);
}
}
-
- static final class InputMethodCallback implements InputMethodManager.FinishedEventCallback {
- WeakReference<NativeActivity> mNa;
-
- InputMethodCallback(NativeActivity na) {
- mNa = new WeakReference<NativeActivity>(na);
- }
-
- @Override
- public void finishedEvent(int seq, boolean handled) {
- NativeActivity na = mNa.get();
- if (na != null) {
- na.finishPreDispatchKeyEventNative(na.mNativeHandle, seq, handled);
- }
- }
- }
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -141,7 +136,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
ActivityInfo ai;
mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
- mInputMethodCallback = new InputMethodCallback(this);
getWindow().takeSurface(this);
getWindow().takeInputQueue(this);
@@ -353,7 +347,8 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
}
void preDispatchKeyEvent(KeyEvent event, int seq) {
- mIMM.dispatchInputEvent(this, seq, event, mInputMethodCallback);
+ // FIXME: Input dispatch should be redirected back through ViewRootImpl again.
+ finishPreDispatchKeyEventNative(mNativeHandle, seq, false);
}
void setWindowFlags(int flags, int mask) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ebca041..a7543a8 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1603,7 +1603,7 @@ public class Notification implements Parcelable
n.defaults = mDefaults;
n.flags = mFlags;
n.bigContentView = makeBigContentView();
- if (mLedOnMs != 0 && mLedOffMs != 0) {
+ if (mLedOnMs != 0 || mLedOffMs != 0) {
n.flags |= FLAG_SHOW_LIGHTS;
}
if ((mDefaults & DEFAULT_LIGHTS) != 0) {
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 7d02342..05b79c1 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -31,6 +31,7 @@ import android.os.SystemClock;
import android.util.Log;
import android.view.Display;
import android.view.InputEvent;
+import android.view.KeyEvent;
import android.view.Surface;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
@@ -618,6 +619,25 @@ public final class UiAutomation {
return screenShot;
}
+ /**
+ * Sets whether this UiAutomation to run in a "monkey" mode. Applications can query whether
+ * they are executed in a "monkey" mode, i.e. run by a test framework, and avoid doing
+ * 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()}
+ */
+ public void setRunAsMonkey(boolean enable) {
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ }
+ try {
+ ActivityManagerNative.getDefault().setUserIsMonkey(enable);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while setting run as monkey!", re);
+ }
+ }
+
private static float getDegreesForRotation(int value) {
switch (value) {
case Surface.ROTATION_90: {
@@ -693,6 +713,11 @@ public final class UiAutomation {
listener.onAccessibilityEvent(AccessibilityEvent.obtain(event));
}
}
+
+ @Override
+ public boolean onKeyEvent(KeyEvent event) {
+ return false;
+ }
});
}
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 9c0064e..3342068 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -709,7 +709,7 @@ public class WallpaperManager {
public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
try {
//Log.v(TAG, "Sending new wallpaper offsets from app...");
- WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+ WindowManagerGlobal.getWindowSession().setWallpaperPosition(
windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep);
//Log.v(TAG, "...app returning after sending offsets!");
} catch (RemoteException e) {
@@ -747,7 +747,7 @@ public class WallpaperManager {
int x, int y, int z, Bundle extras) {
try {
//Log.v(TAG, "Sending new wallpaper offsets from app...");
- WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand(
+ WindowManagerGlobal.getWindowSession().sendWallpaperCommand(
windowToken, action, x, y, z, extras, false);
//Log.v(TAG, "...app returning after sending offsets!");
} catch (RemoteException e) {
@@ -767,7 +767,7 @@ public class WallpaperManager {
*/
public void clearWallpaperOffsets(IBinder windowToken) {
try {
- WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+ WindowManagerGlobal.getWindowSession().setWallpaperPosition(
windowToken, -1, -1, -1, -1);
} catch (RemoteException e) {
// Ignore.
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 644c619..d7f150b 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -288,9 +288,9 @@ public final class BluetoothGattServer implements BluetoothProfile {
}
/**
- * Close the connection to the gatt service.
+ * Close this GATT server instance.
*/
- /*package*/ void close() {
+ public void close() {
if (DBG) Log.d(TAG, "close()");
unregisterCallback();
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ef9b0bf..03e241a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -256,6 +256,12 @@ public abstract class Context {
* Return the Looper for the main thread of the current process. This is
* the thread used to dispatch calls to application components (activities,
* services, etc).
+ * <p>
+ * By definition, this method returns the same result as would be obtained
+ * by calling {@link Looper#getMainLooper() Looper.getMainLooper()}.
+ * </p>
+ *
+ * @return The main looper.
*/
public abstract Looper getMainLooper();
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index da5480e..d4f7f06 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -71,7 +71,9 @@ public interface SharedPreferences {
* {@link #commit} or {@link #apply} are called.
*
* @param key The name of the preference to modify.
- * @param value The new value for the preference.
+ * @param value The new value for the preference. Supplying {@code null}
+ * as the value is equivalent to calling {@link #remove(String)} with
+ * this key.
*
* @return Returns a reference to the same Editor object, so you can
* chain put calls together.
@@ -83,7 +85,9 @@ public interface SharedPreferences {
* back once {@link #commit} is called.
*
* @param key The name of the preference to modify.
- * @param values The new values for the preference.
+ * @param values The set of new values for the preference. Passing {@code null}
+ * for this argument is equivalent to calling {@link #remove(String)} with
+ * this key.
* @return Returns a reference to the same Editor object, so you can
* chain put calls together.
*/
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 8f3b62d..8154bca 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -279,6 +279,30 @@ public class ActivityInfo extends ComponentInfo
public static final int SCREEN_ORIENTATION_FULL_SENSOR = 10;
/**
+ * Constant corresponding to <code>userLandscape</code> in
+ * the {@link android.R.attr#screenOrientation} attribute.
+ */
+ public static final int SCREEN_ORIENTATION_USER_LANDSCAPE = 11;
+
+ /**
+ * Constant corresponding to <code>userPortrait</code> in
+ * the {@link android.R.attr#screenOrientation} attribute.
+ */
+ public static final int SCREEN_ORIENTATION_USER_PORTRAIT = 12;
+
+ /**
+ * Constant corresponding to <code>fullUser</code> in
+ * the {@link android.R.attr#screenOrientation} attribute.
+ */
+ public static final int SCREEN_ORIENTATION_FULL_USER = 13;
+
+ /**
+ * Constant corresponding to <code>locked</code> in
+ * the {@link android.R.attr#screenOrientation} attribute.
+ */
+ public static final int SCREEN_ORIENTATION_LOCKED = 14;
+
+ /**
* The preferred screen orientation this activity would like to run in.
* From the {@link android.R.attr#screenOrientation} attribute, one of
* {@link #SCREEN_ORIENTATION_UNSPECIFIED},
@@ -292,7 +316,11 @@ public class ActivityInfo extends ComponentInfo
* {@link #SCREEN_ORIENTATION_SENSOR_PORTRAIT},
* {@link #SCREEN_ORIENTATION_REVERSE_LANDSCAPE},
* {@link #SCREEN_ORIENTATION_REVERSE_PORTRAIT},
- * {@link #SCREEN_ORIENTATION_FULL_SENSOR}.
+ * {@link #SCREEN_ORIENTATION_FULL_SENSOR},
+ * {@link #SCREEN_ORIENTATION_USER_LANDSCAPE},
+ * {@link #SCREEN_ORIENTATION_USER_PORTRAIT},
+ * {@link #SCREEN_ORIENTATION_FULL_USER},
+ * {@link #SCREEN_ORIENTATION_LOCKED},
*/
public int screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
@@ -398,7 +426,7 @@ public class ActivityInfo extends ComponentInfo
* Bit in {@link #configChanges} that indicates that the activity
* can itself handle changes to the font scaling factor. Set from the
* {@link android.R.attr#configChanges} attribute. This is
- * not a core resource configutation, but a higher-level value, so its
+ * not a core resource configuration, but a higher-level value, so its
* constant starts at the high bits.
*/
public static final int CONFIG_FONT_SCALE = 0x40000000;
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index fb539c5..af1a6d5 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -154,7 +154,7 @@ public class PackageInfo implements Parcelable {
/**
* Flag for {@link #requestedPermissionsFlags}: the requested permission
* is required for the application to run; the user can not optionally
- * disable it.
+ * disable it. Currently all permissions are required.
*/
public static final int REQUESTED_PERMISSION_REQUIRED = 1<<0;
@@ -224,6 +224,9 @@ public class PackageInfo implements Parcelable {
/** @hide */
public String restrictedAccountType;
+ /** @hide */
+ public String requiredAccountType;
+
public PackageInfo() {
}
@@ -266,6 +269,7 @@ public class PackageInfo implements Parcelable {
dest.writeInt(installLocation);
dest.writeInt(requiredForAllUsers ? 1 : 0);
dest.writeString(restrictedAccountType);
+ dest.writeString(requiredAccountType);
}
public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -306,5 +310,6 @@ public class PackageInfo implements Parcelable {
installLocation = source.readInt();
requiredForAllUsers = source.readInt() != 0;
restrictedAccountType = source.readString();
+ requiredAccountType = source.readString();
}
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index da15e3b..30bdfef 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1754,6 +1754,7 @@ public abstract class PackageManager {
/**
* Returns an {@link Intent} suitable for passing to {@code startActivityForResult}
* which prompts the user to grant {@code permissions} to this application.
+ * @hide
*
* @throws NullPointerException if {@code permissions} is {@code null}.
* @throws IllegalArgumentException if {@code permissions} contains {@code null}.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4835d05..acb3725 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -289,6 +289,7 @@ public class PackageParser {
pi.installLocation = p.installLocation;
pi.requiredForAllUsers = p.mRequiredForAllUsers;
pi.restrictedAccountType = p.mRestrictedAccountType;
+ pi.requiredAccountType = p.mRequiredAccountType;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
if ((flags&PackageManager.GET_GIDS) != 0) {
@@ -1036,25 +1037,10 @@ public class PackageParser {
return null;
}
} else if (tagName.equals("uses-permission")) {
- sa = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.AndroidManifestUsesPermission);
-
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- String name = sa.getNonResourceString(
- com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
- boolean required = sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
-
- sa.recycle();
-
- if (name != null && !pkg.requestedPermissions.contains(name)) {
- pkg.requestedPermissions.add(name.intern());
- pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE);
+ if (!parseUsesPermission(pkg, res, parser, attrs, outError)) {
+ return null;
}
- XmlUtils.skipCurrentTag(parser);
-
} else if (tagName.equals("uses-configuration")) {
ConfigurationInfo cPref = new ConfigurationInfo();
sa = res.obtainAttributes(attrs,
@@ -1398,9 +1384,56 @@ public class PackageParser {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
}
+ /*
+ * b/8528162: Ignore the <uses-permission android:required> attribute if
+ * targetSdkVersion < JELLY_BEAN_MR2. There are lots of apps in the wild
+ * which are improperly using this attribute, even though it never worked.
+ */
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ for (int i = 0; i < pkg.requestedPermissionsRequired.size(); i++) {
+ pkg.requestedPermissionsRequired.set(i, Boolean.TRUE);
+ }
+ }
+
return pkg;
}
+ private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
+ AttributeSet attrs, String[] outError)
+ throws XmlPullParserException, IOException {
+ TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AndroidManifestUsesPermission);
+
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ String name = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
+/*
+ boolean required = sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
+*/
+ boolean required = true; // Optional <uses-permission> not supported
+
+ sa.recycle();
+
+ if (name != null) {
+ int index = pkg.requestedPermissions.indexOf(name);
+ if (index == -1) {
+ pkg.requestedPermissions.add(name.intern());
+ pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE);
+ } else {
+ if (pkg.requestedPermissionsRequired.get(index) != required) {
+ outError[0] = "conflicting <uses-permission> entries";
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+ return false;
+ }
+ }
+ }
+
+ XmlUtils.skipCurrentTag(parser);
+ return true;
+ }
+
private static String buildClassName(String pkg, CharSequence clsSeq,
String[] outError) {
if (clsSeq == null || clsSeq.length() <= 0) {
@@ -1784,11 +1817,18 @@ public class PackageParser {
false)) {
owner.mRequiredForAllUsers = true;
}
- String accountType = sa.getString(com.android.internal.R.styleable
- .AndroidManifestApplication_restrictedAccountType);
- if (accountType != null && accountType.length() > 0) {
- owner.mRestrictedAccountType = accountType;
- }
+ }
+
+ String restrictedAccountType = sa.getString(com.android.internal.R.styleable
+ .AndroidManifestApplication_restrictedAccountType);
+ if (restrictedAccountType != null && restrictedAccountType.length() > 0) {
+ owner.mRestrictedAccountType = restrictedAccountType;
+ }
+
+ String requiredAccountType = sa.getString(com.android.internal.R.styleable
+ .AndroidManifestApplication_requiredAccountType);
+ if (requiredAccountType != null && requiredAccountType.length() > 0) {
+ owner.mRequiredAccountType = requiredAccountType;
}
if (sa.getBoolean(
@@ -3307,6 +3347,9 @@ public class PackageParser {
/* The restricted account authenticator type that is used by this application */
public String mRestrictedAccountType;
+ /* The required account type without which this application will not function */
+ public String mRequiredAccountType;
+
/**
* Digest suitable for comparing whether this package's manifest is the
* same as another.
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index ffefaa2..fc9e486 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -17,6 +17,7 @@
package android.content.res;
import android.os.ParcelFileDescriptor;
+import android.os.Trace;
import android.util.Log;
import android.util.TypedValue;
@@ -602,7 +603,12 @@ public final class AssetManager {
* the cookie of the added asset, or 0 on failure.
* {@hide}
*/
- public native final int addAssetPath(String path);
+ public final int addAssetPath(String path) {
+ int res = addAssetPathNative(path);
+ return res;
+ }
+
+ private native final int addAssetPathNative(String path);
/**
* Add multiple sets of assets to the asset manager at once. See
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 0152615..d64bff9 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -16,6 +16,7 @@
package android.content.res;
+import android.os.Trace;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -1977,17 +1978,20 @@ public class Resources {
}
}
- private boolean verifyPreloadConfig(TypedValue value, String name) {
- if ((value.changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE
- | ActivityInfo.CONFIG_DENSITY)) != 0) {
+ static private final int VARYING_CONFIGS = ActivityInfo.activityInfoConfigToNative(
+ ActivityInfo.CONFIG_LAYOUT_DIRECTION);
+
+ private boolean verifyPreloadConfig(int changingConfigurations, int resourceId, String name) {
+ if (((changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE |
+ ActivityInfo.CONFIG_DENSITY)) & VARYING_CONFIGS) != 0) {
String resName;
try {
- resName = getResourceName(value.resourceId);
+ resName = getResourceName(resourceId);
} catch (NotFoundException e) {
resName = "?";
}
Log.w(TAG, "Preloaded " + name + " resource #0x"
- + Integer.toHexString(value.resourceId)
+ + Integer.toHexString(resourceId)
+ " (" + resName + ") that varies with configuration!!");
return false;
}
@@ -2052,20 +2056,24 @@ public class Resources {
+ value.assetCookie + ": " + file);
if (file.endsWith(".xml")) {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
try {
XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "drawable");
dr = Drawable.createFromXml(this, rp);
rp.close();
} catch (Exception e) {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
NotFoundException rnf = new NotFoundException(
"File " + file + " from drawable resource ID #0x"
+ Integer.toHexString(id));
rnf.initCause(e);
throw rnf;
}
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
} else {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
try {
InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
@@ -2075,12 +2083,14 @@ public class Resources {
is.close();
// System.out.println("Created stream: " + dr);
} catch (Exception e) {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
NotFoundException rnf = new NotFoundException(
"File " + file + " from drawable resource ID #0x"
+ Integer.toHexString(id));
rnf.initCause(e);
throw rnf;
}
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
}
@@ -2090,7 +2100,7 @@ public class Resources {
cs = dr.getConstantState();
if (cs != null) {
if (mPreloading) {
- if (verifyPreloadConfig(value, "drawable")) {
+ if (verifyPreloadConfig(cs.getChangingConfigurations(), value.resourceId, "drawable")) {
if (isColorDrawable) {
sPreloadedColorDrawables.put(key, cs);
} else {
@@ -2160,7 +2170,7 @@ public class Resources {
csl = ColorStateList.valueOf(value.data);
if (mPreloading) {
- if (verifyPreloadConfig(value, "color")) {
+ if (verifyPreloadConfig(value.changingConfigurations, value.resourceId, "color")) {
sPreloadedColorStateLists.put(key, csl);
}
}
@@ -2186,18 +2196,21 @@ public class Resources {
String file = value.string.toString();
if (file.endsWith(".xml")) {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
try {
XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "colorstatelist");
csl = ColorStateList.createFromXml(this, rp);
rp.close();
} catch (Exception e) {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
NotFoundException rnf = new NotFoundException(
"File " + file + " from color state list resource ID #0x"
+ Integer.toHexString(id));
rnf.initCause(e);
throw rnf;
}
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
} else {
throw new NotFoundException(
"File " + file + " from drawable resource ID #0x"
@@ -2206,7 +2219,7 @@ public class Resources {
if (csl != null) {
if (mPreloading) {
- if (verifyPreloadConfig(value, "color")) {
+ if (verifyPreloadConfig(value.changingConfigurations, value.resourceId, "color")) {
sPreloadedColorStateLists.put(key, csl);
}
} else {
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 41384d2..074f8fe 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -17,6 +17,8 @@
package android.hardware;
+import android.os.Build;
+
/**
* Class representing a sensor. Use {@link SensorManager#getSensorList} to get
* the list of available Sensors.
@@ -114,11 +116,112 @@ public final class Sensor {
/** A constant describing an ambient temperature sensor type */
public static final int TYPE_AMBIENT_TEMPERATURE = 13;
- /**
+ /**
+ * A constant describing a magnetic field uncalibrated sensor type. See
+ * {@link android.hardware.SensorEvent#values SensorEvent.values} for more
+ * details.
+ * <p>
+ * Similar to {@link #TYPE_MAGNETIC_FIELD} but the hard iron calibration (calibration
+ * due to distortions that arise from magnetized iron, steel or permanenet magnets
+ * on the device) is reported separately. No periodic calibration is performed
+ * (i.e. there are no discontinuities in the data stream while using this sensor).
+ * Assumptions that the magnetic field is due to the Earth's poles is avoided.
+ * Factory calibration and temperature compensation are still performed.
+ * </p>
+ */
+ public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14;
+
+ /**
+ * Identical to {@link #TYPE_ROTATION_VECTOR} except that it doesn't
+ * use the geomagnetic field. Therefore the Y axis doesn't
+ * point north, but instead to some other reference, that reference is
+ * allowed to drift by the same order of magnitude as the gyroscope
+ * drift around the Z axis.
+ * <p>
+ * In the ideal case, a phone rotated and returning to the same real-world
+ * orientation should report the same game rotation vector
+ * (without using the earth's geomagnetic field). However, the orientation
+ * may drift somewhat over time.
+ * </p>
+ */
+
+ public static final int TYPE_GAME_ROTATION_VECTOR = 15;
+
+ /**
+ * A constant describing a gyroscope uncalibrated sensor type. See
+ * {@link android.hardware.SensorEvent#values SensorEvent.values} for more
+ * details.
+ * <p>
+ * No gyro-drift compensation is performed.
+ * Factory calibration and temperature compensation is still applied
+ * to the rate of rotation (angular speeds).
+ * </p>
+ */
+ public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16;
+
+ /**
+ * A constant describing the significant motion trigger sensor.
+ * See {@link android.hardware.SensorEvent#values} for more details.
+ * <p>
+ * It triggers when an event occurs and then automatically disables
+ * itself. The sensor continues to operate while the device is asleep
+ * and will automatically wake the device to notify when significant
+ * motion is detected. The application does not need to hold any wake
+ * locks for this sensor to trigger.
+ * </p>
+ */
+ public static final int TYPE_SIGNIFICANT_MOTION = 17;
+
+ /**
* A constant describing all sensor types.
*/
public static final int TYPE_ALL = -1;
+ /* Reporting mode constants for sensors. Each sensor will have exactly one
+ reporting mode associated with it. */
+ // Events are reported at a constant rate.
+ static int REPORTING_MODE_CONTINUOUS = 1;
+
+ // Events are reported only when the value changes.
+ static int REPORTING_MODE_ON_CHANGE = 2;
+
+ // Upon detection of an event, the sensor deactivates itself and then sends a single event.
+ static int REPORTING_MODE_ONE_SHOT = 3;
+
+ // Note: This needs to be updated, whenever a new sensor is added.
+ private static int[] sSensorReportingModes = {
+ REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS,
+ REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS,
+ REPORTING_MODE_ON_CHANGE, REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS,
+ REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ON_CHANGE,
+ REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS,
+ REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ONE_SHOT };
+
+ // Note: This needs to be updated, whenever a new sensor is added.
+ // Holds the maximum length of the values array associated with {@link SensorEvent} or
+ // {@link TriggerEvent} for the Sensor
+ private static int[] sMaxLengthValuesArray = {
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 3, 3,
+ 6, 4, 6, 1 };
+
+ static int getReportingMode(Sensor sensor) {
+ // mType starts from offset 1.
+ return sSensorReportingModes[sensor.mType - 1];
+ }
+
+ static int getMaxLengthValuesArray(Sensor sensor, int sdkLevel) {
+ // mType starts from offset 1.
+ int len = sMaxLengthValuesArray[sensor.mType - 1];
+
+ // RotationVector length has changed to 3 to 5 for API level 18
+ // Set it to 3 for backward compatibility.
+ if (sensor.getType() == Sensor.TYPE_ROTATION_VECTOR &&
+ sdkLevel <= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ len = 3;
+ }
+ return len;
+ }
+
/* Some of these fields are set only by the native bindings in
* SensorManager.
*/
@@ -132,7 +235,6 @@ public final class Sensor {
private float mPower;
private int mMinDelay;
-
Sensor() {
}
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 51a17c1..012ae7e 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -17,11 +17,9 @@
package android.hardware;
/**
- * <p>
* This class represents a {@link android.hardware.Sensor Sensor} event and
* holds informations such as the sensor's type, the time-stamp, accuracy and of
* course the sensor's {@link SensorEvent#values data}.
- * </p>
*
* <p>
* <u>Definition of the coordinate system used by the SensorEvent API.</u>
@@ -67,15 +65,9 @@ public class SensorEvent {
* Sensor.TYPE_ACCELEROMETER}:</h4> All values are in SI units (m/s^2)
*
* <ul>
- * <p>
- * values[0]: Acceleration minus Gx on the x-axis
- * </p>
- * <p>
- * values[1]: Acceleration minus Gy on the y-axis
- * </p>
- * <p>
- * values[2]: Acceleration minus Gz on the z-axis
- * </p>
+ * <li> values[0]: Acceleration minus Gx on the x-axis </li>
+ * <li> values[1]: Acceleration minus Gy on the y-axis </li>
+ * <li> values[2]: Acceleration minus Gz on the z-axis </li>
* </ul>
*
* <p>
@@ -165,15 +157,9 @@ public class SensorEvent {
* definition of positive rotation and does not agree with the definition of
* roll given earlier.
* <ul>
- * <p>
- * values[0]: Angular speed around the x-axis
- * </p>
- * <p>
- * values[1]: Angular speed around the y-axis
- * </p>
- * <p>
- * values[2]: Angular speed around the z-axis
- * </p>
+ * <li> values[0]: Angular speed around the x-axis </li>
+ * <li> values[1]: Angular speed around the y-axis </li>
+ * <li> values[2]: Angular speed around the z-axis </li>
* </ul>
* <p>
* Typically the output of the gyroscope is integrated over time to
@@ -233,22 +219,19 @@ public class SensorEvent {
* </p>
* <h4>{@link android.hardware.Sensor#TYPE_LIGHT Sensor.TYPE_LIGHT}:</h4>
* <ul>
- * <p>
- * values[0]: Ambient light level in SI lux units
+ * <li>values[0]: Ambient light level in SI lux units </li>
* </ul>
*
* <h4>{@link android.hardware.Sensor#TYPE_PRESSURE Sensor.TYPE_PRESSURE}:</h4>
* <ul>
- * <p>
- * values[0]: Atmospheric pressure in hPa (millibar)
+ * <li>values[0]: Atmospheric pressure in hPa (millibar) </li>
* </ul>
*
* <h4>{@link android.hardware.Sensor#TYPE_PROXIMITY Sensor.TYPE_PROXIMITY}:
* </h4>
*
* <ul>
- * <p>
- * values[0]: Proximity sensor distance measured in centimeters
+ * <li>values[0]: Proximity sensor distance measured in centimeters </li>
* </ul>
*
* <p>
@@ -304,39 +287,27 @@ public class SensorEvent {
* </p>
*
* <ul>
- * <p>
- * values[0]: x*sin(&#952/2)
- * </p>
- * <p>
- * values[1]: y*sin(&#952/2)
- * </p>
- * <p>
- * values[2]: z*sin(&#952/2)
- * </p>
- * <p>
- * values[3]: cos(&#952/2) <i>(optional: only if value.length = 4)</i>
- * </p>
+ * <li> values[0]: x*sin(&#952/2) </li>
+ * <li> values[1]: y*sin(&#952/2) </li>
+ * <li> values[2]: z*sin(&#952/2) </li>
+ * <li> values[3]: cos(&#952/2) </li>
+ * <li> values[4]: estimated heading Accuracy (in radians) (-1 if unavailable)</li>
* </ul>
+ * <p> values[3], originally optional, will always be present from SDK Level 18 onwards.
+ * values[4] is a new value that has been added in SDK Level 18.
+ * </p>
*
* <h4>{@link android.hardware.Sensor#TYPE_ORIENTATION
* Sensor.TYPE_ORIENTATION}:</h4> All values are angles in degrees.
*
* <ul>
- * <p>
- * values[0]: Azimuth, angle between the magnetic north direction and the
+ * <li> values[0]: Azimuth, angle between the magnetic north direction and the
* y-axis, around the z-axis (0 to 359). 0=North, 90=East, 180=South,
- * 270=West
- * </p>
- *
- * <p>
- * values[1]: Pitch, rotation around x-axis (-180 to 180), with positive
- * values when the z-axis moves <b>toward</b> the y-axis.
- * </p>
- *
- * <p>
- * values[2]: Roll, rotation around y-axis (-90 to 90), with positive values
- * when the x-axis moves <b>toward</b> the z-axis.
- * </p>
+ * 270=West </li>
+ * <li> values[1]: Pitch, rotation around x-axis (-180 to 180), with positive
+ * values when the z-axis moves <b>toward</b> the y-axis. </li>
+ * <li> values[2]: Roll, rotation around y-axis (-90 to 90), with positive values
+ * when the x-axis moves <b>toward</b> the z-axis. </li>
* </ul>
*
* <p>
@@ -364,9 +335,7 @@ public class SensorEvent {
* <h4>{@link android.hardware.Sensor#TYPE_RELATIVE_HUMIDITY
* Sensor.TYPE_RELATIVE_HUMIDITY}:</h4>
* <ul>
- * <p>
- * values[0]: Relative ambient air humidity in percent
- * </p>
+ * <li> values[0]: Relative ambient air humidity in percent </li>
* </ul>
* <p>
* When relative ambient air humidity and ambient temperature are
@@ -423,21 +392,95 @@ public class SensorEvent {
* </h4>
*
* <ul>
- * <p>
- * values[0]: ambient (room) temperature in degree Celsius.
+ * <li> values[0]: ambient (room) temperature in degree Celsius.</li>
* </ul>
*
* @see SensorEvent
* @see GeomagneticField
+ *
+ * <h4>{@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD_UNCALIBRATED} </h4>
+ * Similar to {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD},
+ * but the hard iron calibration is reported separately instead of being included
+ * in the measurement. Factory calibration and temperature compensation will still
+ * be applied to the "uncalibrated" measurement. Assumptions that the magnetic field
+ * is due to the Earth's poles is avoided.
+ * <p>
+ * The values array is shown below:
+ * <ul>
+ * <li> values[0] = x_uncalib </li>
+ * <li> values[1] = y_uncalib </li>
+ * <li> values[2] = z_uncalib </li>
+ * <li> values[3] = x_bias </li>
+ * <li> values[4] = y_bias </li>
+ * <li> values[5] = z_bias </li>
+ * </ul>
+ * </p>
+ * <p>
+ * x_uncalib, y_uncalib, z_uncalib are the measured magnetic field in X, Y, Z axes.
+ * Soft iron and temperature calibrations are applied. But the hard iron
+ * calibration is not applied. The values are in micro-Tesla (uT).
+ * </p>
+ * <p>
+ * x_bias, y_bias, z_bias give the iron bias estimated in X, Y, Z axes.
+ * Each field is a component of the estimated hard iron calibration.
+ * The values are in micro-Tesla (uT).
+ * </p>
+ * <p> Hard iron - These distortions arise due to the magnetized iron, steel or permanenet
+ * magnets on the device.
+ * Soft iron - These distortions arise due to the interaction with the earth's magentic
+ * field.
+ * </p>
+ * <h4> {@link android.hardware.Sensor#TYPE_GAME_ROTATION_VECTOR} </h4>
+ * Identical to {@link android.hardware.Sensor#TYPE_ROTATION_VECTOR} except that it
+ * doesn't use the geomagnetic field. Therefore the Y axis doesn't
+ * point north, but instead to some other reference, that reference is
+ * allowed to drift by the same order of magnitude as the gyroscope
+ * drift around the Z axis.
+ * <p>
+ * In the ideal case, a phone rotated and returning to the same real-world
+ * orientation will report the same game rotation vector
+ * (without using the earth's geomagnetic field). However, the orientation
+ * may drift somewhat over time. See {@link android.hardware.Sensor#TYPE_ROTATION_VECTOR}
+ * for a detailed description of the values. This sensor will not have
+ * the estimated heading accuracy value.
+ * </p>
+ *
+ * <h4> {@link android.hardware.Sensor#TYPE_GYROSCOPE_UNCALIBRATED} </h4>
+ * All values are in radians/second and measure the rate of rotation
+ * around the X, Y and Z axis. An estimation of the drift on each axis is
+ * reported as well.
+ * <p>
+ * No gyro-drift compensation is performed. Factory calibration and temperature
+ * compensation is still applied to the rate of rotation (angular speeds).
+ * </p>
+ * <p>
+ * The coordinate system is the same as is used for the
+ * {@link android.hardware.Sensor#TYPE_ACCELEROMETER}
+ * Rotation is positive in the counter-clockwise direction (right-hand rule).
+ * That is, an observer looking from some positive location on the x, y or z axis
+ * at a device positioned on the origin would report positive rotation if the device
+ * appeared to be rotating counter clockwise.
+ * The range would at least be 17.45 rad/s (ie: ~1000 deg/s).
+ * <ul>
+ * <li> values[0] : angular speed (w/o drift compensation) around the X axis in rad/s </li>
+ * <li> values[1] : angular speed (w/o drift compensation) around the Y axis in rad/s </li>
+ * <li> values[2] : angular speed (w/o drift compensation) around the Z axis in rad/s </li>
+ * <li> values[3] : estimated drift around X axis in rad/s </li>
+ * <li> values[4] : estimated drift around Y axis in rad/s </li>
+ * <li> values[5] : estimated drift around Z axis in rad/s </li>
+ * </ul>
+ * </p>
+ * <h4></h4>
+ * <h4> Pro Tip: Always use the length of the values array while performing operations
+ * on it. In earlier versions, this used to be always 3 which has changed now. </h4>
*/
-
public final float[] values;
/**
* The sensor that generated this event. See
* {@link android.hardware.SensorManager SensorManager} for details.
*/
- public Sensor sensor;
+ public Sensor sensor;
/**
* The accuracy of this event. See {@link android.hardware.SensorManager
@@ -445,14 +488,12 @@ public class SensorEvent {
*/
public int accuracy;
-
/**
* The time in nanosecond at which the event happened
*/
public long timestamp;
-
- SensorEvent(int size) {
- values = new float[size];
+ SensorEvent(int valueSize) {
+ values = new float[valueSize];
}
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 7f94794..30118f9 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -38,7 +38,11 @@ import java.util.List;
* hours. Note that the system will <i>not</i> disable sensors automatically when
* the screen turns off.
* </p>
- *
+ * <p class="note">
+ * Note: Don't use this mechanism with a Trigger Sensor, have a look
+ * at {@link TriggerEventListener}. {@link Sensor#TYPE_SIGNIFICANT_MOTION}
+ * is an example of a trigger sensor.
+ * </p>
* <pre class="prettyprint">
* public class SensorActivity extends Activity, implements SensorEventListener {
* private final SensorManager mSensorManager;
@@ -515,6 +519,12 @@ public abstract class SensorManager {
/**
* Unregisters a listener for the sensors with which it is registered.
*
+ * <p class="note"></p>
+ * Note: Don't use this method with a one shot trigger sensor such as
+ * {@link Sensor#TYPE_SIGNIFICANT_MOTION}.
+ * Use {@link #cancelTriggerSensor(TriggerEventListener, Sensor)} instead.
+ * </p>
+ *
* @param listener
* a SensorEventListener object
*
@@ -524,6 +534,7 @@ public abstract class SensorManager {
* @see #unregisterListener(SensorEventListener)
* @see #registerListener(SensorEventListener, Sensor, int)
*
+ * @throws IllegalArgumentException when sensor is a trigger sensor.
*/
public void unregisterListener(SensorEventListener listener, Sensor sensor) {
if (listener == null || sensor == null) {
@@ -558,6 +569,12 @@ public abstract class SensorManager {
* Registers a {@link android.hardware.SensorEventListener
* SensorEventListener} for the given sensor.
*
+ * <p class="note"></p>
+ * Note: Don't use this method with a one shot trigger sensor such as
+ * {@link Sensor#TYPE_SIGNIFICANT_MOTION}.
+ * Use {@link #requestTriggerSensor(TriggerEventListener, Sensor)} instead.
+ * </p>
+ *
* @param listener
* A {@link android.hardware.SensorEventListener SensorEventListener}
* object.
@@ -584,6 +601,7 @@ public abstract class SensorManager {
* @see #unregisterListener(SensorEventListener)
* @see #unregisterListener(SensorEventListener, Sensor)
*
+ * @throws IllegalArgumentException when sensor is null or a trigger sensor
*/
public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) {
return registerListener(listener, sensor, rate, null);
@@ -593,6 +611,12 @@ public abstract class SensorManager {
* Registers a {@link android.hardware.SensorEventListener
* SensorEventListener} for the given sensor.
*
+ * <p class="note"></p>
+ * Note: Don't use this method with a one shot trigger sensor such as
+ * {@link Sensor#TYPE_SIGNIFICANT_MOTION}.
+ * Use {@link #requestTriggerSensor(TriggerEventListener, Sensor)} instead.
+ * </p>
+ *
* @param listener
* A {@link android.hardware.SensorEventListener SensorEventListener}
* object.
@@ -623,6 +647,7 @@ public abstract class SensorManager {
* @see #unregisterListener(SensorEventListener)
* @see #unregisterListener(SensorEventListener, Sensor)
*
+ * @throws IllegalArgumentException when sensor is null or a trigger sensor
*/
public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate,
Handler handler) {
@@ -1310,6 +1335,68 @@ public abstract class SensorManager {
Q[3] = rv[2];
}
+ /**
+ * Requests receiving trigger events for a trigger sensor.
+ *
+ * <p>
+ * When the sensor detects a trigger event condition, such as significant motion in
+ * the case of the {@link Sensor#TYPE_SIGNIFICANT_MOTION}, the provided trigger listener
+ * will be invoked once and then its request to receive trigger events will be canceled.
+ * To continue receiving trigger events, the application must request to receive trigger
+ * events again.
+ * </p>
+ *
+ * @param listener The listener on which the
+ * {@link TriggerEventListener#onTrigger(TriggerEvent)} will be delivered.
+ * @param sensor The sensor to be enabled.
+ *
+ * @return true if the sensor was successfully enabled.
+ *
+ * @throws IllegalArgumentException when sensor is null or not a trigger sensor.
+ */
+ public boolean requestTriggerSensor(TriggerEventListener listener, Sensor sensor) {
+ return requestTriggerSensorImpl(listener, sensor);
+ }
+
+ /**
+ * @hide
+ */
+ protected abstract boolean requestTriggerSensorImpl(TriggerEventListener listener,
+ Sensor sensor);
+
+ /**
+ * Cancels receiving trigger events for a trigger sensor.
+ *
+ * <p>
+ * Note that a Trigger sensor will be auto disabled if
+ * {@link TriggerEventListener#onTrigger(TriggerEvent)} has triggered.
+ * This method is provided in case the user wants to explicitly cancel the request
+ * to receive trigger events.
+ * </p>
+ *
+ * @param listener The listener on which the
+ * {@link TriggerEventListener#onTrigger(TriggerEvent)}
+ * is delivered.It should be the same as the one used
+ * in {@link #requestTriggerSensor(TriggerEventListener, Sensor)}
+ * @param sensor The sensor for which the trigger request should be canceled.
+ * If null, it cancels receiving trigger for all sensors associated
+ * with the listener.
+ *
+ * @return true if successfully canceled.
+ *
+ * @throws IllegalArgumentException when sensor is a trigger sensor.
+ */
+ public boolean cancelTriggerSensor(TriggerEventListener listener, Sensor sensor) {
+ return cancelTriggerSensorImpl(listener, sensor, true);
+ }
+
+ /**
+ * @hide
+ */
+ protected abstract boolean cancelTriggerSensorImpl(TriggerEventListener listener,
+ Sensor sensor, boolean disable);
+
+
private LegacySensorManager getLegacySensorManager() {
synchronized (mSensorListByType) {
if (mLegacySensorManager == null) {
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 9591631..852cf4a 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -1,4 +1,4 @@
-/*
+ /*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +16,19 @@
package android.hardware;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.List;
-
-import dalvik.system.CloseGuard;
-
+import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
+import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import dalvik.system.CloseGuard;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
/**
* Sensor manager implementation that communicates with the built-in
@@ -45,22 +46,19 @@ public class SystemSensorManager extends SensorManager {
private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
// Listener list
- private final ArrayList<SensorEventListenerSensorPair> mListenerDelegates = new ArrayList<SensorEventListenerSensorPair>();
-
- // Common pool of sensor events.
- private static SensorEventPool sPool;
+ private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners =
+ new HashMap<SensorEventListener, SensorEventQueue>();
+ private final HashMap<TriggerEventListener, TriggerEventQueue> mTriggerListeners =
+ new HashMap<TriggerEventListener, TriggerEventQueue>();
// Looper associated with the context in which this instance was created.
private final Looper mMainLooper;
-
- // maps a SensorEventListener to a SensorEventQueue
- private final Hashtable<SensorEventListener, SensorEventQueue> mSensorEventQueueMap;
+ private final int mTargetSdkLevel;
/** {@hide} */
- public SystemSensorManager(Looper mainLooper) {
+ public SystemSensorManager(Context context, Looper mainLooper) {
mMainLooper = mainLooper;
- mSensorEventQueueMap = new Hashtable<SensorEventListener, SensorEventQueue>();
-
+ mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion;
synchronized(sSensorModuleLock) {
if (!sSensorModuleInitialized) {
sSensorModuleInitialized = true;
@@ -80,8 +78,6 @@ public class SystemSensorManager extends SensorManager {
sHandleToSensor.append(sensor.getHandle(), sensor);
}
} while (i>0);
-
- sPool = new SensorEventPool( sFullSensorsList.size()*2 );
}
}
}
@@ -102,136 +98,149 @@ public class SystemSensorManager extends SensorManager {
// Invariants to preserve:
// - one Looper per SensorEventListener
// - one Looper per SensorEventQueue
- // We map SensorEventListeners to a SensorEventQueue, which holds the looper
-
- if (sensor == null) throw new NullPointerException("sensor cannot be null");
-
- boolean result;
- synchronized (mSensorEventQueueMap) {
- // check if we already have this SensorEventListener, Sensor pair
- // registered -- if so, we ignore the register. This is not ideal
- // but this is what the implementation has always been doing.
- for (SensorEventListenerSensorPair l : mListenerDelegates) {
- if (l.isSameListenerSensorPair(listener, sensor)) {
- // already added, just return silently.
- return true;
- }
- }
+ // We map SensorEventListener to a SensorEventQueue, which holds the looper
+ if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
- // now find the SensorEventQueue associated to this listener
- SensorEventQueue queue = mSensorEventQueueMap.get(listener);
- if (queue != null) {
- result = queue.addSensor(sensor, delay);
- if (result) {
- // create a new ListenerDelegate for this pair
- mListenerDelegates.add(new SensorEventListenerSensorPair(listener, sensor));
- }
- } else {
+ // Trigger Sensors should use the requestTriggerSensor call.
+ if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) return false;
+
+ synchronized (mSensorListeners) {
+ SensorEventQueue queue = mSensorListeners.get(listener);
+ if (queue == null) {
Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
- queue = new SensorEventQueue(listener, looper.getQueue());
- result = queue.addSensor(sensor, delay);
- if (result) {
- // create a new ListenerDelegate for this pair
- mListenerDelegates.add(new SensorEventListenerSensorPair(listener, sensor));
- mSensorEventQueueMap.put(listener, queue);
- } else {
+ queue = new SensorEventQueue(listener, looper, this);
+ if (!queue.addSensor(sensor, delay)) {
queue.dispose();
+ return false;
}
+ mSensorListeners.put(listener, queue);
+ return true;
+ } else {
+ return queue.addSensor(sensor, delay);
}
}
- return result;
}
/** @hide */
@Override
protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
- synchronized (mSensorEventQueueMap) {
-
- // remove this listener/sensor from our list
- final ArrayList<SensorEventListenerSensorPair> copy =
- new ArrayList<SensorEventListenerSensorPair>(mListenerDelegates);
- int lastIndex = copy.size()-1;
- for (int i=lastIndex ; i>= 0 ; i--) {
- if (copy.get(i).isSameListenerSensorPair(listener, sensor)) {
- mListenerDelegates.remove(i);
- }
- }
+ // Trigger Sensors should use the cancelTriggerSensor call.
+ if (sensor != null && Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) {
+ return;
+ }
- // find the SensorEventQueue associated to this SensorEventListener
- SensorEventQueue queue = mSensorEventQueueMap.get(listener);
+ synchronized (mSensorListeners) {
+ SensorEventQueue queue = mSensorListeners.get(listener);
if (queue != null) {
- if (sensor != null) {
- queue.removeSensor(sensor);
+ boolean result;
+ if (sensor == null) {
+ result = queue.removeAllSensors();
} else {
- queue.removeAllSensors();
+ result = queue.removeSensor(sensor, true);
}
- if (!queue.hasSensors()) {
- mSensorEventQueueMap.remove(listener);
+ if (result && !queue.hasSensors()) {
+ mSensorListeners.remove(listener);
queue.dispose();
}
}
}
}
+ /** @hide */
+ @Override
+ protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
+ if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
- /*
- * ListenerDelegate is essentially a SensorEventListener, Sensor pair
- * and is associated with a single SensorEventQueue.
- */
- private static final class SensorEventListenerSensorPair {
- private final SensorEventListener mSensorEventListener;
- private final Sensor mSensor;
- public SensorEventListenerSensorPair(SensorEventListener listener, Sensor sensor) {
- mSensorEventListener = listener;
- mSensor = sensor;
- }
- public boolean isSameListenerSensorPair(SensorEventListener listener, Sensor sensor) {
- // if sensor is null, we match only on the listener
- if (sensor != null) {
- return (listener == mSensorEventListener) &&
- (sensor.getHandle() == mSensor.getHandle());
+ if (Sensor.getReportingMode(sensor) != Sensor.REPORTING_MODE_ONE_SHOT) return false;
+
+ synchronized (mTriggerListeners) {
+ TriggerEventQueue queue = mTriggerListeners.get(listener);
+ if (queue == null) {
+ queue = new TriggerEventQueue(listener, mMainLooper, this);
+ if (!queue.addSensor(sensor, 0)) {
+ queue.dispose();
+ return false;
+ }
+ mTriggerListeners.put(listener, queue);
+ return true;
} else {
- return (listener == mSensorEventListener);
+ return queue.addSensor(sensor, 0);
}
}
}
+ /** @hide */
+ @Override
+ protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor,
+ boolean disable) {
+ if (sensor != null && Sensor.getReportingMode(sensor) != Sensor.REPORTING_MODE_ONE_SHOT) {
+ return false;
+ }
+ synchronized (mTriggerListeners) {
+ TriggerEventQueue queue = mTriggerListeners.get(listener);
+ if (queue != null) {
+ boolean result;
+ if (sensor == null) {
+ result = queue.removeAllSensors();
+ } else {
+ result = queue.removeSensor(sensor, disable);
+ }
+ if (result && !queue.hasSensors()) {
+ mTriggerListeners.remove(listener);
+ queue.dispose();
+ }
+ return result;
+ }
+ return false;
+ }
+ }
+
/*
- * SensorEventQueue is the communication channel with the sensor service,
- * there is a one-to-one mapping between SensorEventQueue and
- * SensorEventListener.
+ * BaseEventQueue is the communication channel with the sensor service,
+ * SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between
+ * the queues and the listeners.
*/
- private static final class SensorEventQueue {
- private static native int nativeInitSensorEventQueue(SensorEventQueue eventQ, MessageQueue msgQ, float[] scratch);
+ private static abstract class BaseEventQueue {
+ private native int nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ,
+
+ float[] scratch);
private static native int nativeEnableSensor(int eventQ, int handle, int us);
private static native int nativeDisableSensor(int eventQ, int handle);
private static native void nativeDestroySensorEventQueue(int eventQ);
private int nSensorEventQueue;
- private final SensorEventListener mListener;
private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
- private final SparseIntArray mSensorAccuracies = new SparseIntArray();
- private final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
+ protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
+ protected final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
private final CloseGuard mCloseGuard = CloseGuard.get();
private final float[] mScratch = new float[16];
+ protected final SystemSensorManager mManager;
- public SensorEventQueue(SensorEventListener listener, MessageQueue msgQ) {
- nSensorEventQueue = nativeInitSensorEventQueue(this, msgQ, mScratch);
- mListener = listener;
+ BaseEventQueue(Looper looper, SystemSensorManager manager) {
+ nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch);
mCloseGuard.open("dispose");
+ mManager = manager;
}
+
public void dispose() {
dispose(false);
}
public boolean addSensor(Sensor sensor, int delay) {
- if (enableSensor(sensor, delay) == 0) {
- mActiveSensors.put(sensor.getHandle(), true);
- return true;
+ // Check if already present.
+ int handle = sensor.getHandle();
+ if (mActiveSensors.get(handle)) return false;
+
+ // Get ready to receive events before calling enable.
+ mActiveSensors.put(handle, true);
+ addSensorEvent(sensor);
+ if (enableSensor(sensor, delay) != 0) {
+ removeSensor(sensor, false);
+ return false;
}
- return false;
+ return true;
}
- public void removeAllSensors() {
+ public boolean removeAllSensors() {
for (int i=0 ; i<mActiveSensors.size(); i++) {
if (mActiveSensors.valueAt(i) == true) {
int handle = mActiveSensors.keyAt(i);
@@ -239,26 +248,31 @@ public class SystemSensorManager extends SensorManager {
if (sensor != null) {
disableSensor(sensor);
mActiveSensors.put(handle, false);
+ removeSensorEvent(sensor);
} else {
// it should never happen -- just ignore.
}
}
}
+ return true;
}
- public void removeSensor(Sensor sensor) {
+ public boolean removeSensor(Sensor sensor, boolean disable) {
final int handle = sensor.getHandle();
if (mActiveSensors.get(handle)) {
- disableSensor(sensor);
+ if (disable) disableSensor(sensor);
mActiveSensors.put(sensor.getHandle(), false);
+ removeSensorEvent(sensor);
+ return true;
}
+ return false;
}
public boolean hasSensors() {
// no more sensors are set
return mActiveSensors.indexOfValue(true) >= 0;
}
-
+
@Override
protected void finalize() throws Throwable {
try {
@@ -291,95 +305,115 @@ public class SystemSensorManager extends SensorManager {
if (sensor == null) throw new NullPointerException();
return nativeDisableSensor(nSensorEventQueue, sensor.getHandle());
}
+ protected abstract void dispatchSensorEvent(int handle, float[] values, int accuracy,
+ long timestamp);
+
+ protected abstract void addSensorEvent(Sensor sensor);
+ protected abstract void removeSensorEvent(Sensor sensor);
+ }
+
+ static final class SensorEventQueue extends BaseEventQueue {
+ private final SensorEventListener mListener;
+ private final SparseArray<SensorEvent> mSensorsEvents = new SparseArray<SensorEvent>();
+
+ public SensorEventQueue(SensorEventListener listener, Looper looper,
+ SystemSensorManager manager) {
+ super(looper, manager);
+ mListener = listener;
+ }
+
+ public void addSensorEvent(Sensor sensor) {
+ SensorEvent t = new SensorEvent(Sensor.getMaxLengthValuesArray(sensor,
+ mManager.mTargetSdkLevel));
+ mSensorsEvents.put(sensor.getHandle(), t);
+ }
+
+ public void removeSensorEvent(Sensor sensor) {
+ mSensorsEvents.delete(sensor.getHandle());
+ }
// Called from native code.
@SuppressWarnings("unused")
- private void dispatchSensorEvent(int handle, float[] values, int inAccuracy, long timestamp) {
- // this is always called on the same thread.
- final SensorEvent t = sPool.getFromPool();
- try {
- final Sensor sensor = sHandleToSensor.get(handle);
- final SensorEventListener listener = mListener;
- // FIXME: handle more than 3 values
- System.arraycopy(values, 0, t.values, 0, 3);
- t.timestamp = timestamp;
- t.accuracy = inAccuracy;
- t.sensor = sensor;
- switch (t.sensor.getType()) {
- // Only report accuracy for sensors that support it.
- case Sensor.TYPE_MAGNETIC_FIELD:
- case Sensor.TYPE_ORIENTATION:
- // call onAccuracyChanged() only if the value changes
- final int accuracy = mSensorAccuracies.get(handle);
- if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
- mSensorAccuracies.put(handle, t.accuracy);
- listener.onAccuracyChanged(t.sensor, t.accuracy);
- }
- break;
- default:
- // For other sensors, just report the accuracy once
- if (mFirstEvent.get(handle) == false) {
- mFirstEvent.put(handle, true);
- listener.onAccuracyChanged(
- t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
- }
- break;
- }
- listener.onSensorChanged(t);
- } finally {
- sPool.returnToPool(t);
+ @Override
+ protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
+ long timestamp) {
+ final Sensor sensor = sHandleToSensor.get(handle);
+ SensorEvent t = mSensorsEvents.get(handle);
+ if (t == null) {
+ Log.e(TAG, "Error: Sensor Event is null for Sensor: " + sensor);
+ return;
+ }
+ // Copy from the values array.
+ System.arraycopy(values, 0, t.values, 0, t.values.length);
+ t.timestamp = timestamp;
+ t.accuracy = inAccuracy;
+ t.sensor = sensor;
+ switch (t.sensor.getType()) {
+ // Only report accuracy for sensors that support it.
+ case Sensor.TYPE_MAGNETIC_FIELD:
+ case Sensor.TYPE_ORIENTATION:
+ // call onAccuracyChanged() only if the value changes
+ final int accuracy = mSensorAccuracies.get(handle);
+ if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
+ mSensorAccuracies.put(handle, t.accuracy);
+ mListener.onAccuracyChanged(t.sensor, t.accuracy);
+ }
+ break;
+ default:
+ // For other sensors, just report the accuracy once
+ if (mFirstEvent.get(handle) == false) {
+ mFirstEvent.put(handle, true);
+ mListener.onAccuracyChanged(
+ t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
+ }
+ break;
}
+ mListener.onSensorChanged(t);
}
}
- /*
- * A dumb pool of SensorEvent
- */
- private static final class SensorEventPool {
- private final int mPoolSize;
- private final SensorEvent mPool[];
- private int mNumItemsInPool;
-
- private SensorEvent createSensorEvent() {
- // maximal size for all legacy events is 3
- return new SensorEvent(3);
+ static final class TriggerEventQueue extends BaseEventQueue {
+ private final TriggerEventListener mListener;
+ private final SparseArray<TriggerEvent> mTriggerEvents = new SparseArray<TriggerEvent>();
+
+ public TriggerEventQueue(TriggerEventListener listener, Looper looper,
+ SystemSensorManager manager) {
+ super(looper, manager);
+ mListener = listener;
}
- SensorEventPool(int poolSize) {
- mPoolSize = poolSize;
- mNumItemsInPool = poolSize;
- mPool = new SensorEvent[poolSize];
+ public void addSensorEvent(Sensor sensor) {
+ TriggerEvent t = new TriggerEvent(Sensor.getMaxLengthValuesArray(sensor,
+ mManager.mTargetSdkLevel));
+ mTriggerEvents.put(sensor.getHandle(), t);
}
- SensorEvent getFromPool() {
- SensorEvent t = null;
- synchronized (this) {
- if (mNumItemsInPool > 0) {
- // remove the "top" item from the pool
- final int index = mPoolSize - mNumItemsInPool;
- t = mPool[index];
- mPool[index] = null;
- mNumItemsInPool--;
- }
- }
- if (t == null) {
- // the pool was empty or this item was removed from the pool for
- // the first time. In any case, we need to create a new item.
- t = createSensorEvent();
- }
- return t;
+ public void removeSensorEvent(Sensor sensor) {
+ mTriggerEvents.delete(sensor.getHandle());
}
- void returnToPool(SensorEvent t) {
- synchronized (this) {
- // is there space left in the pool?
- if (mNumItemsInPool < mPoolSize) {
- // if so, return the item to the pool
- mNumItemsInPool++;
- final int index = mPoolSize - mNumItemsInPool;
- mPool[index] = t;
- }
+ // Called from native code.
+ @SuppressWarnings("unused")
+ @Override
+ protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
+ long timestamp) {
+ final Sensor sensor = sHandleToSensor.get(handle);
+ TriggerEvent t = mTriggerEvents.get(handle);
+ if (t == null) {
+ Log.e(TAG, "Error: Trigger Event is null for Sensor: " + sensor);
+ return;
}
+
+ // Copy from the values array.
+ System.arraycopy(values, 0, t.values, 0, t.values.length);
+ t.timestamp = timestamp;
+ t.sensor = sensor;
+
+ // A trigger sensor is auto disabled. So just clean up and don't call native
+ // disable.
+ mManager.cancelTriggerSensorImpl(mListener, sensor, false);
+
+ mListener.onTrigger(t);
}
}
}
diff --git a/core/java/android/hardware/TriggerEvent.java b/core/java/android/hardware/TriggerEvent.java
new file mode 100644
index 0000000..bdd39f3
--- /dev/null
+++ b/core/java/android/hardware/TriggerEvent.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/**
+ * This class represents a Trigger Event - the event
+ * associated with a Trigger Sensor. When the sensor detects a trigger
+ * event condition, such as significant motion in the case of the
+ * {@link Sensor#TYPE_SIGNIFICANT_MOTION}, the {@link TriggerEventListener}
+ * is called with the TriggerEvent. The sensor is automatically canceled
+ * after the trigger.
+ * <p>
+ * This class holds information such as the value of the sensor
+ * when the trigger happened, the timestamp along with detailed
+ * information regarding the Sensor itself.
+ * </p>
+ * @see android.hardware.SensorManager
+ * @see android.hardware.TriggerEvent
+ * @see android.hardware.Sensor
+ */
+public final class TriggerEvent {
+ /**
+ * <p>
+ * The length and contents of the {@link #values values} array depends on
+ * which {@link android.hardware.Sensor sensor} type is being monitored (see
+ * also {@link SensorEvent} for a definition of the coordinate system used).
+ * </p>
+ * <h4> {@link Sensor#TYPE_SIGNIFICANT_MOTION} </h4>
+ * The value field is of length 1. value[0] = 1.0 when the sensor triggers.
+ * 1.0 is the only allowed value.
+ */
+ public final float[] values;
+
+ /**
+ * The sensor that generated this event. See
+ * {@link android.hardware.SensorManager SensorManager} for details.
+ */
+ public Sensor sensor;
+
+ /**
+ * The time in nanosecond at which the event happened
+ */
+ public long timestamp;
+
+ TriggerEvent(int size) {
+ values = new float[size];
+ }
+}
diff --git a/core/java/android/hardware/TriggerEventListener.java b/core/java/android/hardware/TriggerEventListener.java
new file mode 100644
index 0000000..76b2796
--- /dev/null
+++ b/core/java/android/hardware/TriggerEventListener.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/**
+ * This class is the listener used to handle Trigger Sensors.
+ * Trigger Sensors are sensors that trigger an event and are automatically
+ * disabled. {@link Sensor#TYPE_SIGNIFICANT_MOTION} is one such example.
+ * <p>
+ * SensorManager lets you access the device's {@link android.hardware.Sensor
+ * sensors}. Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(java.lang.String)
+ * Context.getSystemService()} with the argument
+ * {@link android.content.Context#SENSOR_SERVICE}.
+ * Usage details are explained in the example below.
+ * </p>
+ *
+ * <pre class="prettyprint">
+ * class TriggerListener extends TriggerEventListener {
+ * public void onTrigger(TriggerEvent event) {
+ * // Do Work.
+ *
+ * // As it is a one shot sensor, it will be canceled automatically.
+ * // SensorManager.requestTriggerSensor(this, mSigMotion); needs to
+ * // be called again, if needed.
+ * }
+ * }
+ * public class SensorActivity extends Activity {
+ * private final SensorManager mSensorManager;
+ * private final Sensor mSigMotion;
+ * private final TriggerEventListener mListener = new TriggerEventListener();
+ *
+ * public SensorActivity() {
+ * mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
+ * mSigMotion = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
+ * }
+ *
+ * protected void onResume() {
+ * super.onResume();
+ * mSensorManager.requestTriggerSensor(mListener, mSigMotion);
+ * }
+ *
+ * protected void onPause() {
+ * super.onPause();
+ * // Call disable to ensure that the trigger request has been canceled.
+ * mSensorManager.cancelTriggerSensor(mListener, mSigMotion);
+ * }
+ *
+ * }
+ * </pre>
+ *
+ * @see TriggerEvent
+ * @see Sensor
+ */
+public abstract class TriggerEventListener {
+ /**
+ * The method that will be called when the sensor
+ * is triggered. Override this method in your implementation
+ * of this class.
+ *
+ * @param event The details of the event.
+ */
+ public abstract void onTrigger(TriggerEvent event);
+}
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
new file mode 100644
index 0000000..35bbb9c
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.location;
+
+import android.content.Context;
+import android.location.Location;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * This class handles geofences managed by various hardware subsystems. It contains
+ * the public APIs that is needed to accomplish the task.
+ *
+ * <p>The APIs should not be called directly by the app developers. A higher level api
+ * which abstracts the hardware should be used instead. All the checks are done by the higher
+ * level public API. Any needed locking should be handled by the higher level API.
+ *
+ * <p> There are 3 states associated with a Geofence: Inside, Outside, Unknown.
+ * There are 3 transitions: {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED},
+ * {@link #GEOFENCE_UNCERTAIN}. The APIs only expose the transitions.
+ *
+ * <p> Inside state: The hardware subsystem is reasonably confident that the user is inside
+ * the geofence. Outside state: The hardware subsystem is reasonably confident that the user
+ * is outside the geofence Unknown state: Unknown state can be interpreted as a state in which the
+ * monitoring subsystem isn't confident enough that the user is either inside or
+ * outside the Geofence. If the accuracy does not improve for a sufficient period of time,
+ * the {@link #GEOFENCE_UNCERTAIN} transition would be triggered. If the accuracy improves later,
+ * an appropriate transition would be triggered. The "reasonably confident" parameter
+ * depends on the hardware system and the positioning algorithms used.
+ * For instance, {@link #MONITORING_TYPE_GPS_HARDWARE} uses 95% as a confidence level.
+ */
+public final class GeofenceHardware {
+ private IGeofenceHardware mService;
+
+ // Hardware systems that do geofence monitoring.
+ static final int NUM_MONITORS = 1;
+
+ /**
+ * Constant for geofence monitoring done by the GPS hardware.
+ */
+ public static final int MONITORING_TYPE_GPS_HARDWARE = 0;
+
+ /**
+ * Constant to indiciate that the monitoring system is currently
+ * available for monitoring geofences.
+ */
+ public static final int MONITOR_CURRENTLY_AVAILABLE = 0;
+
+ /**
+ * Constant to indiciate that the monitoring system is currently
+ * unavailable for monitoring geofences.
+ */
+ public static final int MONITOR_CURRENTLY_UNAVAILABLE = 1;
+
+ /**
+ * Constant to indiciate that the monitoring system is unsupported
+ * for hardware geofence monitoring.
+ */
+ public static final int MONITOR_UNSUPPORTED = 2;
+
+ // The following constants need to match geofence flags in gps.h
+ /**
+ * The constant to indicate that the user has entered the geofence.
+ */
+ public static final int GEOFENCE_ENTERED = 1<<0L;
+
+ /**
+ * The constant to indicate that the user has exited the geofence.
+ */
+ public static final int GEOFENCE_EXITED = 1<<1L;
+
+ /**
+ * The constant to indicate that the user is uncertain with respect to a
+ * geofence. nn
+ */
+ public static final int GEOFENCE_UNCERTAIN = 1<<2L;
+
+ /**
+ * The constant used to indicate success of the particular geofence call
+ */
+ public static final int GEOFENCE_SUCCESS = 0;
+
+ /**
+ * The constant used to indicate that too many geofences have been registered.
+ */
+ public static final int GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 1;
+
+ /**
+ * The constant used to indicate that the geofence id already exists.
+ */
+ public static final int GEOFENCE_ERROR_ID_EXISTS = 2;
+
+ /**
+ * The constant used to indicate that the geofence id is unknown.
+ */
+ public static final int GEOFENCE_ERROR_ID_UNKNOWN = 3;
+
+ /**
+ * The constant used to indicate that the transition requested for the geofence is invalid.
+ */
+ public static final int GEOFENCE_ERROR_INVALID_TRANSITION = 4;
+
+ /**
+ * The constant used to indicate that the geofence operation has failed.
+ */
+ public static final int GEOFENCE_FAILURE = 5;
+
+ static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
+ static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
+
+ private HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>
+ mCallbacks = new HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>();
+ /**
+ * @hide
+ */
+ public GeofenceHardware(IGeofenceHardware service) {
+ mService = service;
+ }
+
+ /**
+ * Returns all the hardware geofence monitoring systems and their status.
+ * Status can be one of {@link #MONITOR_CURRENTLY_AVAILABLE},
+ * {@link #MONITOR_CURRENTLY_UNAVAILABLE} or {@link #MONITOR_UNSUPPORTED}
+ *
+ * <p> Some supported hardware monitoring systems might not be available
+ * for monitoring geofences in certain scenarios. For example, when a user
+ * enters a building, the GPS hardware subsystem might not be able monitor
+ * geofences and will change from {@link #MONITOR_CURRENTLY_AVAILABLE} to
+ * {@link #MONITOR_CURRENTLY_UNAVAILABLE}.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * @return An array indexed by the various monitoring types and their status.
+ * An array of length 0 is returned in case of errors.
+ */
+ public int[] getMonitoringTypesAndStatus() {
+ try {
+ return mService.getMonitoringTypesAndStatus();
+ } catch (RemoteException e) {
+ }
+ return new int[0];
+ }
+
+ /**
+ * Creates a circular geofence which is monitored by subsystems in the hardware.
+ *
+ * <p> When the device detects that is has entered, exited or is uncertain
+ * about the area specified by the geofence, the given callback will be called.
+ *
+ * <p> The {@link GeofenceHardwareCallback#onGeofenceChange} callback will be called,
+ * with the following parameters
+ * <ul>
+ * <li> The geofence Id
+ * <li> The location object indicating the last known location.
+ * <li> The transition associated with the geofence. One of
+ * {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
+ * <li> The timestamp when the geofence transition occured.
+ * <li> The monitoring type ({@link #MONITORING_TYPE_GPS_HARDWARE} is one such example)
+ * that was used.
+ * </ul>
+ *
+ * <p> The geofence will be monitored by the subsystem specified by monitoring_type parameter.
+ * The application does not need to hold a wakelock when the monitoring
+ * is being done by the underlying hardware subsystem. If the same geofence Id is being
+ * monitored by two different monitoring systems, the same id can be used for both calls, as
+ * long as the same callback object is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>This API should not be called directly by the app developers. A higher level api
+ * which abstracts the hardware should be used instead. All the checks are done by the higher
+ * level public API. Any needed locking should be handled by the higher level API.
+ *
+ * @param latitude Latitude of the area to be monitored.
+ * @param longitude Longitude of the area to be monitored.
+ * @param radius Radius (in meters) of the area to be monitored.
+ * @param lastTransition The current state of the geofence. Can be one of
+ * {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED},
+ * {@link #GEOFENCE_UNCERTAIN}.
+ * @param monitorTransitions Bitwise OR of {@link #GEOFENCE_ENTERED},
+ * {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
+ * @param notificationResponsivenes Defines the best-effort description
+ * of how soon should the callback be called when the transition
+ * associated with the Geofence is triggered. For instance, if
+ * set to 1000 millseconds with {@link #GEOFENCE_ENTERED},
+ * the callback will be called 1000 milliseconds within entering
+ * the geofence. This parameter is defined in milliseconds.
+ * @param unknownTimer The time limit after which the
+ * {@link #GEOFENCE_UNCERTAIN} transition
+ * should be triggered. This paramter is defined in milliseconds.
+ * @param monitoringType The type of the hardware subsystem that should be used
+ * to monitor the geofence.
+ * @param callback {@link GeofenceHardwareCallback} that will be use to notify the
+ * transition.
+ * @return true on success.
+ */
+ public boolean addCircularFence(int geofenceId, double latitude, double longitude,
+ double radius, int lastTransition,int monitorTransitions, int notificationResponsivenes,
+ int unknownTimer, int monitoringType, GeofenceHardwareCallback callback) {
+ try {
+ return mService.addCircularFence(geofenceId, latitude, longitude, radius,
+ lastTransition, monitorTransitions, notificationResponsivenes, unknownTimer,
+ monitoringType, getCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Removes a geofence added by {@link #addCircularFence} call.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>This API should not be called directly by the app developers. A higher level api
+ * which abstracts the hardware should be used instead. All the checks are done by the higher
+ * level public API. Any needed locking should be handled by the higher level API.
+ *
+ * @param geofenceId The id of the geofence.
+ * @param monitoringType The type of the hardware subsystem that should be used
+ * to monitor the geofence.
+ * @return true on success.
+ */
+ public boolean removeGeofence(int geofenceId, int monitoringType) {
+ try {
+ return mService.removeGeofence(geofenceId, monitoringType);
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Pauses the monitoring of a geofence added by {@link #addCircularFence} call.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>This API should not be called directly by the app developers. A higher level api
+ * which abstracts the hardware should be used instead. All the checks are done by the higher
+ * level public API. Any needed locking should be handled by the higher level API.
+ *
+ * @param geofenceId The id of the geofence.
+ * @param monitoringType The type of the hardware subsystem that should be used
+ * to monitor the geofence.
+ * @return true on success.
+ */
+ public boolean pauseGeofence(int geofenceId, int monitoringType) {
+ try {
+ return mService.pauseGeofence(geofenceId, monitoringType);
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Resumes the monitoring of a geofence added by {@link #pauseGeofence} call.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>This API should not be called directly by the app developers. A higher level api
+ * which abstracts the hardware should be used instead. All the checks are done by the higher
+ * level public API. Any needed locking should be handled by the higher level API.
+ *
+ * @param geofenceId The id of the geofence.
+ * @param monitorTransition Bitwise OR of {@link #GEOFENCE_ENTERED},
+ * {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
+ * @param monitoringType The type of the hardware subsystem that should be used
+ * to monitor the geofence.
+ * @return true on success.
+ */
+ public boolean resumeGeofence(int geofenceId, int monitorTransition, int monitoringType) {
+ try {
+ return mService.resumeGeofence(geofenceId, monitorTransition, monitoringType);
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Register the callback to be notified when the state of a hardware geofence
+ * monitoring system changes. For instance, it can change from
+ * {@link #MONITOR_CURRENTLY_AVAILABLE} to {@link #MONITOR_CURRENTLY_UNAVAILABLE}
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>This API should not be called directly by the app developers. A higher level api
+ * which abstracts the hardware should be used instead. All the checks are done by the higher
+ * level public API. Any needed locking should be handled by the higher level API.
+ *
+ * <p> The same callback object can be used to be informed of geofence transitions
+ * and state changes of the underlying hardware subsystem.
+ *
+ * @param monitoringType Type of the monitor
+ * @param callback Callback that will be called.
+ * @return true on success
+ */
+ public boolean registerForMonitorStateChangeCallback(int monitoringType,
+ GeofenceHardwareCallback callback) {
+ try {
+ return mService.registerForMonitorStateChangeCallback(monitoringType,
+ getCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Unregister the callback that was used with {@link #registerForMonitorStateChangeCallback}
+ * to notify when the state of the hardware geofence monitoring system changes.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>This API should not be called directly by the app developers. A higher level api
+ * which abstracts the hardware should be used instead. All the checks are done by the higher
+ * level public API. Any needed locking should be handled by the higher level API.
+ *
+ * @param monitoringType Type of the monitor
+ * @param callback Callback that will be called.
+ * @return true on success
+ */
+ public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
+ GeofenceHardwareCallback callback) {
+ boolean result = false;
+ try {
+ result = mService.unregisterForMonitorStateChangeCallback(monitoringType,
+ getCallbackWrapper(callback));
+ if (result) removeCallback(callback);
+
+ } catch (RemoteException e) {
+ }
+ return result;
+ }
+
+
+ private void removeCallback(GeofenceHardwareCallback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
+ }
+ }
+
+ private GeofenceHardwareCallbackWrapper getCallbackWrapper(GeofenceHardwareCallback callback) {
+ synchronized (mCallbacks) {
+ GeofenceHardwareCallbackWrapper wrapper = mCallbacks.get(callback);
+ if (wrapper == null) {
+ wrapper = new GeofenceHardwareCallbackWrapper(callback);
+ mCallbacks.put(callback, wrapper);
+ }
+ return wrapper;
+ }
+ }
+
+ class GeofenceHardwareCallbackWrapper extends IGeofenceHardwareCallback.Stub {
+ private WeakReference<GeofenceHardwareCallback> mCallback;
+
+ GeofenceHardwareCallbackWrapper(GeofenceHardwareCallback c) {
+ mCallback = new WeakReference<GeofenceHardwareCallback>(c);
+ }
+
+ public void onMonitoringSystemChange(int monitoringType, boolean available,
+ Location location) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) c.onMonitoringSystemChange(monitoringType, available, location);
+ }
+
+ public void onGeofenceChange(int geofenceId, int transition, Location location,
+ long timestamp, int monitoringType) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) {
+ c.onGeofenceChange(geofenceId, transition, location, timestamp,
+ monitoringType);
+ }
+ }
+
+ public void onGeofenceAdd(int geofenceId, int status) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) c.onGeofenceAdd(geofenceId, status);
+ }
+
+ public void onGeofenceRemove(int geofenceId, int status) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) {
+ c.onGeofenceRemove(geofenceId, status);
+ removeCallback(c);
+ }
+ }
+
+ public void onGeofencePause(int geofenceId, int status) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) c.onGeofencePause(geofenceId, status);
+ }
+
+ public void onGeofenceResume(int geofenceId, int status) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) c.onGeofenceResume(geofenceId, status);
+ }
+ }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareCallback.java b/core/java/android/hardware/location/GeofenceHardwareCallback.java
new file mode 100644
index 0000000..8ab582a
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareCallback.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.location.Location;
+
+/**
+ * The callback class associated with the APIs in {@link GeofenceHardware}
+ */
+public abstract class GeofenceHardwareCallback {
+
+ /**
+ * The callback called when the state of a monitoring system changes.
+ * {@link GeofenceHardware#MONITORING_TYPE_GPS_HARDWARE} is an example of a
+ * monitoring system.
+ *
+ * @param monitoringType The type of the monitoring system.
+ * @param available Indicates whether the system is currently available or not.
+ * @param location The last known location according to the monitoring system.
+ */
+ public void onMonitoringSystemChange(int monitoringType, boolean available, Location location) {
+ }
+
+ /**
+ * The callback called when there is a transition to report for the specific
+ * geofence.
+ *
+ * @param geofenceId The geofence ID of the geofence
+ * @param transition One of {@link GeofenceHardware#GEOFENCE_ENTERED},
+ * {@link GeofenceHardware#GEOFENCE_EXITED}, {@link GeofenceHardware#GEOFENCE_UNCERTAIN}
+ * @param location The last known location according to the monitoring system.
+ * @param timestamp The timestamp (elapsed real time in milliseconds) when the transition was
+ * detected
+ * @param monitoringType Type of the monitoring system.
+ */
+ public void onGeofenceChange(int geofenceId, int transition, Location location,
+ long timestamp, int monitoringType) {
+ }
+
+ /**
+ * The callback called to notify the success or failure of the add call.
+ *
+ * @param geofenceId The ID of the geofence.
+ * @param status One of {@link GeofenceHardware#GEOFENCE_SUCCESS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_ID_EXISTS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_INVALID_TRANSITION},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_TOO_MANY_GEOFENCES},
+ * {@link GeofenceHardware#GEOFENCE_FAILURE}
+ */
+ public void onGeofenceAdd(int geofenceId, int status) {
+ }
+
+ /**
+ * The callback called to notify the success or failure of the remove call.
+ *
+ * @param geofenceId The ID of the geofence.
+ * @param status One of {@link GeofenceHardware#GEOFENCE_SUCCESS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_ID_UNKNOWN},
+ * {@link GeofenceHardware#GEOFENCE_FAILURE}
+ */
+ public void onGeofenceRemove(int geofenceId, int status) {
+ }
+
+ /**
+ * The callback called to notify the success or failure of the pause call.
+ *
+ * @param geofenceId The ID of the geofence.
+ * @param status One of {@link GeofenceHardware#GEOFENCE_SUCCESS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_ID_UNKNOWN},
+ * {@link GeofenceHardware#GEOFENCE_FAILURE}
+ */
+ public void onGeofencePause(int geofenceId, int status) {
+ }
+
+ /**
+ * The callback called to notify the success or failure of the resume call.
+ *
+ * @param geofenceId The ID of the geofence.
+ * @param status One of {@link GeofenceHardware#GEOFENCE_SUCCESS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_ID_UNKNOWN},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_INVALID_TRANSITION},
+ * {@link GeofenceHardware#GEOFENCE_FAILURE}
+ */
+ public void onGeofenceResume(int geofenceId, int status) {
+ }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
new file mode 100644
index 0000000..21f1ea6
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.location.IGpsGeofenceHardware;
+import android.location.Location;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class manages the geofences which are handled by hardware.
+ *
+ * @hide
+ */
+public final class GeofenceHardwareImpl {
+ private static final String TAG = "GeofenceHardwareImpl";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Context mContext;
+ private static GeofenceHardwareImpl sInstance;
+ private PowerManager.WakeLock mWakeLock;
+ private SparseArray<IGeofenceHardwareCallback> mGeofences =
+ new SparseArray<IGeofenceHardwareCallback>();
+ private ArrayList<IGeofenceHardwareCallback>[] mCallbacks =
+ new ArrayList[GeofenceHardware.NUM_MONITORS];
+
+ private IGpsGeofenceHardware mGpsService;
+
+ private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS];
+
+ // mGeofenceHandler message types
+ private static final int GEOFENCE_TRANSITION_CALLBACK = 1;
+ private static final int ADD_GEOFENCE_CALLBACK = 2;
+ private static final int REMOVE_GEOFENCE_CALLBACK = 3;
+ private static final int PAUSE_GEOFENCE_CALLBACK = 4;
+ private static final int RESUME_GEOFENCE_CALLBACK = 5;
+ private static final int ADD_GEOFENCE = 6;
+ private static final int REMOVE_GEOFENCE = 7;
+
+ // mCallbacksHandler message types
+ private static final int GPS_GEOFENCE_STATUS = 1;
+ private static final int CALLBACK_ADD = 2;
+ private static final int CALLBACK_REMOVE = 3;
+
+ // The following constants need to match GpsLocationFlags enum in gps.h
+ private static final int LOCATION_INVALID = 0;
+ private static final int LOCATION_HAS_LAT_LONG = 1;
+ private static final int LOCATION_HAS_ALTITUDE = 2;
+ private static final int LOCATION_HAS_SPEED = 4;
+ private static final int LOCATION_HAS_BEARING = 8;
+ private static final int LOCATION_HAS_ACCURACY = 16;
+
+ // Resolution level constants used for permission checks.
+ // These constants must be in increasing order of finer resolution.
+ private static final int RESOLUTION_LEVEL_NONE = 1;
+ private static final int RESOLUTION_LEVEL_COARSE = 2;
+ private static final int RESOLUTION_LEVEL_FINE = 3;
+
+ // GPS Geofence errors. Should match gps.h constants.
+ private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0;
+ private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100;
+ private static final int GPS_GEOFENCE_ERROR_ID_EXISTS = -101;
+ private static final int GPS_GEOFENCE_ERROR_ID_UNKNOWN = -102;
+ private static final int GPS_GEOFENCE_ERROR_INVALID_TRANSITION = -103;
+ private static final int GPS_GEOFENCE_ERROR_GENERIC = -149;
+
+
+
+ public synchronized static GeofenceHardwareImpl getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new GeofenceHardwareImpl(context);
+ }
+ return sInstance;
+ }
+
+ private GeofenceHardwareImpl(Context context) {
+ mContext = context;
+ // Init everything to unsupported.
+ setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+ GeofenceHardware.MONITOR_UNSUPPORTED);
+
+ }
+
+ private void acquireWakeLock() {
+ if (mWakeLock == null) {
+ PowerManager powerManager =
+ (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ }
+ mWakeLock.acquire();
+ }
+
+ private void releaseWakeLock() {
+ if (mWakeLock.isHeld()) mWakeLock.release();
+ }
+
+ private void updateGpsHardwareAvailability() {
+ //Check which monitors are available.
+ boolean gpsSupported;
+ try {
+ gpsSupported = mGpsService.isHardwareGeofenceSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote Exception calling LocationManagerService");
+ gpsSupported = false;
+ }
+
+ if (gpsSupported) {
+ // Its assumed currently available at startup.
+ // native layer will update later.
+ setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+ GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE);
+ }
+ }
+
+ public void setGpsHardwareGeofence(IGpsGeofenceHardware service) {
+ if (mGpsService == null) {
+ mGpsService = service;
+ updateGpsHardwareAvailability();
+ } else if (service == null) {
+ mGpsService = null;
+ Log.w(TAG, "GPS Geofence Hardware service seems to have crashed");
+ } else {
+ Log.e(TAG, "Error: GpsService being set again.");
+ }
+ }
+
+ public int[] getMonitoringTypesAndStatus() {
+ synchronized (mSupportedMonitorTypes) {
+ return mSupportedMonitorTypes;
+ }
+ }
+
+ public boolean addCircularFence(int geofenceId, double latitude, double longitude,
+ double radius, int lastTransition,int monitorTransitions, int notificationResponsivenes,
+ int unknownTimer, int monitoringType, IGeofenceHardwareCallback callback) {
+ // This API is not thread safe. Operations on the same geofence need to be serialized
+ // by upper layers
+ if (DEBUG) {
+ Log.d(TAG, "addCircularFence: GeofenceId: " + geofenceId + "Latitude: " + latitude +
+ "Longitude: " + longitude + "Radius: " + radius + "LastTransition: "
+ + lastTransition + "MonitorTransition: " + monitorTransitions +
+ "NotificationResponsiveness: " + notificationResponsivenes +
+ "UnKnown Timer: " + unknownTimer + "MonitoringType: " + monitoringType);
+
+ }
+ boolean result;
+ Message m = mGeofenceHandler.obtainMessage(ADD_GEOFENCE, callback);
+ m.arg1 = geofenceId;
+ mGeofenceHandler.sendMessage(m);
+
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ if (mGpsService == null) return false;
+ try {
+ result = mGpsService.addCircularHardwareGeofence(geofenceId, latitude,
+ longitude, radius, lastTransition, monitorTransitions,
+ notificationResponsivenes, unknownTimer);
+ } catch (RemoteException e) {
+ Log.e(TAG, "AddGeofence: Remote Exception calling LocationManagerService");
+ result = false;
+ }
+ break;
+ default:
+ result = false;
+ }
+ if (!result) {
+ m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE);
+ m.arg1 = geofenceId;
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ if (DEBUG) Log.d(TAG, "addCircularFence: Result is: " + result);
+ return result;
+ }
+
+ public boolean removeGeofence(int geofenceId, int monitoringType) {
+ // This API is not thread safe. Operations on the same geofence need to be serialized
+ // by upper layers
+ if (DEBUG) Log.d(TAG, "Remove Geofence: GeofenceId: " + geofenceId);
+ boolean result = false;
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ if (mGpsService == null) return false;
+ try {
+ result = mGpsService.removeHardwareGeofence(geofenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoveGeofence: Remote Exception calling LocationManagerService");
+ result = false;
+ }
+ break;
+ default:
+ result = false;
+ }
+ if (DEBUG) Log.d(TAG, "removeGeofence: Result is: " + result);
+ return result;
+ }
+
+ public boolean pauseGeofence(int geofenceId, int monitoringType) {
+ // This API is not thread safe. Operations on the same geofence need to be serialized
+ // by upper layers
+ if (DEBUG) Log.d(TAG, "Pause Geofence: GeofenceId: " + geofenceId);
+ boolean result;
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ if (mGpsService == null) return false;
+ try {
+ result = mGpsService.pauseHardwareGeofence(geofenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "PauseGeofence: Remote Exception calling LocationManagerService");
+ result = false;
+ }
+ break;
+ default:
+ result = false;
+ }
+ if (DEBUG) Log.d(TAG, "pauseGeofence: Result is: " + result);
+ return result;
+ }
+
+
+ public boolean resumeGeofence(int geofenceId, int monitorTransition, int monitoringType) {
+ // This API is not thread safe. Operations on the same geofence need to be serialized
+ // by upper layers
+ if (DEBUG) Log.d(TAG, "Resume Geofence: GeofenceId: " + geofenceId);
+ boolean result;
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ if (mGpsService == null) return false;
+ try {
+ result = mGpsService.resumeHardwareGeofence(geofenceId, monitorTransition);
+ } catch (RemoteException e) {
+ Log.e(TAG, "ResumeGeofence: Remote Exception calling LocationManagerService");
+ result = false;
+ }
+ break;
+ default:
+ result = false;
+ }
+ if (DEBUG) Log.d(TAG, "resumeGeofence: Result is: " + result);
+ return result;
+ }
+
+ public boolean registerForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback) {
+ Message m = mCallbacksHandler.obtainMessage(CALLBACK_ADD, callback);
+ m.arg1 = monitoringType;
+ mCallbacksHandler.sendMessage(m);
+ return true;
+ }
+
+ public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback) {
+ Message m = mCallbacksHandler.obtainMessage(CALLBACK_REMOVE, callback);
+ m.arg1 = monitoringType;
+ mCallbacksHandler.sendMessage(m);
+ return true;
+ }
+
+ private Location getLocation(int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp) {
+ if (DEBUG) Log.d(TAG, "GetLocation: " + flags + ":" + latitude);
+ Location location = new Location(LocationManager.GPS_PROVIDER);
+ if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ location.setLatitude(latitude);
+ location.setLongitude(longitude);
+ location.setTime(timestamp);
+ // It would be nice to push the elapsed real-time timestamp
+ // further down the stack, but this is still useful
+ location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+ }
+ if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
+ location.setAltitude(altitude);
+ } else {
+ location.removeAltitude();
+ }
+ if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
+ location.setSpeed(speed);
+ } else {
+ location.removeSpeed();
+ }
+ if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
+ location.setBearing(bearing);
+ } else {
+ location.removeBearing();
+ }
+ if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
+ location.setAccuracy(accuracy);
+ } else {
+ location.removeAccuracy();
+ }
+ return location;
+ }
+
+ /**
+ * called from GpsLocationProvider to report geofence transition
+ */
+ public void reportGpsGeofenceTransition(int geofenceId, int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp, int transition, long transitionTimestamp) {
+ if (DEBUG) Log.d(TAG, "GeofenceTransition: Flags: " + flags + " Lat: " + latitude +
+ " Long: " + longitude + " Altitude: " + altitude + " Speed: " + speed + " Bearing: " +
+ bearing + " Accuracy: " + accuracy + " Timestamp: " + timestamp + " Transition: " +
+ transition + " TransitionTimestamp: " + transitionTimestamp);
+ Location location = getLocation(flags, latitude, longitude, altitude, speed, bearing,
+ accuracy, timestamp);
+ GeofenceTransition t = new GeofenceTransition(geofenceId, transition, timestamp, location);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(GEOFENCE_TRANSITION_CALLBACK, t);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider to report GPS status change.
+ */
+ public void reportGpsGeofenceStatus(int status, int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp) {
+ Location location = getLocation(flags, latitude, longitude, altitude, speed, bearing,
+ accuracy, timestamp);
+ boolean available = false;
+ if (status == GeofenceHardware.GPS_GEOFENCE_AVAILABLE) available = true;
+
+ int val = (available ? GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE :
+ GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE);
+ setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, val);
+
+ acquireWakeLock();
+ Message m = mCallbacksHandler.obtainMessage(GPS_GEOFENCE_STATUS, location);
+ m.arg1 = val;
+ mCallbacksHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider add geofence callback.
+ */
+ public void reportGpsGeofenceAddStatus(int geofenceId, int status) {
+ if (DEBUG) Log.d(TAG, "Add Callback: GPS : Id: " + geofenceId + " Status: " + status);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(ADD_GEOFENCE_CALLBACK);
+ m.arg1 = geofenceId;
+ m.arg2 = getGeofenceStatus(status);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider remove geofence callback.
+ */
+ public void reportGpsGeofenceRemoveStatus(int geofenceId, int status) {
+ if (DEBUG) Log.d(TAG, "Remove Callback: GPS : Id: " + geofenceId + " Status: " + status);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE_CALLBACK);
+ m.arg1 = geofenceId;
+ m.arg2 = getGeofenceStatus(status);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider pause geofence callback.
+ */
+ public void reportGpsGeofencePauseStatus(int geofenceId, int status) {
+ if (DEBUG) Log.d(TAG, "Pause Callback: GPS : Id: " + geofenceId + " Status: " + status);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(PAUSE_GEOFENCE_CALLBACK);
+ m.arg1 = geofenceId;
+ m.arg2 = getGeofenceStatus(status);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider resume geofence callback.
+ */
+ public void reportGpsGeofenceResumeStatus(int geofenceId, int status) {
+ if (DEBUG) Log.d(TAG, "Resume Callback: GPS : Id: " + geofenceId + " Status: " + status);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(RESUME_GEOFENCE_CALLBACK);
+ m.arg1 = geofenceId;
+ m.arg2 = getGeofenceStatus(status);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ // All operations on mGeofences
+ private Handler mGeofenceHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ int geofenceId;
+ int status;
+ IGeofenceHardwareCallback callback;
+ switch (msg.what) {
+ case ADD_GEOFENCE:
+ geofenceId = msg.arg1;
+ callback = (IGeofenceHardwareCallback) msg.obj;
+ mGeofences.put(geofenceId, callback);
+ break;
+ case REMOVE_GEOFENCE:
+ geofenceId = msg.arg1;
+ mGeofences.remove(geofenceId);
+ break;
+ case ADD_GEOFENCE_CALLBACK:
+ geofenceId = msg.arg1;
+ callback = mGeofences.get(geofenceId);
+ if (callback == null) return;
+
+ try {
+ callback.onGeofenceAdd(geofenceId, msg.arg2);
+ } catch (RemoteException e) {Log.i(TAG, "Remote Exception:" + e);}
+ releaseWakeLock();
+ break;
+ case REMOVE_GEOFENCE_CALLBACK:
+ geofenceId = msg.arg1;
+ callback = mGeofences.get(geofenceId);
+ if (callback == null) return;
+
+ try {
+ callback.onGeofenceRemove(geofenceId, msg.arg2);
+ } catch (RemoteException e) {}
+ mGeofences.remove(geofenceId);
+ releaseWakeLock();
+ break;
+
+ case PAUSE_GEOFENCE_CALLBACK:
+ geofenceId = msg.arg1;
+ callback = mGeofences.get(geofenceId);
+ if (callback == null) return;
+
+ try {
+ callback.onGeofencePause(geofenceId, msg.arg2);
+ } catch (RemoteException e) {}
+ releaseWakeLock();
+ break;
+
+ case RESUME_GEOFENCE_CALLBACK:
+ geofenceId = msg.arg1;
+ callback = mGeofences.get(geofenceId);
+ if (callback == null) return;
+
+ try {
+ callback.onGeofenceResume(geofenceId, msg.arg2);
+ } catch (RemoteException e) {}
+ releaseWakeLock();
+ break;
+
+ case GEOFENCE_TRANSITION_CALLBACK:
+ GeofenceTransition geofenceTransition = (GeofenceTransition)(msg.obj);
+ callback = mGeofences.get(geofenceTransition.mGeofenceId);
+
+ if (DEBUG) Log.d(TAG, "GeofenceTransistionCallback: GPS : GeofenceId: " +
+ geofenceTransition.mGeofenceId +
+ "Transition: " + geofenceTransition.mTransition +
+ "Location: " + geofenceTransition.mLocation + ":" + mGeofences);
+
+ try {
+ callback.onGeofenceChange(
+ geofenceTransition.mGeofenceId, geofenceTransition.mTransition,
+ geofenceTransition.mLocation, geofenceTransition.mTimestamp,
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE);
+ } catch (RemoteException e) {}
+ releaseWakeLock();
+ break;
+ }
+ }
+ };
+
+ // All operations on mCallbacks
+ private Handler mCallbacksHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ int monitoringType;
+ ArrayList<IGeofenceHardwareCallback> callbackList;
+ IGeofenceHardwareCallback callback;
+
+ switch (msg.what) {
+ case GPS_GEOFENCE_STATUS:
+ Location location = (Location) msg.obj;
+ int val = msg.arg1;
+ boolean available;
+ available = (val == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE ?
+ true : false);
+ callbackList = mCallbacks[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE];
+ if (callbackList == null) return;
+
+ if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available);
+
+ for (IGeofenceHardwareCallback c: callbackList) {
+ try {
+ c.onMonitoringSystemChange(
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, available,
+ location);
+ } catch (RemoteException e) {}
+ }
+ releaseWakeLock();
+ break;
+ case CALLBACK_ADD:
+ monitoringType = msg.arg1;
+ callback = (IGeofenceHardwareCallback) msg.obj;
+ callbackList = mCallbacks[monitoringType];
+ if (callbackList == null) {
+ callbackList = new ArrayList<IGeofenceHardwareCallback>();
+ mCallbacks[monitoringType] = callbackList;
+ }
+ if (!callbackList.contains(callback)) callbackList.add(callback);
+ break;
+ case CALLBACK_REMOVE:
+ monitoringType = msg.arg1;
+ callback = (IGeofenceHardwareCallback) msg.obj;
+ callbackList = mCallbacks[monitoringType];
+ if (callbackList != null) {
+ callbackList.remove(callback);
+ }
+ break;
+ }
+ }
+ };
+
+ private class GeofenceTransition {
+ private int mGeofenceId, mTransition;
+ private long mTimestamp;
+ private Location mLocation;
+
+ GeofenceTransition(int geofenceId, int transition, long timestamp, Location location) {
+ mGeofenceId = geofenceId;
+ mTransition = transition;
+ mTimestamp = timestamp;
+ mLocation = location;
+ }
+ }
+
+ private void setMonitorAvailability(int monitor, int val) {
+ synchronized (mSupportedMonitorTypes) {
+ mSupportedMonitorTypes[monitor] = val;
+ }
+ }
+
+
+ int getMonitoringResolutionLevel(int monitoringType) {
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ return RESOLUTION_LEVEL_FINE;
+ }
+ return RESOLUTION_LEVEL_NONE;
+ }
+
+ int getAllowedResolutionLevel(int pid, int uid) {
+ if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
+ pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return RESOLUTION_LEVEL_FINE;
+ } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return RESOLUTION_LEVEL_COARSE;
+ } else {
+ return RESOLUTION_LEVEL_NONE;
+ }
+ }
+
+ private int getGeofenceStatus(int status) {
+ switch (status) {
+ case GPS_GEOFENCE_OPERATION_SUCCESS:
+ return GeofenceHardware.GEOFENCE_SUCCESS;
+ case GPS_GEOFENCE_ERROR_GENERIC:
+ return GeofenceHardware.GEOFENCE_FAILURE;
+ case GPS_GEOFENCE_ERROR_ID_EXISTS:
+ return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
+ case GPS_GEOFENCE_ERROR_INVALID_TRANSITION:
+ return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
+ case GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES:
+ return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
+ case GPS_GEOFENCE_ERROR_ID_UNKNOWN:
+ return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
+ }
+ return -1;
+ }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareService.java b/core/java/android/hardware/location/GeofenceHardwareService.java
new file mode 100644
index 0000000..0eccee6
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareService.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.Manifest;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.location.IGpsGeofenceHardware;
+import android.os.Binder;
+import android.os.IBinder;
+
+/**
+ * Service that handles hardware geofencing.
+ *
+ * @hide
+ */
+public class GeofenceHardwareService extends Service {
+ private GeofenceHardwareImpl mGeofenceHardwareImpl;
+ private Context mContext;
+
+ @Override
+ public void onCreate() {
+ mContext = this;
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ return false;
+ }
+
+ @Override
+ public void onDestroy() {
+ mGeofenceHardwareImpl = null;
+ }
+
+
+ private void checkPermission(int pid, int uid, int monitoringType) {
+ if (mGeofenceHardwareImpl.getAllowedResolutionLevel(pid, uid) <
+ mGeofenceHardwareImpl.getMonitoringResolutionLevel(monitoringType)) {
+ throw new SecurityException("Insufficient permissions to access hardware geofence for"
+ + " type: " + monitoringType);
+ }
+ }
+
+ private IBinder mBinder = new IGeofenceHardware.Stub() {
+ public void setGpsGeofenceHardware(IGpsGeofenceHardware service) {
+ mGeofenceHardwareImpl.setGpsHardwareGeofence(service);
+ }
+
+ public int[] getMonitoringTypesAndStatus() {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ return mGeofenceHardwareImpl.getMonitoringTypesAndStatus();
+ }
+
+ public boolean addCircularFence(int id, double lat, double longitude, double radius,
+ int lastTransition, int monitorTransitions, int
+ notificationResponsiveness, int unknownTimer, int monitoringType,
+ IGeofenceHardwareCallback callback) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.addCircularFence(id, lat, longitude, radius,
+ lastTransition, monitorTransitions, notificationResponsiveness, unknownTimer,
+ monitoringType, callback);
+ }
+
+ public boolean removeGeofence(int id, int monitoringType) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.removeGeofence(id, monitoringType);
+ }
+
+ public boolean pauseGeofence(int id, int monitoringType) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.pauseGeofence(id, monitoringType);
+ }
+
+ public boolean resumeGeofence(int id, int monitorTransitions, int monitoringType) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.resumeGeofence(id, monitorTransitions, monitoringType);
+ }
+
+ public boolean registerForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.registerForMonitorStateChangeCallback(monitoringType,
+ callback);
+ }
+
+ public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.unregisterForMonitorStateChangeCallback(monitoringType,
+ callback);
+ }
+ };
+}
diff --git a/core/java/android/hardware/location/IGeofenceHardware.aidl b/core/java/android/hardware/location/IGeofenceHardware.aidl
new file mode 100644
index 0000000..4ba02b8
--- /dev/null
+++ b/core/java/android/hardware/location/IGeofenceHardware.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENS E-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.location.IGpsGeofenceHardware;
+import android.hardware.location.IGeofenceHardwareCallback;
+
+/** @hide */
+interface IGeofenceHardware {
+ void setGpsGeofenceHardware(in IGpsGeofenceHardware service);
+ int[] getMonitoringTypesAndStatus();
+ boolean addCircularFence(int id, double lat, double longitude, double radius,
+ int lastTransition, int monitorTransitions, int notificationResponsiveness,
+ int unknownTimer, int monitoringType, in IGeofenceHardwareCallback callback);
+ boolean removeGeofence(int id, int monitoringType);
+ boolean pauseGeofence(int id, int monitoringType);
+ boolean resumeGeofence(int id, int monitorTransitions, int monitoringType);
+ boolean registerForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback);
+ boolean unregisterForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback);
+}
diff --git a/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl b/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl
new file mode 100644
index 0000000..678fc49
--- /dev/null
+++ b/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.location.Location;
+
+/** @hide */
+oneway interface IGeofenceHardwareCallback {
+ void onMonitoringSystemChange(int monitoringType, boolean available, in Location location);
+ void onGeofenceChange(int geofenceId, int transition, in Location location,
+ long timestamp, int monitoringType);
+ void onGeofenceAdd(int geofenceId, int status);
+ void onGeofenceRemove(int geofenceId, int status);
+ void onGeofencePause(int geofenceId, int status);
+ void onGeofenceResume(int geofenceId, int status);
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 4e4980d..78bf9af 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1136,7 +1136,7 @@ public class ConnectivityManager {
* HTTP proxy. A {@code null} value will clear the global HTTP proxy.
*
* <p>This method requires the call to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
* {@hide}
*/
public void setGlobalProxy(ProxyProperties p) {
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index cc3c5f7..1d051dd 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -60,6 +60,7 @@ public class RouteInfo implements Parcelable {
private final boolean mIsDefault;
private final boolean mIsHost;
+ private final boolean mHasGateway;
/**
* Constructs a RouteInfo object.
@@ -97,6 +98,8 @@ public class RouteInfo implements Parcelable {
gateway = Inet6Address.ANY;
}
}
+ mHasGateway = (!gateway.isAnyLocalAddress());
+
mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(),
destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength());
mGateway = gateway;
@@ -171,6 +174,10 @@ public class RouteInfo implements Parcelable {
return mIsHost;
}
+ public boolean hasGateway() {
+ return mHasGateway;
+ }
+
public String toString() {
String val = "";
if (mDestination != null) val = mDestination.toString();
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 16b4835..e9e7551 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -152,7 +152,15 @@ public class Binder implements IBinder {
* not return until the current process is exiting.
*/
public static final native void joinThreadPool();
-
+
+ /**
+ * Returns true if the specified interface is a proxy.
+ * @hide
+ */
+ public static final boolean isProxy(IInterface iface) {
+ return iface.asBinder() != iface;
+ }
+
/**
* Default constructor initializes the object.
*/
diff --git a/core/java/android/os/LatencyTimer.java b/core/java/android/os/LatencyTimer.java
deleted file mode 100644
index ed2f0f9e..0000000
--- a/core/java/android/os/LatencyTimer.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import android.util.Log;
-
-import java.util.HashMap;
-
-/**
- * A class to help with measuring latency in your code.
- *
- * Suggested usage:
- * 1) Instanciate a LatencyTimer as a class field.
- * private [static] LatencyTimer mLt = new LatencyTimer(100, 1000);
- * 2) At various points in the code call sample with a string and the time delta to some fixed time.
- * The string should be unique at each point of the code you are measuring.
- * mLt.sample("before processing event", System.nanoTime() - event.getEventTimeNano());
- * processEvent(event);
- * mLt.sample("after processing event ", System.nanoTime() - event.getEventTimeNano());
- *
- * @hide
- */
-public final class LatencyTimer
-{
- final String TAG = "LatencyTimer";
- final int mSampleSize;
- final int mScaleFactor;
- volatile HashMap<String, long[]> store = new HashMap<String, long[]>();
-
- /**
- * Creates a LatencyTimer object
- * @param sampleSize number of samples to collect before printing out the average
- * @param scaleFactor divisor used to make each sample smaller to prevent overflow when
- * (sampleSize * average sample value)/scaleFactor > Long.MAX_VALUE
- */
- public LatencyTimer(int sampleSize, int scaleFactor) {
- if (scaleFactor == 0) {
- scaleFactor = 1;
- }
- mScaleFactor = scaleFactor;
- mSampleSize = sampleSize;
- }
-
- /**
- * Add a sample delay for averaging.
- * @param tag string used for printing out the result. This should be unique at each point of
- * this called.
- * @param delta time difference from an unique point of reference for a particular iteration
- */
- public void sample(String tag, long delta) {
- long[] array = getArray(tag);
-
- // array[mSampleSize] holds the number of used entries
- final int index = (int) array[mSampleSize]++;
- array[index] = delta;
- if (array[mSampleSize] == mSampleSize) {
- long totalDelta = 0;
- for (long d : array) {
- totalDelta += d/mScaleFactor;
- }
- array[mSampleSize] = 0;
- Log.i(TAG, tag + " average = " + totalDelta / mSampleSize);
- }
- }
-
- private long[] getArray(String tag) {
- long[] data = store.get(tag);
- if (data == null) {
- synchronized(store) {
- data = store.get(tag);
- if (data == null) {
- data = new long[mSampleSize + 1];
- store.put(tag, data);
- data[mSampleSize] = 0;
- }
- }
- }
- return data;
- }
-}
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 38f4d5e..fa28765 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -192,9 +192,25 @@ public final class Looper {
}
/**
+ * Returns true if the current thread is this looper's thread.
+ * @hide
+ */
+ public boolean isCurrentThread() {
+ return Thread.currentThread() == mThread;
+ }
+
+ /**
* Quits the looper.
- *
- * Causes the {@link #loop} method to terminate as soon as possible.
+ * <p>
+ * Causes the {@link #loop} method to terminate as soon as all remaining messages
+ * in the message queue that are already due to be delivered have been handled.
+ * However delayed messages with due times in the future may not be handled before
+ * the loop terminates.
+ * </p><p>
+ * Any attempt to post messages to the queue after {@link #quit} has been called
+ * will fail. For example, the {@link Handler#sendMessage(Message)} method will
+ * return false when the looper is being terminated.
+ * </p>
*/
public void quit() {
mQueue.quit();
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index e0d40c9..c058bfc 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -132,11 +132,6 @@ public final class MessageQueue {
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
- if (mQuiting) {
- dispose();
- return null;
- }
-
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
@@ -170,6 +165,12 @@ public final class MessageQueue {
nextPollTimeoutMillis = -1;
}
+ // Process the quit message now that all pending messages have been handled.
+ if (mQuiting) {
+ dispose();
+ return null;
+ }
+
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index facab4c..476b4ea 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -166,11 +166,6 @@ public class Process {
public static final int LAST_SHARED_APPLICATION_GID = 59999;
/**
- * Defines a secondary group id for access to the bluetooth hardware.
- */
- public static final int BLUETOOTH_GID = 2000;
-
- /**
* Standard priority of application threads.
* Use with {@link #setThreadPriority(int)} and
* {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 27ed6b6..617f490 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -19,41 +19,55 @@ package android.os;
import android.util.Log;
/**
- * Writes trace events to the kernel trace buffer. These trace events can be
- * collected using the "atrace" program for offline analysis.
+ * Writes trace events to the system trace buffer. These trace events can be
+ * collected and visualized using the Systrace tool.
*
* This tracing mechanism is independent of the method tracing mechanism
* offered by {@link Debug#startMethodTracing}. In particular, it enables
- * tracing of events that occur across processes.
- *
- * @hide
+ * tracing of events that occur across multiple processes.
*/
public final class Trace {
+ /*
+ * Writes trace events to the kernel trace buffer. These trace events can be
+ * collected using the "atrace" program for offline analysis.
+ */
+
private static final String TAG = "Trace";
// These tags must be kept in sync with system/core/include/cutils/trace.h.
+ /** @hide */
public static final long TRACE_TAG_NEVER = 0;
+ /** @hide */
public static final long TRACE_TAG_ALWAYS = 1L << 0;
+ /** @hide */
public static final long TRACE_TAG_GRAPHICS = 1L << 1;
+ /** @hide */
public static final long TRACE_TAG_INPUT = 1L << 2;
+ /** @hide */
public static final long TRACE_TAG_VIEW = 1L << 3;
+ /** @hide */
public static final long TRACE_TAG_WEBVIEW = 1L << 4;
+ /** @hide */
public static final long TRACE_TAG_WINDOW_MANAGER = 1L << 5;
+ /** @hide */
public static final long TRACE_TAG_ACTIVITY_MANAGER = 1L << 6;
+ /** @hide */
public static final long TRACE_TAG_SYNC_MANAGER = 1L << 7;
+ /** @hide */
public static final long TRACE_TAG_AUDIO = 1L << 8;
+ /** @hide */
public static final long TRACE_TAG_VIDEO = 1L << 9;
+ /** @hide */
public static final long TRACE_TAG_CAMERA = 1L << 10;
+ /** @hide */
public static final long TRACE_TAG_HAL = 1L << 11;
- private static final long TRACE_TAG_NOT_READY = 1L << 63;
+ /** @hide */
+ public static final long TRACE_TAG_APP = 1L << 12;
+ /** @hide */
+ public static final long TRACE_TAG_RESOURCES = 1L << 13;
- public static final int TRACE_FLAGS_START_BIT = 1;
- public static final String[] TRACE_TAGS = {
- "Graphics", "Input", "View", "WebView", "Window Manager",
- "Activity Manager", "Sync Manager", "Audio", "Video", "Camera", "HAL",
- };
-
- public static final String PROPERTY_TRACE_TAG_ENABLEFLAGS = "debug.atrace.tags.enableflags";
+ private static final long TRACE_TAG_NOT_READY = 1L << 63;
+ private static final int MAX_SECTION_NAME_LEN = 127;
// Must be volatile to avoid word tearing.
private static volatile long sEnabledTags = TRACE_TAG_NOT_READY;
@@ -62,6 +76,9 @@ public final class Trace {
private static native void nativeTraceCounter(long tag, String name, int value);
private static native void nativeTraceBegin(long tag, String name);
private static native void nativeTraceEnd(long tag);
+ private static native void nativeAsyncTraceBegin(long tag, String name, int cookie);
+ private static native void nativeAsyncTraceEnd(long tag, String name, int cookie);
+ private static native void nativeSetAppTracingAllowed(boolean allowed);
static {
// We configure two separate change callbacks, one in Trace.cpp and one here. The
@@ -111,6 +128,8 @@ public final class Trace {
*
* @param traceTag The trace tag to check.
* @return True if the trace tag is valid.
+ *
+ * @hide
*/
public static boolean isTagEnabled(long traceTag) {
long tags = sEnabledTags;
@@ -126,6 +145,8 @@ public final class Trace {
* @param traceTag The trace tag.
* @param counterName The counter name to appear in the trace.
* @param counterValue The counter value.
+ *
+ * @hide
*/
public static void traceCounter(long traceTag, String counterName, int counterValue) {
if (isTagEnabled(traceTag)) {
@@ -134,11 +155,28 @@ public final class Trace {
}
/**
- * Writes a trace message to indicate that a given method has begun.
- * Must be followed by a call to {@link #traceEnd} using the same tag.
+ * Set whether application tracing is allowed for this process. This is intended to be set
+ * once at application start-up time based on whether the application is debuggable.
+ *
+ * @hide
+ */
+ public static void setAppTracingAllowed(boolean allowed) {
+ nativeSetAppTracingAllowed(allowed);
+
+ // Setting whether app tracing is allowed may change the tags, so we update the cached
+ // tags here.
+ cacheEnabledTags();
+ }
+
+ /**
+ * Writes a trace message to indicate that a given section of code has
+ * begun. Must be followed by a call to {@link #traceEnd} using the same
+ * tag.
*
* @param traceTag The trace tag.
* @param methodName The method name to appear in the trace.
+ *
+ * @hide
*/
public static void traceBegin(long traceTag, String methodName) {
if (isTagEnabled(traceTag)) {
@@ -151,10 +189,81 @@ public final class Trace {
* Must be called exactly once for each call to {@link #traceBegin} using the same tag.
*
* @param traceTag The trace tag.
+ *
+ * @hide
*/
public static void traceEnd(long traceTag) {
if (isTagEnabled(traceTag)) {
nativeTraceEnd(traceTag);
}
}
+
+ /**
+ * Writes a trace message to indicate that a given section of code has
+ * begun. Must be followed by a call to {@link #asyncTraceEnd} using the same
+ * tag. Unlike {@link #traceBegin(long, String)} and {@link #traceEnd(long)},
+ * asynchronous events do not need to be nested. The name and cookie used to
+ * begin an event must be used to end it.
+ *
+ * @param traceTag The trace tag.
+ * @param methodName The method name to appear in the trace.
+ * @param cookie Unique identifier for distinguishing simultaneous events
+ *
+ * @hide
+ */
+ public static void asyncTraceBegin(long traceTag, String methodName, int cookie) {
+ if (isTagEnabled(traceTag)) {
+ nativeAsyncTraceBegin(traceTag, methodName, cookie);
+ }
+ }
+
+ /**
+ * Writes a trace message to indicate that the current method has ended.
+ * Must be called exactly once for each call to {@link #asyncTraceBegin(long, String, int)}
+ * using the same tag, name and cookie.
+ *
+ * @param traceTag The trace tag.
+ * @param methodName The method name to appear in the trace.
+ * @param cookie Unique identifier for distinguishing simultaneous events
+ *
+ * @hide
+ */
+ public static void asyncTraceEnd(long traceTag, String methodName, int cookie) {
+ if (isTagEnabled(traceTag)) {
+ nativeAsyncTraceEnd(traceTag, methodName, cookie);
+ }
+ }
+
+ /**
+ * Writes a trace message to indicate that a given section of code has begun. This call must
+ * be followed by a corresponding call to {@link #endSection()} on the same thread.
+ *
+ * <p class="note"> At this time the vertical bar character '|', newline character '\n', and
+ * null character '\0' are used internally by the tracing mechanism. If sectionName contains
+ * these characters they will be replaced with a space character in the trace.
+ *
+ * @param sectionName The name of the code section to appear in the trace. This may be at
+ * most 127 Unicode code units long.
+ */
+ public static void beginSection(String sectionName) {
+ if (isTagEnabled(TRACE_TAG_APP)) {
+ if (sectionName.length() > MAX_SECTION_NAME_LEN) {
+ throw new IllegalArgumentException("sectionName is too long");
+ }
+ nativeTraceBegin(TRACE_TAG_APP, sectionName);
+ }
+ }
+
+ /**
+ * Writes a trace message to indicate that a given section of code has ended. This call must
+ * be preceeded by a corresponding call to {@link #beginSection(String)}. Calling this method
+ * will mark the end of the most recently begun section of code, so care must be taken to
+ * ensure that beginSection / endSection pairs are properly nested and called from the same
+ * thread.
+ */
+ public static void endSection() {
+ if (isTagEnabled(TRACE_TAG_APP)) {
+ nativeTraceEnd(TRACE_TAG_APP);
+ }
+ }
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b9b8f08..e580e2b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -110,7 +110,6 @@ public class UserManager {
*/
public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
-
/**
* Key for user restrictions. Specifies if a user is disallowed from transferring files over
* USB. The default value is <code>false</code>.
@@ -121,6 +120,26 @@ public class UserManager {
*/
public static final String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
+ /**
+ * Key for user restrictions. Specifies if a user is disallowed from configuring user
+ * credentials. The default value is <code>false</code>.
+ * <p/>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
+
+ /**
+ * Key for user restrictions. Specifies if a user is disallowed from removing users.
+ * The default value is <code>false</code>.
+ * <p/>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_REMOVE_USER = "no_remove_user";
+
private static UserManager sInstance = null;
public synchronized static UserManager get(Context context) {
@@ -179,16 +198,15 @@ public class UserManager {
}
/**
- * Used to check if the user making this call is a restricted user. Restricted users may have
- * application restrictions imposed on them. All apps should default to the most restrictive
- * version, unless they have specific restrictions available through a call to
- * {@link Context#getApplicationRestrictions()}.
+ * Used to check if the user making this call is linked to another user. Linked users may have
+ * a reduced number of available apps, app restrictions and account restrictions.
+ * @return whether the user making this call is a linked user
*/
- public boolean isUserRestricted() {
+ public boolean isLinkedUser() {
try {
return mService.isRestricted();
} catch (RemoteException re) {
- Log.w(TAG, "Could not check if user restricted ", re);
+ Log.w(TAG, "Could not check if user is limited ", re);
return false;
}
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 66083c8..c41c35e 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -35,9 +35,7 @@ import android.database.Cursor;
import android.database.DatabaseUtils;
import android.graphics.Rect;
import android.net.Uri;
-import android.os.Bundle;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Pair;
@@ -939,6 +937,15 @@ public final class ContactsContract {
* its row id changed as a result of a sync or aggregation.
*/
public static final String LOOKUP_KEY = "lookup";
+
+ /**
+ * Timestamp (milliseconds since epoch) of when this contact was last updated. This
+ * includes updates to all data associated with this contact including raw contacts. Any
+ * modification (including deletes and inserts) of underlying contact data are also
+ * reflected in this timestamp.
+ */
+ public static final String CONTACT_LAST_UPDATED_TIMESTAMP =
+ "contact_last_updated_timestamp";
}
/**
@@ -2113,6 +2120,56 @@ public final class ContactsContract {
return id >= Profile.MIN_ID;
}
+ protected interface DeletedContactsColumns {
+
+ /**
+ * A reference to the {@link ContactsContract.Contacts#_ID} that was deleted.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String CONTACT_ID = "contact_id";
+
+ /**
+ * Time (milliseconds since epoch) that the contact was deleted.
+ */
+ public static final String CONTACT_DELETED_TIMESTAMP = "contact_deleted_timestamp";
+ }
+
+ /**
+ * Constants for the deleted contact table. This table holds a log of deleted contacts.
+ * <p>
+ * Log older than {@link #DAYS_KEPT_MILLISECONDS} may be deleted.
+ */
+ public static final class DeletedContacts implements DeletedContactsColumns {
+
+ /**
+ * This utility class cannot be instantiated
+ */
+ private DeletedContacts() {
+ }
+
+ /**
+ * The content:// style URI for this table, which requests a directory of raw contact rows
+ * matching the selection criteria.
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI,
+ "deleted_contacts");
+
+ /**
+ * Number of days that the delete log will be kept. After this time, delete records may be
+ * deleted.
+ *
+ * @hide
+ */
+ private static final int DAYS_KEPT = 30;
+
+ /**
+ * Milliseconds that the delete log will be kept. After this time, delete records may be
+ * deleted.
+ */
+ public static final long DAYS_KEPT_MILLISECONDS = 1000L * 60L * 60L * 24L * (long)DAYS_KEPT;
+ }
+
+
protected interface RawContactsColumns {
/**
* A reference to the {@link ContactsContract.Contacts#_ID} that this
@@ -3796,50 +3853,11 @@ public final class ContactsContract {
* Columns in the Data_Usage_Stat table
*/
protected interface DataUsageStatColumns {
- /** What the referenced {@link Data} was used for.
- * @see DataUsageStatColumns#USAGE_TYPE_CALL
- * @see DataUsageStatColumns#USAGE_TYPE_LONG_TEXT
- * @see DataUsageStatColumns#USAGE_TYPE_SHORT_TEXT
- */
- public static final String USAGE_TYPE = "usage_type";
-
/** The last time (in milliseconds) this {@link Data} was used. */
public static final String LAST_TIME_USED = "last_time_used";
- /** The number of times the referenced {@link Data} has been used for the purpose described
- * in {@link DataUsageStatColumns#USAGE_TYPE}.
- */
+ /** The number of times the referenced {@link Data} has been used. */
public static final String TIMES_USED = "times_used";
-
- /**
- * Integer value for USAGE_TYPE.
- * This type of usage refers to voice interaction, which includes phone calls, voice chat,
- * and video chat.
- *
- * @see DataUsageFeedback#USAGE_TYPE
- * @see DataUsageStatColumns#USAGE_TYPE
- */
- public static final int USAGE_TYPE_CALL = 0;
-
- /**
- * Integer value for USAGE_TYPE.
- * This type of usage refers to text interaction involving longer messages, which includes
- * email.
- *
- * @see DataUsageFeedback#USAGE_TYPE
- * @see DataUsageStatColumns#USAGE_TYPE
- */
- public static final int USAGE_TYPE_LONG_TEXT = 1;
-
- /**
- * Integer value for USAGE_TYPE.
- * This type of usage for text interaction involving shorter messages, which includes SMS
- * and text chat with email addresses.
- *
- * @see DataUsageFeedback#USAGE_TYPE
- * @see DataUsageStatColumns#USAGE_TYPE
- */
- public static final int USAGE_TYPE_SHORT_TEXT = 2;
}
/**
@@ -7948,6 +7966,13 @@ public final class ContactsContract {
"android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED";
/**
+ * This is the intent that is fired when the contacts database is created. <p> The
+ * READ_CONTACT permission is required to receive these broadcasts.
+ */
+ public static final String CONTACTS_DATABASE_CREATED =
+ "android.provider.Contacts.DATABASE_CREATED";
+
+ /**
* Starts an Activity that lets the user pick a contact to attach an image to.
* After picking the contact it launches the image cropper in face detection mode.
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 03ee9eb..88ee414 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -655,6 +655,22 @@ public final class Settings {
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS";
+ /**
+ * Activity Action: Show Notification listener settings.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ * @see android.service.notification.NotificationListenerService
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS
+ = "android.settings.NOTIFICATION_LISTENER_SETTINGS";
+
// End of Intent actions for Settings
/**
@@ -5398,6 +5414,20 @@ public final class Settings {
public static final String CERT_PIN_UPDATE_METADATA_URL = "cert_pin_metadata_url";
/**
+ * URL for intent firewall updates
+ * @hide
+ */
+ public static final String INTENT_FIREWALL_UPDATE_CONTENT_URL =
+ "intent_firewall_content_url";
+
+ /**
+ * URL for intent firewall update metadata
+ * @hide
+ */
+ public static final String INTENT_FIREWALL_UPDATE_METADATA_URL =
+ "intent_firewall_metadata_url";
+
+ /**
* SELinux enforcement status. If 0, permissive; if 1, enforcing.
* @hide
*/
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index e1cc90e..3d75dc8 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -78,7 +78,7 @@ public interface IKeystoreService extends IInterface {
return _result;
}
- public int insert(String name, byte[] item, int uid) throws RemoteException {
+ public int insert(String name, byte[] item, int uid, int flags) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
@@ -87,6 +87,7 @@ public interface IKeystoreService extends IInterface {
_data.writeString(name);
_data.writeByteArray(item);
_data.writeInt(uid);
+ _data.writeInt(flags);
mRemote.transact(Stub.TRANSACTION_insert, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -243,7 +244,7 @@ public interface IKeystoreService extends IInterface {
return _result;
}
- public int generate(String name, int uid) throws RemoteException {
+ public int generate(String name, int uid, int flags) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
@@ -251,6 +252,7 @@ public interface IKeystoreService extends IInterface {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeInt(uid);
+ _data.writeInt(flags);
mRemote.transact(Stub.TRANSACTION_generate, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -261,7 +263,8 @@ public interface IKeystoreService extends IInterface {
return _result;
}
- public int import_key(String name, byte[] data, int uid) throws RemoteException {
+ public int import_key(String name, byte[] data, int uid, int flags)
+ throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
@@ -270,6 +273,7 @@ public interface IKeystoreService extends IInterface {
_data.writeString(name);
_data.writeByteArray(data);
_data.writeInt(uid);
+ _data.writeInt(flags);
mRemote.transact(Stub.TRANSACTION_import, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -538,7 +542,7 @@ public interface IKeystoreService extends IInterface {
public byte[] get(String name) throws RemoteException;
- public int insert(String name, byte[] item, int uid) throws RemoteException;
+ public int insert(String name, byte[] item, int uid, int flags) throws RemoteException;
public int del(String name, int uid) throws RemoteException;
@@ -556,9 +560,9 @@ public interface IKeystoreService extends IInterface {
public int zero() throws RemoteException;
- public int generate(String name, int uid) throws RemoteException;
+ public int generate(String name, int uid, int flags) throws RemoteException;
- public int import_key(String name, byte[] data, int uid) throws RemoteException;
+ public int import_key(String name, byte[] data, int uid, int flags) throws RemoteException;
public byte[] sign(String name, byte[] data) throws RemoteException;
diff --git a/core/java/android/app/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index f010a2a..425fdc1 100644
--- a/core/java/android/app/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.app;
+package android.service.notification;
-import com.android.internal.statusbar.StatusBarNotification;
+import android.service.notification.StatusBarNotification;
/** @hide */
oneway interface INotificationListener
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
new file mode 100644
index 0000000..86bab2a
--- /dev/null
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import android.annotation.SdkConstant;
+import android.app.INotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+public abstract class NotificationListenerService extends Service {
+ // TAG = "NotificationListenerService[MySubclass]"
+ private final String TAG = NotificationListenerService.class.getSimpleName()
+ + "[" + getClass().getSimpleName() + "]";
+
+ private INotificationListenerWrapper mWrapper = null;
+
+ private INotificationManager mNoMan;
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE
+ = "android.service.notification.NotificationListenerService";
+
+ /**
+ * Implement this method to learn about new notifications as they are posted by apps.
+ *
+ * @param sbn A data structure encapsulating the original {@link android.app.Notification}
+ * object as well as its identifying information (tag and id) and source
+ * (package name).
+ */
+ public abstract void onNotificationPosted(StatusBarNotification sbn);
+
+ /**
+ * Implement this method to learn when notifications are removed.
+ * <P>
+ * This might occur because the user has dismissed the notification using system UI (or another
+ * notification listener) or because the app has withdrawn the notification.
+ *
+ * @param sbn A data structure encapsulating the original {@link android.app.Notification}
+ * object as well as its identifying information (tag and id) and source
+ * (package name).
+ */
+ public abstract void onNotificationRemoved(StatusBarNotification sbn);
+
+ private final INotificationManager getNotificationInterface() {
+ if (mNoMan == null) {
+ mNoMan = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ }
+ return mNoMan;
+ }
+
+ /**
+ * Inform the notification manager about dismissal of a single notification.
+ * <p>
+ * Use this if your listener has a user interface that allows the user to dismiss individual
+ * notifications, similar to the behavior of Android's status bar and notification panel.
+ * It should be called after the user dismisses a single notification using your UI;
+ * upon being informed, the notification manager will actually remove the notification
+ * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
+ * <P>
+ * <b>Note:</b> If your listener allows the user to fire a notification's
+ * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
+ * this method at that time <i>if</i> the Notification in question has the
+ * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
+ *
+ * @param pkg Package of the notifying app.
+ * @param tag Tag of the notification as specified by the notifying app in
+ * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
+ * @param id ID of the notification as specified by the notifying app in
+ * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
+ */
+ public final void clearNotification(String pkg, String tag, int id) {
+ try {
+ getNotificationInterface().clearNotificationFromListener(mWrapper, pkg, tag, id);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ /**
+ * Inform the notification manager about dismissal of all notifications.
+ * <p>
+ * Use this if your listener has a user interface that allows the user to dismiss all
+ * notifications, similar to the behavior of Android's status bar and notification panel.
+ * It should be called after the user invokes the "dismiss all" function of your UI;
+ * upon being informed, the notification manager will actually remove all active notifications
+ * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks.
+ *
+ * {@see #clearNotification(String, String, int)}
+ */
+ public final void clearAllNotifications() {
+ try {
+ getNotificationInterface().clearAllNotificationsFromListener(mWrapper);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (mWrapper == null) {
+ mWrapper = new INotificationListenerWrapper();
+ }
+ return mWrapper;
+ }
+
+ private class INotificationListenerWrapper extends INotificationListener.Stub {
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn) {
+ NotificationListenerService.this.onNotificationPosted(sbn);
+ }
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn) {
+ NotificationListenerService.this.onNotificationRemoved(sbn);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.aidl b/core/java/android/service/notification/StatusBarNotification.aidl
index bd9e89c..ba81972 100644
--- a/core/java/com/android/internal/statusbar/StatusBarNotification.aidl
+++ b/core/java/android/service/notification/StatusBarNotification.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.statusbar;
+package android.service.notification;
parcelable StatusBarNotification;
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 23e87fc..ef5f8c4 100644
--- a/core/java/com/android/internal/statusbar/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.statusbar;
+package android.service.notification;
import android.app.Notification;
import android.os.Parcel;
@@ -23,34 +23,54 @@ import android.os.UserHandle;
/**
* Class encapsulating a Notification. Sent by the NotificationManagerService to clients including
- * the IStatusBar (in System UI).
+ * the status bar and any {@link android.service.notification.NotificationListenerService}s.
*/
public class StatusBarNotification implements Parcelable {
+ /** The package of the app that posted the notification. */
public final String pkg;
- public final String basePkg;
+ /** The id supplied to {@link android.app.NotificationManager#notify}. */
public final int id;
+ /** The tag supplied to {@link android.app.NotificationManager#notify}, or null if no tag
+ * was specified. */
public final String tag;
+
+ /** The notifying app's calling uid. @hide */
public final int uid;
+ /** The notifying app's base package. @hide */
+ public final String basePkg;
+ /** @hide */
public final int initialPid;
// TODO: make this field private and move callers to an accessor that
// ensures sourceUser is applied.
+
+ /** The {@link android.app.Notification} supplied to
+ * {@link android.app.NotificationManager#notify}. */
public final Notification notification;
- public final int score;
+ /** The {@link android.os.UserHandle} for whom this notification is intended. */
public final UserHandle user;
+ /** The time (in {@link System#currentTimeMillis} time) the notification was posted,
+ * which may be different than {@link android.app.Notification#when}.
+ */
public final long postTime;
- /** This is temporarily needed for the JB MR1 PDK. */
+ /** @hide */
+ public final int score;
+
+ /** This is temporarily needed for the JB MR1 PDK.
+ * @hide */
@Deprecated
public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score,
Notification notification) {
this(pkg, id, tag, uid, initialPid, score, notification, UserHandle.OWNER);
}
+ /** @hide */
public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score,
Notification notification, UserHandle user) {
this(pkg, null, id, tag, uid, initialPid, score, notification, user);
}
+ /** @hide */
public StatusBarNotification(String pkg, String basePkg, int id, String tag, int uid,
int initialPid, int score, Notification notification, UserHandle user) {
this(pkg, basePkg, id, tag, uid, initialPid, score, notification, user,
@@ -147,10 +167,17 @@ public class StatusBarNotification implements Parcelable {
this.score, this.notification);
}
+ /** Convenience method to check the notification's flags for
+ * {@link Notification#FLAG_ONGOING_EVENT}.
+ */
public boolean isOngoing() {
return (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
}
+ /** Convenience method to check the notification's flags for
+ * either {@link Notification#FLAG_ONGOING_EVENT} or
+ * {@link Notification#FLAG_NO_CLEAR}.
+ */
public boolean isClearable() {
return ((notification.flags & Notification.FLAG_ONGOING_EVENT) == 0)
&& ((notification.flags & Notification.FLAG_NO_CLEAR) == 0);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 71d8fb6..5db8168 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -762,7 +762,7 @@ public abstract class WallpaperService extends Service {
mWindowToken = wrapper.mWindowToken;
mSurfaceHolder.setSizeFromLayout();
mInitializing = true;
- mSession = WindowManagerGlobal.getWindowSession(getMainLooper());
+ mSession = WindowManagerGlobal.getWindowSession();
mWindow.setSession(mSession);
diff --git a/core/java/android/text/bidi/BidiFormatter.java b/core/java/android/text/bidi/BidiFormatter.java
index c5a77a6..7a08213 100644
--- a/core/java/android/text/bidi/BidiFormatter.java
+++ b/core/java/android/text/bidi/BidiFormatter.java
@@ -273,6 +273,14 @@ public final class BidiFormatter {
private final TextDirectionHeuristic mDefaultTextDirectionHeuristic;
/**
+ * Factory for creating an instance of BidiFormatter for the default locale directionality.
+ *
+ */
+ public static BidiFormatter getInstance() {
+ return new Builder().build();
+ }
+
+ /**
* Factory for creating an instance of BidiFormatter given the context directionality.
*
* @param rtlContext Whether the context directionality is RTL.
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 7918823..8308459 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -451,10 +451,8 @@ public abstract class HardwareRenderer {
* @param attachInfo AttachInfo tied to the specified view.
* @param callbacks Callbacks invoked when drawing happens.
* @param dirty The dirty rectangle to update, can be null.
- *
- * @return true if the dirty rect was ignored, false otherwise
*/
- abstract boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
+ abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
Rect dirty);
/**
@@ -992,11 +990,7 @@ public abstract class HardwareRenderer {
mCanvas = createCanvas();
mCanvas.setName(mName);
}
- if (mCanvas != null) {
- setEnabled(true);
- } else {
- Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created");
- }
+ setEnabled(true);
}
return mCanvas != null;
@@ -1340,7 +1334,7 @@ public abstract class HardwareRenderer {
}
@Override
- boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
+ void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
Rect dirty) {
if (canDraw()) {
if (!hasDirtyRegions()) {
@@ -1374,7 +1368,7 @@ public abstract class HardwareRenderer {
callbacks.onHardwarePreDraw(canvas);
if (displayList != null) {
- status = drawDisplayList(attachInfo, canvas, displayList, status);
+ status |= drawDisplayList(attachInfo, canvas, displayList, status);
} else {
// Shouldn't reach here
view.draw(canvas);
@@ -1401,11 +1395,8 @@ public abstract class HardwareRenderer {
}
attachInfo.mIgnoreDirtyState = false;
- return dirty == null;
}
}
-
- return false;
}
private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index a797176..40ee1ad 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -54,6 +54,7 @@ public final class InputChannel implements Parcelable {
private native void nativeTransferTo(InputChannel other);
private native void nativeReadFromParcel(Parcel parcel);
private native void nativeWriteToParcel(Parcel parcel);
+ private native void nativeDup(InputChannel target);
private native String nativeGetName();
@@ -64,7 +65,7 @@ public final class InputChannel implements Parcelable {
*/
public InputChannel() {
}
-
+
@Override
protected void finalize() throws Throwable {
try {
@@ -125,6 +126,15 @@ public final class InputChannel implements Parcelable {
nativeTransferTo(outParameter);
}
+ /**
+ * Duplicates the input channel.
+ */
+ public InputChannel dup() {
+ InputChannel target = new InputChannel();
+ nativeDup(target);
+ return target;
+ }
+
@Override
public int describeContents() {
return Parcelable.CONTENTS_FILE_DESCRIPTOR;
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index dd523d2..2595ee5 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -371,8 +371,8 @@ public final class InputDevice implements Parcelable {
if (axis < 0) {
break;
}
- addMotionRange(axis, in.readInt(),
- in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat());
+ addMotionRange(axis, in.readInt(), in.readFloat(), in.readFloat(), in.readFloat(),
+ in.readFloat(), in.readFloat());
}
}
@@ -584,8 +584,8 @@ public final class InputDevice implements Parcelable {
// Called from native code.
private void addMotionRange(int axis, int source,
- float min, float max, float flat, float fuzz) {
- mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz));
+ float min, float max, float flat, float fuzz, float resolution) {
+ mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz, resolution));
}
/**
@@ -625,14 +625,17 @@ public final class InputDevice implements Parcelable {
private float mMax;
private float mFlat;
private float mFuzz;
+ private float mResolution;
- private MotionRange(int axis, int source, float min, float max, float flat, float fuzz) {
+ private MotionRange(int axis, int source, float min, float max, float flat, float fuzz,
+ float resolution) {
mAxis = axis;
mSource = source;
mMin = min;
mMax = max;
mFlat = flat;
mFuzz = fuzz;
+ mResolution = resolution;
}
/**
@@ -711,6 +714,14 @@ public final class InputDevice implements Parcelable {
public float getFuzz() {
return mFuzz;
}
+
+ /**
+ * Gets the resolution for input device measurements with respect to this axis.
+ * @return The resolution in units per millimeter, or units per radian for rotational axes.
+ */
+ public float getResolution() {
+ return mResolution;
+ }
}
@Override
@@ -734,6 +745,7 @@ public final class InputDevice implements Parcelable {
out.writeFloat(range.mMax);
out.writeFloat(range.mFlat);
out.writeFloat(range.mFuzz);
+ out.writeFloat(range.mResolution);
}
out.writeInt(-1);
}
@@ -788,6 +800,7 @@ public final class InputDevice implements Parcelable {
description.append(" max=").append(range.mMax);
description.append(" flat=").append(range.mFlat);
description.append(" fuzz=").append(range.mFuzz);
+ description.append(" resolution=").append(range.mResolution);
description.append("\n");
}
return description.toString();
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 26a5b26..aa43bad 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -19,6 +19,7 @@ package android.view;
import android.graphics.Canvas;
import android.os.Handler;
import android.os.Message;
+import android.os.Trace;
import android.widget.FrameLayout;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -423,6 +424,8 @@ public abstract class LayoutInflater {
*/
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
+
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
@@ -520,6 +523,8 @@ public abstract class LayoutInflater {
mConstructorArgs[1] = null;
}
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+
return result;
}
}
@@ -547,6 +552,8 @@ public abstract class LayoutInflater {
Class<? extends View> clazz = null;
try {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
+
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = mContext.getClassLoader().loadClass(
@@ -615,6 +622,8 @@ public abstract class LayoutInflater {
+ (clazz == null ? "<unknown>" : clazz.getName()));
ie.initCause(e);
throw ie;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
@@ -645,7 +654,7 @@ public abstract class LayoutInflater {
/**
* Version of {@link #onCreateView(String, AttributeSet)} that also
- * takes the future parent of the view being constructure. The default
+ * takes the future parent of the view being constructed. The default
* implementation simply calls {@link #onCreateView(String, AttributeSet)}.
*
* @param parent The future parent of the returned view. <em>Note that
diff --git a/core/java/android/view/SimulatedDpad.java b/core/java/android/view/SimulatedDpad.java
deleted file mode 100644
index c889328..0000000
--- a/core/java/android/view/SimulatedDpad.java
+++ /dev/null
@@ -1,298 +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;
-
-import android.app.SearchManager;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.util.Log;
-
-/**
- * This class creates DPAD events from TouchNavigation events.
- *
- * @see ViewRootImpl
- */
-
-//TODO: Make this class an internal class of ViewRootImpl.java
-class SimulatedDpad {
-
- private static final String TAG = "SimulatedDpad";
-
- // Maximum difference in milliseconds between the down and up of a touch
- // event for it to be considered a tap
- // TODO:Read this value from a configuration file
- private static final int MAX_TAP_TIME = 250;
- // Where the cutoff is for determining an edge swipe
- private static final float EDGE_SWIPE_THRESHOLD = 0.9f;
- private static final int MSG_FLICK = 313;
- // TODO: Pass touch slop from the input device
- private static final int TOUCH_SLOP = 30;
- // The position of the previous TouchNavigation event
- private float mLastTouchNavigationXPosition;
- private float mLastTouchNavigationYPosition;
- // Where the Touch Navigation was initially pressed
- private float mTouchNavigationEnterXPosition;
- private float mTouchNavigationEnterYPosition;
- // When the most recent ACTION_HOVER_ENTER occurred
- private long mLastTouchNavigationStartTimeMs = 0;
- // When the most recent direction key was sent
- private long mLastTouchNavigationKeySendTimeMs = 0;
- // When the most recent touch event of any type occurred
- private long mLastTouchNavigationEventTimeMs = 0;
- // Did the swipe begin in a valid region
- private boolean mEdgeSwipePossible;
-
- private final Context mContext;
-
- // How quickly keys were sent;
- private int mKeySendRateMs = 0;
- private int mLastKeySent;
- // Last movement in device screen pixels
- private float mLastMoveX = 0;
- private float mLastMoveY = 0;
- // Offset from the initial touch. Gets reset as direction keys are sent.
- private float mAccumulatedX;
- private float mAccumulatedY;
-
- // Change in position allowed during tap events
- private float mTouchSlop;
- private float mTouchSlopSquared;
- // Has the TouchSlop constraint been invalidated
- private boolean mAlwaysInTapRegion = true;
-
- // Information from the most recent event.
- // Used to determine what device sent the event during a fling.
- private int mLastSource;
- private int mLastMetaState;
- private int mLastDeviceId;
-
- // TODO: Currently using screen dimensions tuned to a Galaxy Nexus, need to
- // read this from a config file instead
- private int mDistancePerTick;
- private int mDistancePerTickSquared;
- // Highest rate that the flinged events can occur at before dying out
- private int mMaxRepeatDelay;
- // The square of the minimum distance needed for a flick to register
- private int mMinFlickDistanceSquared;
- // How quickly the repeated events die off
- private float mFlickDecay;
-
- public SimulatedDpad(Context context) {
- mDistancePerTick = SystemProperties.getInt("persist.vr_dist_tick", 64);
- mDistancePerTickSquared = mDistancePerTick * mDistancePerTick;
- mMaxRepeatDelay = SystemProperties.getInt("persist.vr_repeat_delay", 300);
- mMinFlickDistanceSquared = SystemProperties.getInt("persist.vr_min_flick", 20);
- mMinFlickDistanceSquared *= mMinFlickDistanceSquared;
- mFlickDecay = Float.parseFloat(SystemProperties.get(
- "persist.sys.vr_flick_decay", "1.3"));
- mTouchSlop = TOUCH_SLOP;
- mTouchSlopSquared = mTouchSlop * mTouchSlop;
-
- mContext = context;
- }
-
- private final Handler mHandler = new Handler(true /*async*/) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_FLICK: {
- final long time = SystemClock.uptimeMillis();
- ViewRootImpl viewroot = (ViewRootImpl) msg.obj;
- // Send the key
- viewroot.enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, msg.arg2, 0, mLastMetaState,
- mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
- viewroot.enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, msg.arg2, 0, mLastMetaState,
- mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
-
- // Increase the delay by the decay factor and resend
- final int delay = (int) Math.ceil(mFlickDecay * msg.arg1);
- if (delay <= mMaxRepeatDelay) {
- Message msgCopy = Message.obtain(msg);
- msgCopy.arg1 = delay;
- msgCopy.setAsynchronous(true);
- mHandler.sendMessageDelayed(msgCopy, delay);
- }
- break;
- }
- }
- }
- };
-
- public void updateTouchNavigation(ViewRootImpl viewroot, MotionEvent event,
- boolean synthesizeNewKeys) {
- if (!synthesizeNewKeys) {
- mHandler.removeMessages(MSG_FLICK);
- }
- InputDevice device = event.getDevice();
- if (device == null) {
- return;
- }
- // Store what time the TouchNavigation event occurred
- final long time = SystemClock.uptimeMillis();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mLastTouchNavigationStartTimeMs = time;
- mAlwaysInTapRegion = true;
- mTouchNavigationEnterXPosition = event.getX();
- mTouchNavigationEnterYPosition = event.getY();
- mAccumulatedX = 0;
- mAccumulatedY = 0;
- mLastMoveX = 0;
- mLastMoveY = 0;
- if (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
- * EDGE_SWIPE_THRESHOLD < event.getY()) {
- // Did the swipe begin in a valid region
- mEdgeSwipePossible = true;
- }
- // Clear any flings
- if (synthesizeNewKeys) {
- mHandler.removeMessages(MSG_FLICK);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- // Determine whether the move is slop or an intentional move
- float deltaX = event.getX() - mTouchNavigationEnterXPosition;
- float deltaY = event.getY() - mTouchNavigationEnterYPosition;
- if (mTouchSlopSquared < deltaX * deltaX + deltaY * deltaY) {
- mAlwaysInTapRegion = false;
- }
- // Checks if the swipe has crossed the midpoint
- // and if our swipe gesture is complete
- if (event.getY() < (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
- * .5) && mEdgeSwipePossible) {
- mEdgeSwipePossible = false;
-
- Intent intent =
- ((SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, false, UserHandle.USER_CURRENT_OR_SELF);
- if (intent != null) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e){
- Log.e(TAG, "Could not start search activity");
- }
- } else {
- Log.e(TAG, "Could not find a search activity");
- }
- }
- // Find the difference in position between the two most recent
- // TouchNavigation events
- mLastMoveX = event.getX() - mLastTouchNavigationXPosition;
- mLastMoveY = event.getY() - mLastTouchNavigationYPosition;
- mAccumulatedX += mLastMoveX;
- mAccumulatedY += mLastMoveY;
- float mAccumulatedXSquared = mAccumulatedX * mAccumulatedX;
- float mAccumulatedYSquared = mAccumulatedY * mAccumulatedY;
- // Determine if we've moved far enough to send a key press
- if (mAccumulatedXSquared > mDistancePerTickSquared ||
- mAccumulatedYSquared > mDistancePerTickSquared) {
- float dominantAxis;
- float sign;
- boolean isXAxis;
- int key;
- int repeatCount = 0;
- // Determine dominant axis
- if (mAccumulatedXSquared > mAccumulatedYSquared) {
- dominantAxis = mAccumulatedX;
- isXAxis = true;
- } else {
- dominantAxis = mAccumulatedY;
- isXAxis = false;
- }
- // Determine sign of axis
- sign = (dominantAxis > 0) ? 1 : -1;
- // Determine key to send
- if (isXAxis) {
- key = (sign == 1) ? KeyEvent.KEYCODE_DPAD_RIGHT :
- KeyEvent.KEYCODE_DPAD_LEFT;
- } else {
- key = (sign == 1) ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
- }
- // Send key until maximum distance constraint is satisfied
- while (dominantAxis * dominantAxis > mDistancePerTickSquared) {
- repeatCount++;
- dominantAxis -= sign * mDistancePerTick;
- if (synthesizeNewKeys) {
- viewroot.enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, key, 0, event.getMetaState(),
- event.getDeviceId(), 0, KeyEvent.FLAG_FALLBACK,
- event.getSource()));
- viewroot.enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, key, 0, event.getMetaState(),
- event.getDeviceId(), 0, KeyEvent.FLAG_FALLBACK,
- event.getSource()));
- }
- }
- // Save new axis values
- mAccumulatedX = isXAxis ? dominantAxis : 0;
- mAccumulatedY = isXAxis ? 0 : dominantAxis;
-
- mLastKeySent = key;
- mKeySendRateMs = (int) (time - mLastTouchNavigationKeySendTimeMs) / repeatCount;
- mLastTouchNavigationKeySendTimeMs = time;
- }
- break;
- case MotionEvent.ACTION_UP:
- if (time - mLastTouchNavigationStartTimeMs < MAX_TAP_TIME && mAlwaysInTapRegion) {
- if (synthesizeNewKeys) {
- viewroot.enqueueInputEvent(new KeyEvent(mLastTouchNavigationStartTimeMs,
- time, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0,
- event.getMetaState(), event.getDeviceId(), 0,
- KeyEvent.FLAG_FALLBACK, event.getSource()));
- viewroot.enqueueInputEvent(new KeyEvent(mLastTouchNavigationStartTimeMs,
- time, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0,
- event.getMetaState(), event.getDeviceId(), 0,
- KeyEvent.FLAG_FALLBACK, event.getSource()));
- }
- } else {
- float xMoveSquared = mLastMoveX * mLastMoveX;
- float yMoveSquared = mLastMoveY * mLastMoveY;
- // Determine whether the last gesture was a fling.
- if (mMinFlickDistanceSquared <= xMoveSquared + yMoveSquared &&
- time - mLastTouchNavigationEventTimeMs <= MAX_TAP_TIME &&
- mKeySendRateMs <= mMaxRepeatDelay && mKeySendRateMs > 0) {
- mLastDeviceId = event.getDeviceId();
- mLastSource = event.getSource();
- mLastMetaState = event.getMetaState();
-
- if (synthesizeNewKeys) {
- Message message = Message.obtain(mHandler, MSG_FLICK,
- mKeySendRateMs, mLastKeySent, viewroot);
- message.setAsynchronous(true);
- mHandler.sendMessageDelayed(message, mKeySendRateMs);
- }
- }
- }
- mEdgeSwipePossible = false;
- break;
- }
-
- // Store touch event position and time
- mLastTouchNavigationEventTimeMs = time;
- mLastTouchNavigationXPosition = event.getX();
- mLastTouchNavigationYPosition = event.getY();
- }
-}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 5d0f523..14fa9cb 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -480,6 +480,7 @@ public class SurfaceView extends View {
if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
mLayout.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
}
+ mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
if (mWindow == null) {
Display display = getDisplay();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3b06da7..4fb2431 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2758,6 +2758,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
TransformationInfo mTransformationInfo;
+ /**
+ * Current clip bounds. to which all drawing of this view are constrained.
+ */
+ private Rect mClipBounds = null;
+
private boolean mLastIsOpaque;
/**
@@ -10103,7 +10108,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Offset this view's horizontal location by the specified amount of pixels.
*
- * @param offset the numer of pixels to offset the view by
+ * @param offset the number of pixels to offset the view by
*/
public void offsetLeftAndRight(int offset) {
if (offset != 0) {
@@ -12107,7 +12112,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
//System.out.println("Attached! " + this);
mAttachInfo = info;
if (mOverlay != null) {
- mOverlay.dispatchAttachedToWindow(info, visibility);
+ mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
}
mWindowAttachCount++;
// We will need to evaluate the drawable state at least once.
@@ -12178,7 +12183,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mAttachInfo = null;
if (mOverlay != null) {
- mOverlay.dispatchDetachedFromWindow();
+ mOverlay.getOverlayView().dispatchDetachedFromWindow();
}
}
@@ -12831,7 +12836,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
- mOverlay.draw(canvas);
+ mOverlay.getOverlayView().draw(canvas);
}
} else {
draw(canvas);
@@ -13147,7 +13152,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
- mOverlay.draw(canvas);
+ mOverlay.getOverlayView().draw(canvas);
}
} else {
draw(canvas);
@@ -13374,6 +13379,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Sets a rectangular area on this view to which the view will be clipped
+ * it is drawn. Setting the value to null will remove the clip bounds
+ * and the view will draw normally, using its full bounds.
+ *
+ * @param clipBounds The rectangular area, in the local coordinates of
+ * this view, to which future drawing operations will be clipped.
+ */
+ public void setClipBounds(Rect clipBounds) {
+ mClipBounds = clipBounds;
+ if (clipBounds != null) {
+ invalidate(clipBounds);
+ } else {
+ invalidate();
+ }
+ }
+
+ /**
+ * Returns a copy of the current {@link #setClipBounds(Rect) clipBounds}.
+ *
+ * @return A copy of the current clip bounds if clip bounds are set,
+ * otherwise null.
+ */
+ public Rect getClipBounds() {
+ return (mClipBounds != null) ? new Rect(mClipBounds) : null;
+ }
+
+ /**
* Utility function, called by draw(canvas, parent, drawingTime) to handle the less common
* case of an active Animation being run on the view.
*/
@@ -13849,6 +13881,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param canvas The Canvas to which the View is rendered.
*/
public void draw(Canvas canvas) {
+ if (mClipBounds != null) {
+ canvas.clipRect(mClipBounds);
+ }
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
@@ -13905,7 +13940,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
onDrawScrollBars(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
- mOverlay.dispatchDraw(canvas);
+ mOverlay.getOverlayView().dispatchDraw(canvas);
}
// we're done...
@@ -14049,34 +14084,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
onDrawScrollBars(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
- mOverlay.dispatchDraw(canvas);
+ mOverlay.getOverlayView().dispatchDraw(canvas);
}
}
/**
- * Called by the addToOverlay() methods to create, attach, and size the overlay as necessary
+ * Returns the overlay for this view, creating it if it does not yet exist.
+ * Adding drawables to the overlay will cause them to be displayed whenever
+ * the view itself is redrawn. Objects in the overlay should be actively
+ * managed: remove them when they should not be displayed anymore. The
+ * overlay will always have the same size as its host view.
+ *
+ * <p>Note: Overlays do not currently work correctly with {@link
+ * SurfaceView} or {@link TextureView}; contents in overlays for these
+ * types of views may not display correctly.</p>
+ *
+ * @return The ViewOverlay object for this view.
+ * @see ViewOverlay
*/
- private void setupOverlay() {
+ public ViewOverlay getOverlay() {
if (mOverlay == null) {
mOverlay = new ViewOverlay(mContext, this);
- mOverlay.mAttachInfo = mAttachInfo;
- mOverlay.setRight(mRight);
- mOverlay.setBottom(mBottom);
}
- }
-
- /**
- * Returns the overlay for this view, creating it if it does not yet exist. Adding drawables
- * and/or views to the overlay will cause them to be displayed whenever the view itself is
- * redrawn. Objects in the overlay should be actively managed: remove them when they should
- * not be displayed anymore and invalidate this view appropriately when overlay drawables
- * change. The overlay will always have the same size as its host view.
- *
- * @return The Overlay object for this view.
- * @see Overlay
- */
- public Overlay getOverlay() {
- setupOverlay();
return mOverlay;
}
@@ -14360,8 +14389,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) {
onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
if (mOverlay != null) {
- mOverlay.setRight(mRight);
- mOverlay.setBottom(mBottom);
+ mOverlay.getOverlayView().setRight(newWidth);
+ mOverlay.getOverlayView().setBottom(newHeight);
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f615e1bc..bf502dd 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1464,10 +1464,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final float y = event.getY();
final int childrenCount = mChildrenCount;
if (childrenCount != 0) {
+ final boolean customChildOrder = isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
HoverTarget lastHoverTarget = null;
for (int i = childrenCount - 1; i >= 0; i--) {
- final View child = children[i];
+ final int childIndex = customChildOrder
+ ? getChildDrawingOrder(childrenCount, i) : i;
+ final View child = children[childIndex];
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
@@ -1876,34 +1879,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
- final float x = ev.getX(actionIndex);
- final float y = ev.getY(actionIndex);
-
- if (mOverlay != null) {
- ViewOverlay overlay = (ViewOverlay) mOverlay;
- // Check to see whether the overlay can handle the event
- final View child = mOverlay;
- if (canViewReceivePointerEvents(child) &&
- isTransformedTouchPointInView(x, y, child, null)) {
- newTouchTarget = getTouchTarget(child);
- if (newTouchTarget != null) {
- newTouchTarget.pointerIdBits |= idBitsToAssign;
- } else {
- resetCancelNextUpFlag(child);
- if (dispatchTransformedTouchEvent(ev, false, child,
- idBitsToAssign)) {
- mLastTouchDownTime = ev.getDownTime();
- mLastTouchDownX = ev.getX();
- mLastTouchDownY = ev.getY();
- newTouchTarget = addTouchTarget(child, idBitsToAssign);
- alreadyDispatchedToNewTouchTarget = true;
- }
- }
- }
- }
-
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
+ final float x = ev.getX(actionIndex);
+ final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final View[] children = mChildren;
@@ -2991,6 +2970,30 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
+ * Returns the ViewGroupOverlay for this view group, creating it if it does
+ * not yet exist. In addition to {@link ViewOverlay}'s support for drawables,
+ * {@link ViewGroupOverlay} allows views to be added to the overlay. These
+ * views, like overlay drawables, are visual-only; they do not receive input
+ * events and should not be used as anything other than a temporary
+ * representation of a view in a parent container, such as might be used
+ * by an animation effect.
+ *
+ * <p>Note: Overlays do not currently work correctly with {@link
+ * SurfaceView} or {@link TextureView}; contents in overlays for these
+ * types of views may not display correctly.</p>
+ *
+ * @return The ViewGroupOverlay object for this view.
+ * @see ViewGroupOverlay
+ */
+ @Override
+ public ViewGroupOverlay getOverlay() {
+ if (mOverlay == null) {
+ mOverlay = new ViewGroupOverlay(mContext, this);
+ }
+ return (ViewGroupOverlay) mOverlay;
+ }
+
+ /**
* Returns the index of the child to draw for this iteration. Override this
* if you want to change the drawing order of children. By default, it
* returns i.
@@ -3055,11 +3058,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
if (mOverlay != null) {
- mOverlay.mRecreateDisplayList = (mOverlay.mPrivateFlags & PFLAG_INVALIDATED)
+ View overlayView = mOverlay.getOverlayView();
+ overlayView.mRecreateDisplayList = (overlayView.mPrivateFlags & PFLAG_INVALIDATED)
== PFLAG_INVALIDATED;
- mOverlay.mPrivateFlags &= ~PFLAG_INVALIDATED;
- mOverlay.getDisplayList();
- mOverlay.mRecreateDisplayList = false;
+ overlayView.mPrivateFlags &= ~PFLAG_INVALIDATED;
+ overlayView.getDisplayList();
+ overlayView.mRecreateDisplayList = false;
}
}
@@ -3079,6 +3083,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
+ * Returns whether ths group's children are clipped to their bounds before drawing.
+ * The default value is true.
+ * @see #setClipChildren(boolean)
+ *
+ * @return True if the group's children will be clipped to their bounds,
+ * false otherwise.
+ */
+ public boolean getClipChildren() {
+ return ((mGroupFlags & FLAG_CLIP_CHILDREN) != 0);
+ }
+
+ /**
* By default, children are clipped to their bounds before drawing. This
* allows view groups to override this behavior for animations, etc.
*
@@ -4524,7 +4540,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
@Override
public final void layout(int l, int t, int r, int b) {
- if (mTransition == null || !mTransition.isChangingLayout()) {
+ if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
if (mTransition != null) {
mTransition.layoutChange(this);
}
diff --git a/core/java/android/view/Overlay.java b/core/java/android/view/ViewGroupOverlay.java
index 6630752..c1b24f2 100644
--- a/core/java/android/view/Overlay.java
+++ b/core/java/android/view/ViewGroupOverlay.java
@@ -15,43 +15,37 @@
*/
package android.view;
+import android.content.Context;
import android.graphics.drawable.Drawable;
/**
- * An overlay is an extra layer that sits on top of a View (the "host view")
- * which is drawn after all other content in that view (including children,
- * if the view is a ViewGroup). Interaction with the overlay layer is done in
- * terms of adding/removing views and drawables.
+ * A group overlay is an extra layer that sits on top of a ViewGroup
+ * (the "host view") which is drawn after all other content in that view
+ * (including the view group's children). Interaction with the overlay
+ * layer is done by adding and removing views and drawables.
*
- * @see android.view.View#getOverlay()
+ * <p>ViewGroupOverlay is a subclass of {@link ViewOverlay}, adding the ability to
+ * manage views for overlays on ViewGroups, in addition to the drawable
+ * support in ViewOverlay.</p>
+ *
+ * @see ViewGroup#getOverlay()
*/
-public interface Overlay {
+public class ViewGroupOverlay extends ViewOverlay {
- /**
- * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
- * the host view. Any drawable added to the overlay should be removed when it is no longer
- * needed or no longer visible.
- *
- * @param drawable The Drawable to be added to the overlay. This drawable will be
- * drawn when the view redraws its overlay.
- * @see #remove(android.graphics.drawable.Drawable)
- * @see #add(View)
- */
- void add(Drawable drawable);
-
- /**
- * Removes the specified Drawable from the overlay.
- *
- * @param drawable The Drawable to be removed from the overlay.
- * @see #add(android.graphics.drawable.Drawable)
- */
- void remove(Drawable drawable);
+ ViewGroupOverlay(Context context, View hostView) {
+ super(context, hostView);
+ }
/**
* Adds a View to the overlay. The bounds of the added view should be
* relative to the host view. Any view added to the overlay should be
* removed when it is no longer needed or no longer visible.
*
+ * <p>Views in the overlay are visual-only; they do not receive input
+ * events and do not participate in focus traversal. Overlay views
+ * are intended to be transient, such as might be needed by a temporary
+ * animation effect.</p>
+ *
* <p>If the view has a parent, the view will be removed from that parent
* before being added to the overlay. Also, the view will be repositioned
* such that it is in the same relative location inside the activity. For
@@ -62,20 +56,20 @@ public interface Overlay {
* @param view The View to be added to the overlay. The added view will be
* drawn when the overlay is drawn.
* @see #remove(View)
- * @see #add(android.graphics.drawable.Drawable)
+ * @see ViewOverlay#add(Drawable)
*/
- void add(View view);
+ public void add(View view) {
+ mOverlayViewGroup.add(view);
+ }
/**
* Removes the specified View from the overlay.
*
* @param view The View to be removed from the overlay.
* @see #add(View)
+ * @see ViewOverlay#remove(Drawable)
*/
- void remove(View view);
-
- /**
- * Removes all views and drawables from the overlay.
- */
- void clear();
+ public void remove(View view) {
+ mOverlayViewGroup.remove(view);
+ }
}
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
index 8b18d53..78e2597 100644
--- a/core/java/android/view/ViewOverlay.java
+++ b/core/java/android/view/ViewOverlay.java
@@ -23,215 +23,286 @@ import android.graphics.drawable.Drawable;
import java.util.ArrayList;
/**
- * ViewOverlay is a container that View uses to host all objects (views and
- * drawables) that are added to its "overlay", gotten through
- * {@link View#getOverlay()}. Views and drawables are added to the overlay
- * via the add/remove methods in this class. These views and drawables are
- * drawn whenever the view itself is drawn; first the view draws its own
- * content (and children, if it is a ViewGroup), then it draws its overlay
- * (if it has one).
+ * An overlay is an extra layer that sits on top of a View (the "host view")
+ * which is drawn after all other content in that view (including children,
+ * if the view is a ViewGroup). Interaction with the overlay layer is done
+ * by adding and removing drawables.
*
- * Besides managing and drawing the list of drawables, this class serves
- * two purposes:
- * (1) it noops layout calls because children are absolutely positioned and
- * (2) it forwards all invalidation calls to its host view. The invalidation
- * redirect is necessary because the overlay is not a child of the host view
- * and invalidation cannot therefore follow the normal path up through the
- * parent hierarchy.
+ * <p>An overlay requested from a ViewGroup is of type {@link ViewGroupOverlay},
+ * which also supports adding and removing views.</p>
*
- * @hide
+ * @see View#getOverlay() View.getOverlay()
+ * @see ViewGroup#getOverlay() ViewGroup.getOverlay()
+ * @see ViewGroupOverlay
*/
-class ViewOverlay extends ViewGroup implements Overlay {
+public class ViewOverlay {
/**
- * The View for which this is an overlay. Invalidations of the overlay are redirected to
- * this host view.
+ * The actual container for the drawables (and views, if it's a ViewGroupOverlay).
+ * All of the management and rendering details for the overlay are handled in
+ * OverlayViewGroup.
*/
- View mHostView;
+ OverlayViewGroup mOverlayViewGroup;
+
+ ViewOverlay(Context context, View hostView) {
+ mOverlayViewGroup = new OverlayViewGroup(context, hostView);
+ }
/**
- * The set of drawables to draw when the overlay is rendered.
+ * Used internally by View and ViewGroup to handle drawing and invalidation
+ * of the overlay
+ * @return
*/
- ArrayList<Drawable> mDrawables = null;
-
- ViewOverlay(Context context, View host) {
- super(context);
- mHostView = host;
- mParent = mHostView.getParent();
+ ViewGroup getOverlayView() {
+ return mOverlayViewGroup;
}
- @Override
+ /**
+ * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
+ * the host view. Any drawable added to the overlay should be removed when it is no longer
+ * needed or no longer visible.
+ *
+ * @param drawable The Drawable to be added to the overlay. This drawable will be
+ * drawn when the view redraws its overlay.
+ * @see #remove(Drawable)
+ */
public void add(Drawable drawable) {
- if (mDrawables == null) {
- mDrawables = new ArrayList<Drawable>();
- }
- if (!mDrawables.contains(drawable)) {
- // Make each drawable unique in the overlay; can't add it more than once
- mDrawables.add(drawable);
- invalidate(drawable.getBounds());
- drawable.setCallback(this);
- }
+ mOverlayViewGroup.add(drawable);
}
- @Override
+ /**
+ * Removes the specified Drawable from the overlay.
+ *
+ * @param drawable The Drawable to be removed from the overlay.
+ * @see #add(Drawable)
+ */
public void remove(Drawable drawable) {
- if (mDrawables != null) {
- mDrawables.remove(drawable);
- invalidate(drawable.getBounds());
- drawable.setCallback(null);
- }
+ mOverlayViewGroup.remove(drawable);
}
- @Override
- public void invalidateDrawable(Drawable drawable) {
- invalidate(drawable.getBounds());
+ /**
+ * Removes all content from the overlay.
+ */
+ public void clear() {
+ mOverlayViewGroup.clear();
}
- @Override
- public void add(View child) {
- int deltaX = 0;
- int deltaY = 0;
- if (child.getParent() instanceof ViewGroup) {
- ViewGroup parent = (ViewGroup) child.getParent();
- if (parent != mHostView) {
- // Moving to different container; figure out how to position child such that
- // it is in the same location on the screen
- int[] parentLocation = new int[2];
- int[] hostViewLocation = new int[2];
- parent.getLocationOnScreen(parentLocation);
- mHostView.getLocationOnScreen(hostViewLocation);
- child.offsetLeftAndRight(parentLocation[0] - hostViewLocation[0]);
- child.offsetTopAndBottom(parentLocation[1] - hostViewLocation[1]);
+ boolean isEmpty() {
+ return mOverlayViewGroup.isEmpty();
+ }
+
+ /**
+ * OverlayViewGroup is a container that View and ViewGroup use to host
+ * drawables and views added to their overlays ({@link ViewOverlay} and
+ * {@link ViewGroupOverlay}, respectively). Drawables are added to the overlay
+ * via the add/remove methods in ViewOverlay, Views are added/removed via
+ * ViewGroupOverlay. These drawable and view objects are
+ * drawn whenever the view itself is drawn; first the view draws its own
+ * content (and children, if it is a ViewGroup), then it draws its overlay
+ * (if it has one).
+ *
+ * <p>Besides managing and drawing the list of drawables, this class serves
+ * two purposes:
+ * (1) it noops layout calls because children are absolutely positioned and
+ * (2) it forwards all invalidation calls to its host view. The invalidation
+ * redirect is necessary because the overlay is not a child of the host view
+ * and invalidation cannot therefore follow the normal path up through the
+ * parent hierarchy.</p>
+ *
+ * @see View#getOverlay()
+ * @see ViewGroup#getOverlay()
+ */
+ static class OverlayViewGroup extends ViewGroup {
+
+ /**
+ * The View for which this is an overlay. Invalidations of the overlay are redirected to
+ * this host view.
+ */
+ View mHostView;
+
+ /**
+ * The set of drawables to draw when the overlay is rendered.
+ */
+ ArrayList<Drawable> mDrawables = null;
+
+ OverlayViewGroup(Context context, View hostView) {
+ super(context);
+ mHostView = hostView;
+ mAttachInfo = mHostView.mAttachInfo;
+ mRight = hostView.getWidth();
+ mBottom = hostView.getHeight();
+ }
+
+ public void add(Drawable drawable) {
+ if (mDrawables == null) {
+
+ mDrawables = new ArrayList<Drawable>();
+ }
+ if (!mDrawables.contains(drawable)) {
+ // Make each drawable unique in the overlay; can't add it more than once
+ mDrawables.add(drawable);
+ invalidate(drawable.getBounds());
+ drawable.setCallback(this);
}
- parent.removeView(child);
}
- super.addView(child);
- }
- @Override
- public void remove(View view) {
- super.removeView(view);
- }
+ public void remove(Drawable drawable) {
+ if (mDrawables != null) {
+ mDrawables.remove(drawable);
+ invalidate(drawable.getBounds());
+ drawable.setCallback(null);
+ }
+ }
- @Override
- public void clear() {
- removeAllViews();
- mDrawables.clear();
- }
+ public void add(View child) {
+ if (child.getParent() instanceof ViewGroup) {
+ ViewGroup parent = (ViewGroup) child.getParent();
+ if (parent != mHostView) {
+ // Moving to different container; figure out how to position child such that
+ // it is in the same location on the screen
+ int[] parentLocation = new int[2];
+ int[] hostViewLocation = new int[2];
+ parent.getLocationOnScreen(parentLocation);
+ mHostView.getLocationOnScreen(hostViewLocation);
+ child.offsetLeftAndRight(parentLocation[0] - hostViewLocation[0]);
+ child.offsetTopAndBottom(parentLocation[1] - hostViewLocation[1]);
+ }
+ parent.removeView(child);
+ }
+ super.addView(child);
+ }
- boolean isEmpty() {
- if (getChildCount() == 0 && (mDrawables == null || mDrawables.size() == 0)) {
- return true;
+ public void remove(View view) {
+ super.removeView(view);
}
- return false;
- }
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size();
- for (int i = 0; i < numDrawables; ++i) {
- mDrawables.get(i).draw(canvas);
+ public void clear() {
+ removeAllViews();
+ mDrawables.clear();
}
- }
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- // Noop: children are positioned absolutely
- }
+ boolean isEmpty() {
+ if (getChildCount() == 0 &&
+ (mDrawables == null || mDrawables.size() == 0)) {
+ return true;
+ }
+ return false;
+ }
- /*
- The following invalidation overrides exist for the purpose of redirecting invalidation to
- the host view. The overlay is not parented to the host view (since a View cannot be a parent),
- so the invalidation cannot proceed through the normal parent hierarchy.
- There is a built-in assumption that the overlay exactly covers the host view, therefore
- the invalidation rectangles received do not need to be adjusted when forwarded to
- the host view.
- */
+ @Override
+ public void invalidateDrawable(Drawable drawable) {
+ invalidate(drawable.getBounds());
+ }
- @Override
- public void invalidate(Rect dirty) {
- super.invalidate(dirty);
- if (mHostView != null) {
- mHostView.invalidate(dirty);
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size();
+ for (int i = 0; i < numDrawables; ++i) {
+ mDrawables.get(i).draw(canvas);
+ }
}
- }
- @Override
- public void invalidate(int l, int t, int r, int b) {
- super.invalidate(l, t, r, b);
- if (mHostView != null) {
- mHostView.invalidate(l, t, r, b);
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // Noop: children are positioned absolutely
}
- }
- @Override
- public void invalidate() {
- super.invalidate();
- if (mHostView != null) {
- mHostView.invalidate();
+ /*
+ The following invalidation overrides exist for the purpose of redirecting invalidation to
+ the host view. The overlay is not parented to the host view (since a View cannot be a
+ parent), so the invalidation cannot proceed through the normal parent hierarchy.
+ There is a built-in assumption that the overlay exactly covers the host view, therefore
+ the invalidation rectangles received do not need to be adjusted when forwarded to
+ the host view.
+ */
+
+ @Override
+ public void invalidate(Rect dirty) {
+ super.invalidate(dirty);
+ if (mHostView != null) {
+ mHostView.invalidate(dirty);
+ }
}
- }
- @Override
- void invalidate(boolean invalidateCache) {
- super.invalidate(invalidateCache);
- if (mHostView != null) {
- mHostView.invalidate(invalidateCache);
+ @Override
+ public void invalidate(int l, int t, int r, int b) {
+ super.invalidate(l, t, r, b);
+ if (mHostView != null) {
+ mHostView.invalidate(l, t, r, b);
+ }
}
- }
- @Override
- void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
- super.invalidateViewProperty(invalidateParent, forceRedraw);
- if (mHostView != null) {
- mHostView.invalidateViewProperty(invalidateParent, forceRedraw);
+ @Override
+ public void invalidate() {
+ super.invalidate();
+ if (mHostView != null) {
+ mHostView.invalidate();
+ }
}
- }
- @Override
- protected void invalidateParentCaches() {
- super.invalidateParentCaches();
- if (mHostView != null) {
- mHostView.invalidateParentCaches();
+ @Override
+ void invalidate(boolean invalidateCache) {
+ super.invalidate(invalidateCache);
+ if (mHostView != null) {
+ mHostView.invalidate(invalidateCache);
+ }
}
- }
- @Override
- protected void invalidateParentIfNeeded() {
- super.invalidateParentIfNeeded();
- if (mHostView != null) {
- mHostView.invalidateParentIfNeeded();
+ @Override
+ void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
+ super.invalidateViewProperty(invalidateParent, forceRedraw);
+ if (mHostView != null) {
+ mHostView.invalidateViewProperty(invalidateParent, forceRedraw);
+ }
}
- }
- public void invalidateChildFast(View child, final Rect dirty) {
- if (mHostView != null) {
- // Note: This is not a "fast" invalidation. Would be nice to instead invalidate using DL
- // properties and a dirty rect instead of causing a real invalidation of the host view
- int left = child.mLeft;
- int top = child.mTop;
- if (!child.getMatrix().isIdentity()) {
- child.transformRect(dirty);
+ @Override
+ protected void invalidateParentCaches() {
+ super.invalidateParentCaches();
+ if (mHostView != null) {
+ mHostView.invalidateParentCaches();
+ }
+ }
+
+ @Override
+ protected void invalidateParentIfNeeded() {
+ super.invalidateParentIfNeeded();
+ if (mHostView != null) {
+ mHostView.invalidateParentIfNeeded();
}
- dirty.offset(left, top);
- mHostView.invalidate(dirty);
}
- }
- @Override
- public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
- if (mHostView != null) {
- dirty.offset(location[0], location[1]);
- if (mHostView instanceof ViewGroup) {
- location[0] = 0;
- location[1] = 0;
- super.invalidateChildInParent(location, dirty);
- return ((ViewGroup) mHostView).invalidateChildInParent(location, dirty);
- } else {
- invalidate(dirty);
+ public void invalidateChildFast(View child, final Rect dirty) {
+ if (mHostView != null) {
+ // Note: This is not a "fast" invalidation. Would be nice to instead invalidate
+ // using DisplayList properties and a dirty rect instead of causing a real
+ // invalidation of the host view
+ int left = child.mLeft;
+ int top = child.mTop;
+ if (!child.getMatrix().isIdentity()) {
+ child.transformRect(dirty);
+ }
+ dirty.offset(left, top);
+ mHostView.invalidate(dirty);
}
}
- return null;
+
+ @Override
+ public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
+ if (mHostView != null) {
+ dirty.offset(location[0], location[1]);
+ if (mHostView instanceof ViewGroup) {
+ location[0] = 0;
+ location[1] = 0;
+ super.invalidateChildInParent(location, dirty);
+ return ((ViewGroup) mHostView).invalidateChildInParent(location, dirty);
+ } else {
+ invalidate(dirty);
+ }
+ }
+ return null;
+ }
}
+
}
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 22f98b7..98df064 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -16,6 +16,7 @@
package android.view;
+import android.animation.Animatable;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.animation.TimeInterpolator;
@@ -323,6 +324,15 @@ public class ViewPropertyAnimator {
}
/**
+ * Returns the timing interpolator that this animation uses.
+ *
+ * @return The timing interpolator for this animation.
+ */
+ public TimeInterpolator getInterpolator() {
+ return null;
+ }
+
+ /**
* Sets a listener for events in the underlying Animators that run the property
* animations.
*
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9387624..f03c077 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -42,7 +42,6 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
-import android.os.LatencyTimer;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -116,9 +115,6 @@ public final class ViewRootImpl implements ViewParent,
* at 60 Hz. This can be used to measure the potential framerate.
*/
private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering";
-
- private static final boolean MEASURE_LATENCY = false;
- private static LatencyTimer lt;
/**
* Maximum time we allow the user to roll the trackball enough to generate
@@ -138,26 +134,15 @@ public final class ViewRootImpl implements ViewParent,
private static boolean sRenderThreadQueried = false;
private static final Object[] sRenderThreadQueryLock = new Object[0];
+ final Context mContext;
final IWindowSession mWindowSession;
final Display mDisplay;
final String mBasePackageName;
- long mLastTrackballTime = 0;
- final TrackballAxis mTrackballAxisX = new TrackballAxis();
- final TrackballAxis mTrackballAxisY = new TrackballAxis();
-
- final SimulatedDpad mSimulatedDpad;
-
- int mLastJoystickXDirection;
- int mLastJoystickYDirection;
- int mLastJoystickXKeyCode;
- int mLastJoystickYKeyCode;
-
final int[] mTmpLocation = new int[2];
final TypedValue mTmpValue = new TypedValue();
-
- final InputMethodCallback mInputMethodCallback;
+
final Thread mThread;
final WindowLeaked mLocation;
@@ -197,7 +182,6 @@ public final class ViewRootImpl implements ViewParent,
int mHeight;
Rect mDirty;
final Rect mCurrentDirty = new Rect();
- final Rect mPreviousDirty = new Rect();
boolean mIsAnimating;
CompatibilityInfo.Translator mTranslator;
@@ -232,38 +216,23 @@ public final class ViewRootImpl implements ViewParent,
int mClientWindowLayoutFlags;
boolean mLastOverscanRequested;
- /** Event was not handled and is finished.
- * @hide */
- public static final int EVENT_NOT_HANDLED = 0;
- /** Event was handled and is finished.
- * @hide */
- public static final int EVENT_HANDLED = 1;
- /** Event is waiting on the IME.
- * @hide */
- public static final int EVENT_PENDING_IME = 2;
- /** Event requires post-IME dispatch.
- * @hide */
- public static final int EVENT_POST_IME = 3;
-
// Pool of queued input events.
private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
private QueuedInputEvent mQueuedInputEventPool;
private int mQueuedInputEventPoolSize;
/* Input event queue.
- * Pending input events are input events waiting to be handled by the application. Current
- * input events are input events which are being handled but are waiting on some action by the
- * IME, even if they themselves may not need to be handled by the IME.
+ * Pending input events are input events waiting to be delivered to the input stages
+ * and handled by the application.
*/
QueuedInputEvent mPendingInputEventHead;
QueuedInputEvent mPendingInputEventTail;
int mPendingInputEventCount;
- QueuedInputEvent mActiveInputEventHead;
- QueuedInputEvent mActiveInputEventTail;
- int mActiveInputEventCount;
boolean mProcessInputEventsScheduled;
String mPendingInputEventQueueLengthCounterName = "pq";
- String mActiveInputEventQueueLengthCounterName = "aq";
+
+ InputStage mFirstInputStage;
+ InputStage mFirstPostImeInputStage;
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
@@ -362,18 +331,8 @@ public final class ViewRootImpl implements ViewParent,
}
public ViewRootImpl(Context context, Display display) {
- super();
-
- if (MEASURE_LATENCY) {
- if (lt == null) {
- lt = new LatencyTimer(100, 1000);
- }
- }
-
- // Initialize the statics when this class is first instantiated. This is
- // done here instead of in the static block because Zygote does not
- // allow the spawning of threads.
- mWindowSession = WindowManagerGlobal.getWindowSession(context.getMainLooper());
+ mContext = context;
+ mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
@@ -391,7 +350,6 @@ public final class ViewRootImpl implements ViewParent,
mWinFrame = new Rect();
mWindow = new W(this);
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
- mInputMethodCallback = new InputMethodCallback(this);
mViewVisibility = View.GONE;
mTransparentRegion = new Region();
mPreviousTransparentRegion = new Region();
@@ -412,7 +370,6 @@ public final class ViewRootImpl implements ViewParent,
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mAttachInfo.mScreenOn = powerManager.isScreenOn();
loadSystemProperties();
- mSimulatedDpad = new SimulatedDpad(context);
}
/**
@@ -660,8 +617,22 @@ public final class ViewRootImpl implements ViewParent,
view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
- mPendingInputEventQueueLengthCounterName = "pq:" + attrs.getTitle();
- mActiveInputEventQueueLengthCounterName = "aq:" + attrs.getTitle();
+ // Set up the input pipeline.
+ CharSequence counterSuffix = attrs.getTitle();
+ InputStage syntheticStage = new SyntheticInputStage();
+ InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticStage);
+ InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
+ "aq:native-post-ime:" + counterSuffix);
+ InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
+ InputStage imeStage = new ImeInputStage(earlyPostImeStage,
+ "aq:ime:" + counterSuffix);
+ InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
+ InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
+ "aq:native-pre-ime:" + counterSuffix);
+
+ mFirstInputStage = nativePreImeStage;
+ mFirstPostImeInputStage = earlyPostImeStage;
+ mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
}
}
}
@@ -767,10 +738,11 @@ public final class ViewRootImpl implements ViewParent,
final boolean translucent = attrs.format != PixelFormat.OPAQUE;
mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent);
- mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());
- mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested
- = mAttachInfo.mHardwareRenderer != null;
-
+ if (mAttachInfo.mHardwareRenderer != null) {
+ mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());
+ mAttachInfo.mHardwareAccelerated =
+ mAttachInfo.mHardwareAccelerationRequested = true;
+ }
} else if (fakeHwAccelerated) {
// The window had wanted to use hardware acceleration, but this
// is not allowed in its process. By setting this flag, it can
@@ -1755,7 +1727,7 @@ public final class ViewRootImpl implements ViewParent,
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
- // By this point all views have been sized and positionned
+ // By this point all views have been sized and positioned
// We can compute the transparent area
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
@@ -1773,6 +1745,7 @@ public final class ViewRootImpl implements ViewParent,
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
+ mFullRedrawNeeded = true;
// reconfigure window manager
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
@@ -2387,14 +2360,10 @@ public final class ViewRootImpl implements ViewParent,
mResizeAlpha = resizeAlpha;
mCurrentDirty.set(dirty);
- mCurrentDirty.union(mPreviousDirty);
- mPreviousDirty.set(dirty);
dirty.setEmpty();
- if (attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,
- animating ? null : mCurrentDirty)) {
- mPreviousDirty.set(0, 0, mWidth, mHeight);
- }
+ attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,
+ animating ? null : mCurrentDirty);
} else {
// If we get here with a disabled & requested hardware renderer, something went
// wrong (an invalidate posted right before we destroyed the hardware surface
@@ -2449,6 +2418,8 @@ public final class ViewRootImpl implements ViewParent,
canvas = mSurface.lockCanvas(dirty);
+ // The dirty rectangle can be modified by Surface.lockCanvas()
+ //noinspection ConstantConditions
if (left != dirty.left || top != dirty.top || right != dirty.right ||
bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
@@ -2622,12 +2593,12 @@ public final class ViewRootImpl implements ViewParent,
// requestChildRectangleOnScreen() call (in which case 'rectangle'
// is non-null and we just want to scroll to whatever that
// rectangle is).
- View focus = mView.findFocus();
+ final View focus = mView.findFocus();
if (focus == null) {
return false;
}
View lastScrolledFocus = (mLastScrolledFocus != null) ? mLastScrolledFocus.get() : null;
- if (lastScrolledFocus != null && focus != lastScrolledFocus) {
+ if (focus != lastScrolledFocus) {
// If the focus has changed, then ignore any requests to scroll
// to a rectangle; first we want to make sure the entire focus
// view is visible.
@@ -2642,7 +2613,7 @@ public final class ViewRootImpl implements ViewParent,
// as they are.
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Keeping scroll y="
+ mScrollY + " vi=" + vi.toShortString());
- } else if (focus != null) {
+ } else {
// We need to determine if the currently focused view is
// within the visible part of the window and, if not, apply
// a pan so it can be seen.
@@ -2861,7 +2832,7 @@ public final class ViewRootImpl implements ViewParent,
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
-
+
// Dispose the input channel after removing the window so the Window Manager
// doesn't interpret the input channel being closed as an abnormal termination.
if (mInputChannel != null) {
@@ -2953,8 +2924,6 @@ public final class ViewRootImpl implements ViewParent,
private final static int MSG_DISPATCH_DONE_ANIMATING = 22;
private final static int MSG_INVALIDATE_WORLD = 23;
private final static int MSG_WINDOW_MOVED = 24;
- private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 25;
- private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 26;
final class ViewRootHandler extends Handler {
@Override
@@ -3004,10 +2973,6 @@ public final class ViewRootImpl implements ViewParent,
return "MSG_DISPATCH_DONE_ANIMATING";
case MSG_WINDOW_MOVED:
return "MSG_WINDOW_MOVED";
- case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
- return "MSG_ENQUEUE_X_AXIS_KEY_REPEAT";
- case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT:
- return "MSG_ENQUEUE_Y_AXIS_KEY_REPEAT";
}
return super.getMessageName(message);
}
@@ -3099,8 +3064,7 @@ public final class ViewRootImpl implements ViewParent,
boolean inTouchMode = msg.arg2 != 0;
ensureTouchModeLocally(inTouchMode);
- if (mAttachInfo.mHardwareRenderer != null &&
- mSurface != null && mSurface.isValid()) {
+ if (mAttachInfo.mHardwareRenderer != null && mSurface.isValid()){
mFullRedrawNeeded = true;
try {
mAttachInfo.mHardwareRenderer.initializeIfNeeded(
@@ -3231,18 +3195,6 @@ public final class ViewRootImpl implements ViewParent,
invalidateWorld(mView);
}
} break;
- case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
- case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: {
- KeyEvent oldEvent = (KeyEvent)msg.obj;
- KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent, SystemClock.uptimeMillis(),
- oldEvent.getRepeatCount() + 1);
- if (mAttachInfo.mHasWindowFocus) {
- enqueueInputEvent(e);
- Message m = obtainMessage(msg.what, e);
- m.setAsynchronous(true);
- sendMessageDelayed(m, mViewConfiguration.getKeyRepeatDelay());
- }
- } break;
}
}
}
@@ -3365,366 +3317,1425 @@ public final class ViewRootImpl implements ViewParent,
return false;
}
- private int deliverInputEvent(QueuedInputEvent q) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
- try {
- if (q.mEvent instanceof KeyEvent) {
- return deliverKeyEvent(q);
+ /**
+ * Base class for implementing a stage in the chain of responsibility
+ * for processing input events.
+ * <p>
+ * Events are delivered to the stage by the {@link #deliver} method. The stage
+ * then has the choice of finishing the event or forwarding it to the next stage.
+ * </p>
+ */
+ abstract class InputStage {
+ private final InputStage mNext;
+
+ protected static final int FORWARD = 0;
+ protected static final int FINISH_HANDLED = 1;
+ protected static final int FINISH_NOT_HANDLED = 2;
+
+ /**
+ * Creates an input stage.
+ * @param next The next stage to which events should be forwarded.
+ */
+ public InputStage(InputStage next) {
+ mNext = next;
+ }
+
+ /**
+ * Delivers an event to be processed.
+ */
+ public final void deliver(QueuedInputEvent q) {
+ if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
+ forward(q);
+ } else if (mView == null || !mAdded) {
+ finish(q, false);
} else {
- final int source = q.mEvent.getSource();
- if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- return deliverPointerEvent(q);
- } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- return deliverTrackballEvent(q);
+ apply(q, onProcess(q));
+ }
+ }
+
+ /**
+ * Marks the the input event as finished then forwards it to the next stage.
+ */
+ protected void finish(QueuedInputEvent q, boolean handled) {
+ q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
+ if (handled) {
+ q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
+ }
+ forward(q);
+ }
+
+ /**
+ * Forwards the event to the next stage.
+ */
+ protected void forward(QueuedInputEvent q) {
+ onDeliverToNext(q);
+ }
+
+ /**
+ * Applies a result code from {@link #onProcess} to the specified event.
+ */
+ protected void apply(QueuedInputEvent q, int result) {
+ if (result == FORWARD) {
+ forward(q);
+ } else if (result == FINISH_HANDLED) {
+ finish(q, true);
+ } else if (result == FINISH_NOT_HANDLED) {
+ finish(q, false);
+ } else {
+ throw new IllegalArgumentException("Invalid result: " + result);
+ }
+ }
+
+ /**
+ * Called when an event is ready to be processed.
+ * @return A result code indicating how the event was handled.
+ */
+ protected int onProcess(QueuedInputEvent q) {
+ return FORWARD;
+ }
+
+ /**
+ * Called when an event is being delivered to the next stage.
+ */
+ protected void onDeliverToNext(QueuedInputEvent q) {
+ if (mNext != null) {
+ mNext.deliver(q);
+ } else {
+ finishInputEvent(q);
+ }
+ }
+ }
+
+ /**
+ * Base class for implementing an input pipeline stage that supports
+ * asynchronous and out-of-order processing of input events.
+ * <p>
+ * In addition to what a normal input stage can do, an asynchronous
+ * input stage may also defer an input event that has been delivered to it
+ * and finish or forward it later.
+ * </p>
+ */
+ abstract class AsyncInputStage extends InputStage {
+ private final String mTraceCounter;
+
+ private QueuedInputEvent mQueueHead;
+ private QueuedInputEvent mQueueTail;
+ private int mQueueLength;
+
+ protected static final int DEFER = 3;
+
+ /**
+ * Creates an asynchronous input stage.
+ * @param next The next stage to which events should be forwarded.
+ * @param traceCounter The name of a counter to record the size of
+ * the queue of pending events.
+ */
+ public AsyncInputStage(InputStage next, String traceCounter) {
+ super(next);
+ mTraceCounter = traceCounter;
+ }
+
+ /**
+ * Marks the event as deferred, which is to say that it will be handled
+ * asynchronously. The caller is responsible for calling {@link #forward}
+ * or {@link #finish} later when it is done handling the event.
+ */
+ protected void defer(QueuedInputEvent q) {
+ q.mFlags |= QueuedInputEvent.FLAG_DEFERRED;
+ enqueue(q);
+ }
+
+ @Override
+ protected void forward(QueuedInputEvent q) {
+ // Clear the deferred flag.
+ q.mFlags &= ~QueuedInputEvent.FLAG_DEFERRED;
+
+ // Fast path if the queue is empty.
+ QueuedInputEvent curr = mQueueHead;
+ if (curr == null) {
+ super.forward(q);
+ return;
+ }
+
+ // Determine whether the event must be serialized behind any others
+ // before it can be delivered to the next stage. This is done because
+ // deferred events might be handled out of order by the stage.
+ final int deviceId = q.mEvent.getDeviceId();
+ QueuedInputEvent prev = null;
+ boolean blocked = false;
+ while (curr != null && curr != q) {
+ if (!blocked && deviceId == curr.mEvent.getDeviceId()) {
+ blocked = true;
+ }
+ prev = curr;
+ curr = curr.mNext;
+ }
+
+ // If the event is blocked, then leave it in the queue to be delivered later.
+ // Note that the event might not yet be in the queue if it was not previously
+ // deferred so we will enqueue it if needed.
+ if (blocked) {
+ if (curr == null) {
+ enqueue(q);
+ }
+ return;
+ }
+
+ // The event is not blocked. Deliver it immediately.
+ if (curr != null) {
+ curr = curr.mNext;
+ dequeue(q, prev);
+ }
+ super.forward(q);
+
+ // Dequeuing this event may have unblocked successors. Deliver them.
+ while (curr != null) {
+ if (deviceId == curr.mEvent.getDeviceId()) {
+ if ((curr.mFlags & QueuedInputEvent.FLAG_DEFERRED) != 0) {
+ break;
+ }
+ QueuedInputEvent next = curr.mNext;
+ dequeue(curr, prev);
+ super.forward(curr);
+ curr = next;
} else {
- return deliverGenericMotionEvent(q);
+ prev = curr;
+ curr = curr.mNext;
}
}
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+
+ @Override
+ protected void apply(QueuedInputEvent q, int result) {
+ if (result == DEFER) {
+ defer(q);
+ } else {
+ super.apply(q, result);
+ }
+ }
+
+ private void enqueue(QueuedInputEvent q) {
+ if (mQueueTail == null) {
+ mQueueHead = q;
+ mQueueTail = q;
+ } else {
+ mQueueTail.mNext = q;
+ mQueueTail = q;
+ }
+
+ mQueueLength += 1;
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength);
+ }
+
+ private void dequeue(QueuedInputEvent q, QueuedInputEvent prev) {
+ if (prev == null) {
+ mQueueHead = q.mNext;
+ } else {
+ prev.mNext = q.mNext;
+ }
+ if (mQueueTail == q) {
+ mQueueTail = prev;
+ }
+ q.mNext = null;
+
+ mQueueLength -= 1;
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength);
}
}
- private int deliverInputEventPostIme(QueuedInputEvent q) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEventPostIme");
- try {
+ /**
+ * Delivers pre-ime input events to a native activity.
+ * Does not support pointer events.
+ */
+ final class NativePreImeInputStage extends AsyncInputStage {
+ public NativePreImeInputStage(InputStage next, String traceCounter) {
+ super(next, traceCounter);
+ }
+
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
+ return FORWARD;
+ }
+ }
+
+ /**
+ * Delivers pre-ime input events to the view hierarchy.
+ * Does not support pointer events.
+ */
+ final class ViewPreImeInputStage extends InputStage {
+ public ViewPreImeInputStage(InputStage next) {
+ super(next);
+ }
+
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
+ if (q.mEvent instanceof KeyEvent) {
+ return processKeyEvent(q);
+ }
+ return FORWARD;
+ }
+
+ private int processKeyEvent(QueuedInputEvent q) {
+ final KeyEvent event = (KeyEvent)q.mEvent;
+ if (mView.dispatchKeyEventPreIme(event)) {
+ return FINISH_HANDLED;
+ }
+ return FORWARD;
+ }
+ }
+
+ /**
+ * Delivers input events to the ime.
+ * Does not support pointer events.
+ */
+ final class ImeInputStage extends AsyncInputStage
+ implements InputMethodManager.FinishedInputEventCallback {
+ public ImeInputStage(InputStage next, String traceCounter) {
+ super(next, traceCounter);
+ }
+
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
+ if (mLastWasImTarget) {
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null) {
+ final InputEvent event = q.mEvent;
+ if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
+ int result = imm.dispatchInputEvent(event, q, this, mHandler);
+ if (result == InputMethodManager.DISPATCH_HANDLED) {
+ return FINISH_HANDLED;
+ } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
+ return FINISH_NOT_HANDLED;
+ } else {
+ return DEFER; // callback will be invoked later
+ }
+ }
+ }
+ return FORWARD;
+ }
+
+ @Override
+ public void onFinishedInputEvent(Object token, boolean handled) {
+ QueuedInputEvent q = (QueuedInputEvent)token;
+ if (handled) {
+ finish(q, true);
+ return;
+ }
+
+ // If the window doesn't currently have input focus, then drop
+ // this event. This could be an event that came back from the
+ // IME dispatch but the window has lost focus in the meantime.
+ if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
+ Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+ finish(q, false);
+ return;
+ }
+
+ forward(q);
+ }
+ }
+
+ /**
+ * Performs early processing of post-ime input events.
+ */
+ final class EarlyPostImeInputStage extends InputStage {
+ public EarlyPostImeInputStage(InputStage next) {
+ super(next);
+ }
+
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
- return deliverKeyEventPostIme(q);
+ return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
- if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- return deliverTrackballEventPostIme(q);
- } else {
- return deliverGenericMotionEventPostIme(q);
+ if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ return processPointerEvent(q);
}
}
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ return FORWARD;
+ }
+
+ private int processKeyEvent(QueuedInputEvent q) {
+ final KeyEvent event = (KeyEvent)q.mEvent;
+
+ // If the key's purpose is to exit touch mode then we consume it
+ // and consider it handled.
+ if (checkForLeavingTouchModeAndConsume(event)) {
+ return FINISH_HANDLED;
+ }
+
+ // Make sure the fallback event policy sees all keys that will be
+ // delivered to the view hierarchy.
+ mFallbackEventHandler.preDispatchKeyEvent(event);
+ return FORWARD;
+ }
+
+ private int processPointerEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+
+ // Translate the pointer event for compatibility, if needed.
+ if (mTranslator != null) {
+ mTranslator.translateEventInScreenToAppWindow(event);
+ }
+
+ // Enter touch mode on down or scroll.
+ final int action = event.getAction();
+ if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
+ ensureTouchMode(true);
+ }
+
+ // Offset the scroll position.
+ if (mCurScrollY != 0) {
+ event.offsetLocation(0, mCurScrollY);
+ }
+
+ // Remember the touch position for possible drag-initiation.
+ if (event.isTouchEvent()) {
+ mLastTouchPoint.x = event.getRawX();
+ mLastTouchPoint.y = event.getRawY();
+ }
+ return FORWARD;
}
}
- private int deliverPointerEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- final boolean isTouchEvent = event.isTouchEvent();
- if (mInputEventConsistencyVerifier != null) {
- if (isTouchEvent) {
- mInputEventConsistencyVerifier.onTouchEvent(event, 0);
+ /**
+ * Delivers post-ime input events to a native activity.
+ */
+ final class NativePostImeInputStage extends AsyncInputStage {
+ public NativePostImeInputStage(InputStage next, String traceCounter) {
+ super(next, traceCounter);
+ }
+
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
+ return FORWARD;
+ }
+ }
+
+ /**
+ * Delivers post-ime input events to the view hierarchy.
+ */
+ final class ViewPostImeInputStage extends InputStage {
+ public ViewPostImeInputStage(InputStage next) {
+ super(next);
+ }
+
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
+ if (q.mEvent instanceof KeyEvent) {
+ return processKeyEvent(q);
} else {
- mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
+ final int source = q.mEvent.getSource();
+ if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ return processPointerEvent(q);
+ } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ return processTrackballEvent(q);
+ } else {
+ return processGenericMotionEvent(q);
+ }
}
}
- // If there is no view, then the event will not be handled.
- if (mView == null || !mAdded) {
- return EVENT_NOT_HANDLED;
+ private int processKeyEvent(QueuedInputEvent q) {
+ final KeyEvent event = (KeyEvent)q.mEvent;
+
+ // Deliver the key to the view hierarchy.
+ if (mView.dispatchKeyEvent(event)) {
+ return FINISH_HANDLED;
+ }
+
+ // If the Control modifier is held, try to interpret the key as a shortcut.
+ if (event.getAction() == KeyEvent.ACTION_DOWN
+ && event.isCtrlPressed()
+ && event.getRepeatCount() == 0
+ && !KeyEvent.isModifierKey(event.getKeyCode())) {
+ if (mView.dispatchKeyShortcutEvent(event)) {
+ return FINISH_HANDLED;
+ }
+ }
+
+ // Apply the fallback event policy.
+ if (mFallbackEventHandler.dispatchKeyEvent(event)) {
+ return FINISH_HANDLED;
+ }
+
+ // Handle automatic focus changes.
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ int direction = 0;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_LEFT;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_RIGHT;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_UP;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_DOWN;
+ }
+ break;
+ case KeyEvent.KEYCODE_TAB:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_FORWARD;
+ } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
+ direction = View.FOCUS_BACKWARD;
+ }
+ break;
+ }
+ if (direction != 0) {
+ View focused = mView.findFocus();
+ if (focused != null) {
+ View v = focused.focusSearch(direction);
+ if (v != null && v != focused) {
+ // do the math the get the interesting rect
+ // of previous focused into the coord system of
+ // newly focused view
+ focused.getFocusedRect(mTempRect);
+ if (mView instanceof ViewGroup) {
+ ((ViewGroup) mView).offsetDescendantRectToMyCoords(
+ focused, mTempRect);
+ ((ViewGroup) mView).offsetRectIntoDescendantCoords(
+ v, mTempRect);
+ }
+ if (v.requestFocus(direction, mTempRect)) {
+ playSoundEffect(SoundEffectConstants
+ .getContantForFocusDirection(direction));
+ return FINISH_HANDLED;
+ }
+ }
+
+ // Give the focused view a last chance to handle the dpad key.
+ if (mView.dispatchUnhandledMove(focused, direction)) {
+ return FINISH_HANDLED;
+ }
+ } else {
+ // find the best view to give focus to in this non-touch-mode with no-focus
+ View v = focusSearch(null, direction);
+ if (v != null && v.requestFocus(direction)) {
+ return FINISH_HANDLED;
+ }
+ }
+ }
+ }
+ return FORWARD;
}
- // Translate the pointer event for compatibility, if needed.
- if (mTranslator != null) {
- mTranslator.translateEventInScreenToAppWindow(event);
+ private int processPointerEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+
+ if (mView.dispatchPointerEvent(event)) {
+ return FINISH_HANDLED;
+ }
+ return FORWARD;
}
- // Enter touch mode on down or scroll.
- final int action = event.getAction();
- if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
- ensureTouchMode(true);
+ private int processTrackballEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+
+ if (mView.dispatchTrackballEvent(event)) {
+ return FINISH_HANDLED;
+ }
+ return FORWARD;
}
- // Offset the scroll position.
- if (mCurScrollY != 0) {
- event.offsetLocation(0, mCurScrollY);
+ private int processGenericMotionEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+
+ // Deliver the event to the view.
+ if (mView.dispatchGenericMotionEvent(event)) {
+ return FINISH_HANDLED;
+ }
+ return FORWARD;
}
- if (MEASURE_LATENCY) {
- lt.sample("A Dispatching PointerEvents", System.nanoTime() - event.getEventTimeNano());
+ }
+
+ /**
+ * Performs synthesis of new input events from unhandled input events.
+ */
+ final class SyntheticInputStage extends InputStage {
+ private final SyntheticTrackballHandler mTrackball = new SyntheticTrackballHandler();
+ private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler();
+ private final SyntheticTouchNavigationHandler mTouchNavigation =
+ new SyntheticTouchNavigationHandler();
+
+ public SyntheticInputStage() {
+ super(null);
}
- // Remember the touch position for possible drag-initiation.
- if (isTouchEvent) {
- mLastTouchPoint.x = event.getRawX();
- mLastTouchPoint.y = event.getRawY();
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
+ q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED;
+ if (q.mEvent instanceof MotionEvent) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+ final int source = event.getSource();
+ if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ mTrackball.process(event);
+ return FINISH_HANDLED;
+ } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+ mJoystick.process(event);
+ return FINISH_HANDLED;
+ } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
+ == InputDevice.SOURCE_TOUCH_NAVIGATION) {
+ mTouchNavigation.process(event);
+ return FINISH_HANDLED;
+ }
+ }
+ return FORWARD;
}
- // Dispatch touch to view hierarchy.
- boolean handled = mView.dispatchPointerEvent(event);
- if (MEASURE_LATENCY) {
- lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
+ @Override
+ protected void onDeliverToNext(QueuedInputEvent q) {
+ if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) {
+ // Cancel related synthetic events if any prior stage has handled the event.
+ if (q.mEvent instanceof MotionEvent) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+ final int source = event.getSource();
+ if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ mTrackball.cancel(event);
+ } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+ mJoystick.cancel(event);
+ } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
+ == InputDevice.SOURCE_TOUCH_NAVIGATION) {
+ mTouchNavigation.cancel(event);
+ }
+ }
+ }
+ super.onDeliverToNext(q);
}
- return handled ? EVENT_HANDLED : EVENT_NOT_HANDLED;
}
- private int deliverTrackballEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
+ /**
+ * Creates dpad events from unhandled trackball movements.
+ */
+ final class SyntheticTrackballHandler {
+ private final TrackballAxis mX = new TrackballAxis();
+ private final TrackballAxis mY = new TrackballAxis();
+ private long mLastTime;
+
+ public void process(MotionEvent event) {
+ // Translate the trackball event into DPAD keys and try to deliver those.
+ long curTime = SystemClock.uptimeMillis();
+ if ((mLastTime + MAX_TRACKBALL_DELAY) < curTime) {
+ // It has been too long since the last movement,
+ // so restart at the beginning.
+ mX.reset(0);
+ mY.reset(0);
+ mLastTime = curTime;
+ }
+
+ final int action = event.getAction();
+ final int metaState = event.getMetaState();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mX.reset(2);
+ mY.reset(2);
+ enqueueInputEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD));
+ break;
+ case MotionEvent.ACTION_UP:
+ mX.reset(2);
+ mY.reset(2);
+ enqueueInputEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD));
+ break;
+ }
+
+ if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + mX.position + " step="
+ + mX.step + " dir=" + mX.dir + " acc=" + mX.acceleration
+ + " move=" + event.getX()
+ + " / Y=" + mY.position + " step="
+ + mY.step + " dir=" + mY.dir + " acc=" + mY.acceleration
+ + " move=" + event.getY());
+ final float xOff = mX.collect(event.getX(), event.getEventTime(), "X");
+ final float yOff = mY.collect(event.getY(), event.getEventTime(), "Y");
+
+ // Generate DPAD events based on the trackball movement.
+ // We pick the axis that has moved the most as the direction of
+ // the DPAD. When we generate DPAD events for one axis, then the
+ // other axis is reset -- we don't want to perform DPAD jumps due
+ // to slight movements in the trackball when making major movements
+ // along the other axis.
+ int keycode = 0;
+ int movement = 0;
+ float accel = 1;
+ if (xOff > yOff) {
+ movement = mX.generate();
+ if (movement != 0) {
+ keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
+ : KeyEvent.KEYCODE_DPAD_LEFT;
+ accel = mX.acceleration;
+ mY.reset(2);
+ }
+ } else if (yOff > 0) {
+ movement = mY.generate();
+ if (movement != 0) {
+ keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
+ : KeyEvent.KEYCODE_DPAD_UP;
+ accel = mY.acceleration;
+ mX.reset(2);
+ }
+ }
+
+ if (keycode != 0) {
+ if (movement < 0) movement = -movement;
+ int accelMovement = (int)(movement * accel);
+ if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
+ + " accelMovement=" + accelMovement
+ + " accel=" + accel);
+ if (accelMovement > movement) {
+ if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
+ + keycode);
+ movement--;
+ int repeatCount = accelMovement - movement;
+ enqueueInputEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD));
+ }
+ while (movement > 0) {
+ if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
+ + keycode);
+ movement--;
+ curTime = SystemClock.uptimeMillis();
+ enqueueInputEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_DOWN, keycode, 0, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD));
+ enqueueInputEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_UP, keycode, 0, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD));
+ }
+ mLastTime = curTime;
+ }
}
- int result = EVENT_POST_IME;
- if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
- if (LOCAL_LOGV)
- Log.v(TAG, "Dispatching trackball " + event + " to " + mView);
+ public void cancel(MotionEvent event) {
+ mLastTime = Integer.MIN_VALUE;
- // Dispatch to the IME before propagating down the view hierarchy.
- result = dispatchImeInputEvent(q);
+ // If we reach this, we consumed a trackball event.
+ // Because we will not translate the trackball event into a key event,
+ // touch mode will not exit, so we exit touch mode here.
+ if (mView != null && mAdded) {
+ ensureTouchMode(false);
+ }
}
- return result;
}
- private int deliverTrackballEventPostIme(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent) q.mEvent;
+ /**
+ * Maintains state information for a single trackball axis, generating
+ * discrete (DPAD) movements based on raw trackball motion.
+ */
+ static final class TrackballAxis {
+ /**
+ * The maximum amount of acceleration we will apply.
+ */
+ static final float MAX_ACCELERATION = 20;
- // If there is no view, then the event will not be handled.
- if (mView == null || !mAdded) {
- return EVENT_NOT_HANDLED;
+ /**
+ * The maximum amount of time (in milliseconds) between events in order
+ * for us to consider the user to be doing fast trackball movements,
+ * and thus apply an acceleration.
+ */
+ static final long FAST_MOVE_TIME = 150;
+
+ /**
+ * Scaling factor to the time (in milliseconds) between events to how
+ * much to multiple/divide the current acceleration. When movement
+ * is < FAST_MOVE_TIME this multiplies the acceleration; when >
+ * FAST_MOVE_TIME it divides it.
+ */
+ static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
+
+ static final float FIRST_MOVEMENT_THRESHOLD = 0.5f;
+ static final float SECOND_CUMULATIVE_MOVEMENT_THRESHOLD = 2.0f;
+ static final float SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD = 1.0f;
+
+ float position;
+ float acceleration = 1;
+ long lastMoveTime = 0;
+ int step;
+ int dir;
+ int nonAccelMovement;
+
+ void reset(int _step) {
+ position = 0;
+ acceleration = 1;
+ lastMoveTime = 0;
+ step = _step;
+ dir = 0;
}
- // Deliver the trackball event to the view.
- if (mView.dispatchTrackballEvent(event)) {
- // If we reach this, we delivered a trackball event to mView and
- // mView consumed it. Because we will not translate the trackball
- // event into a key event, touch mode will not exit, so we exit
- // touch mode here.
- ensureTouchMode(false);
- mLastTrackballTime = Integer.MIN_VALUE;
- return EVENT_HANDLED;
+ /**
+ * Add trackball movement into the state. If the direction of movement
+ * has been reversed, the state is reset before adding the
+ * movement (so that you don't have to compensate for any previously
+ * collected movement before see the result of the movement in the
+ * new direction).
+ *
+ * @return Returns the absolute value of the amount of movement
+ * collected so far.
+ */
+ float collect(float off, long time, String axis) {
+ long normTime;
+ if (off > 0) {
+ normTime = (long)(off * FAST_MOVE_TIME);
+ if (dir < 0) {
+ if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!");
+ position = 0;
+ step = 0;
+ acceleration = 1;
+ lastMoveTime = 0;
+ }
+ dir = 1;
+ } else if (off < 0) {
+ normTime = (long)((-off) * FAST_MOVE_TIME);
+ if (dir > 0) {
+ if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!");
+ position = 0;
+ step = 0;
+ acceleration = 1;
+ lastMoveTime = 0;
+ }
+ dir = -1;
+ } else {
+ normTime = 0;
+ }
+
+ // The number of milliseconds between each movement that is
+ // considered "normal" and will not result in any acceleration
+ // or deceleration, scaled by the offset we have here.
+ if (normTime > 0) {
+ long delta = time - lastMoveTime;
+ lastMoveTime = time;
+ float acc = acceleration;
+ if (delta < normTime) {
+ // The user is scrolling rapidly, so increase acceleration.
+ float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR;
+ if (scale > 1) acc *= scale;
+ if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off="
+ + off + " normTime=" + normTime + " delta=" + delta
+ + " scale=" + scale + " acc=" + acc);
+ acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION;
+ } else {
+ // The user is scrolling slowly, so decrease acceleration.
+ float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR;
+ if (scale > 1) acc /= scale;
+ if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off="
+ + off + " normTime=" + normTime + " delta=" + delta
+ + " scale=" + scale + " acc=" + acc);
+ acceleration = acc > 1 ? acc : 1;
+ }
+ }
+ position += off;
+ return Math.abs(position);
+ }
+
+ /**
+ * Generate the number of discrete movement events appropriate for
+ * the currently collected trackball movement.
+ *
+ * @return Returns the number of discrete movements, either positive
+ * or negative, or 0 if there is not enough trackball movement yet
+ * for a discrete movement.
+ */
+ int generate() {
+ int movement = 0;
+ nonAccelMovement = 0;
+ do {
+ final int dir = position >= 0 ? 1 : -1;
+ switch (step) {
+ // If we are going to execute the first step, then we want
+ // to do this as soon as possible instead of waiting for
+ // a full movement, in order to make things look responsive.
+ case 0:
+ if (Math.abs(position) < FIRST_MOVEMENT_THRESHOLD) {
+ return movement;
+ }
+ movement += dir;
+ nonAccelMovement += dir;
+ step = 1;
+ break;
+ // If we have generated the first movement, then we need
+ // to wait for the second complete trackball motion before
+ // generating the second discrete movement.
+ case 1:
+ if (Math.abs(position) < SECOND_CUMULATIVE_MOVEMENT_THRESHOLD) {
+ return movement;
+ }
+ movement += dir;
+ nonAccelMovement += dir;
+ position -= SECOND_CUMULATIVE_MOVEMENT_THRESHOLD * dir;
+ step = 2;
+ break;
+ // After the first two, we generate discrete movements
+ // consistently with the trackball, applying an acceleration
+ // if the trackball is moving quickly. This is a simple
+ // acceleration on top of what we already compute based
+ // on how quickly the wheel is being turned, to apply
+ // a longer increasing acceleration to continuous movement
+ // in one direction.
+ default:
+ if (Math.abs(position) < SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD) {
+ return movement;
+ }
+ movement += dir;
+ position -= dir * SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD;
+ float acc = acceleration;
+ acc *= 1.1f;
+ acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
+ break;
+ }
+ } while (true);
}
+ }
+
+ /**
+ * Creates dpad events from unhandled joystick movements.
+ */
+ final class SyntheticJoystickHandler extends Handler {
+ private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 1;
+ private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 2;
- // Translate the trackball event into DPAD keys and try to deliver those.
- final TrackballAxis x = mTrackballAxisX;
- final TrackballAxis y = mTrackballAxisY;
+ private int mLastXDirection;
+ private int mLastYDirection;
+ private int mLastXKeyCode;
+ private int mLastYKeyCode;
- long curTime = SystemClock.uptimeMillis();
- if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) {
- // It has been too long since the last movement,
- // so restart at the beginning.
- x.reset(0);
- y.reset(0);
- mLastTrackballTime = curTime;
+ public SyntheticJoystickHandler() {
+ super(true);
}
- final int action = event.getAction();
- final int metaState = event.getMetaState();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- x.reset(2);
- y.reset(2);
- enqueueInputEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD));
- break;
- case MotionEvent.ACTION_UP:
- x.reset(2);
- y.reset(2);
- enqueueInputEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD));
- break;
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
+ case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: {
+ KeyEvent oldEvent = (KeyEvent)msg.obj;
+ KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent,
+ SystemClock.uptimeMillis(),
+ oldEvent.getRepeatCount() + 1);
+ if (mAttachInfo.mHasWindowFocus) {
+ enqueueInputEvent(e);
+ Message m = obtainMessage(msg.what, e);
+ m.setAsynchronous(true);
+ sendMessageDelayed(m, ViewConfiguration.getKeyRepeatDelay());
+ }
+ } break;
+ }
}
- if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
- + x.step + " dir=" + x.dir + " acc=" + x.acceleration
- + " move=" + event.getX()
- + " / Y=" + y.position + " step="
- + y.step + " dir=" + y.dir + " acc=" + y.acceleration
- + " move=" + event.getY());
- final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
- final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
-
- // Generate DPAD events based on the trackball movement.
- // We pick the axis that has moved the most as the direction of
- // the DPAD. When we generate DPAD events for one axis, then the
- // other axis is reset -- we don't want to perform DPAD jumps due
- // to slight movements in the trackball when making major movements
- // along the other axis.
- int keycode = 0;
- int movement = 0;
- float accel = 1;
- if (xOff > yOff) {
- movement = x.generate((2/event.getXPrecision()));
- if (movement != 0) {
- keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
- : KeyEvent.KEYCODE_DPAD_LEFT;
- accel = x.acceleration;
- y.reset(2);
- }
- } else if (yOff > 0) {
- movement = y.generate((2/event.getYPrecision()));
- if (movement != 0) {
- keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
- : KeyEvent.KEYCODE_DPAD_UP;
- accel = y.acceleration;
- x.reset(2);
- }
- }
-
- if (keycode != 0) {
- if (movement < 0) movement = -movement;
- int accelMovement = (int)(movement * accel);
- if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
- + " accelMovement=" + accelMovement
- + " accel=" + accel);
- if (accelMovement > movement) {
- if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
- + keycode);
- movement--;
- int repeatCount = accelMovement - movement;
- enqueueInputEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD));
- }
- while (movement > 0) {
- if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
- + keycode);
- movement--;
- curTime = SystemClock.uptimeMillis();
- enqueueInputEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_DOWN, keycode, 0, metaState,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD));
- enqueueInputEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_UP, keycode, 0, metaState,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD));
- }
- mLastTrackballTime = curTime;
- }
-
- // Unfortunately we can't tell whether the application consumed the keys, so
- // we always consider the trackball event handled.
- return EVENT_HANDLED;
- }
-
- private int deliverGenericMotionEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
- }
-
- int result = EVENT_POST_IME;
- if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
- if (LOCAL_LOGV)
- Log.v(TAG, "Dispatching generic motion " + event + " to " + mView);
-
- // Dispatch to the IME before propagating down the view hierarchy.
- result = dispatchImeInputEvent(q);
- }
- return result;
- }
-
- private int deliverGenericMotionEventPostIme(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent) q.mEvent;
- final int source = event.getSource();
- final boolean isJoystick = event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK);
- final boolean isTouchNavigation = event.isFromSource(InputDevice.SOURCE_TOUCH_NAVIGATION);
-
- // If there is no view, then the event will not be handled.
- if (mView == null || !mAdded) {
- if (isJoystick) {
- updateJoystickDirection(event, false);
- } else if (isTouchNavigation) {
- mSimulatedDpad.updateTouchNavigation(this, event, false);
- }
- return EVENT_NOT_HANDLED;
- }
-
- // Deliver the event to the view.
- if (mView.dispatchGenericMotionEvent(event)) {
- if (isJoystick) {
- updateJoystickDirection(event, false);
- } else if (isTouchNavigation) {
- mSimulatedDpad.updateTouchNavigation(this, event, false);
- }
- return EVENT_HANDLED;
- }
-
- if (isJoystick) {
- // Translate the joystick event into DPAD keys and try to deliver
- // those.
- updateJoystickDirection(event, true);
- return EVENT_HANDLED;
+ public void process(MotionEvent event) {
+ update(event, true);
}
- if (isTouchNavigation) {
- mSimulatedDpad.updateTouchNavigation(this, event, true);
- return EVENT_HANDLED;
+
+ public void cancel(MotionEvent event) {
+ update(event, false);
+ }
+
+ private void update(MotionEvent event, boolean synthesizeNewKeys) {
+ final long time = event.getEventTime();
+ final int metaState = event.getMetaState();
+ final int deviceId = event.getDeviceId();
+ final int source = event.getSource();
+
+ int xDirection = joystickAxisValueToDirection(
+ event.getAxisValue(MotionEvent.AXIS_HAT_X));
+ if (xDirection == 0) {
+ xDirection = joystickAxisValueToDirection(event.getX());
+ }
+
+ int yDirection = joystickAxisValueToDirection(
+ event.getAxisValue(MotionEvent.AXIS_HAT_Y));
+ if (yDirection == 0) {
+ yDirection = joystickAxisValueToDirection(event.getY());
+ }
+
+ if (xDirection != mLastXDirection) {
+ if (mLastXKeyCode != 0) {
+ removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
+ enqueueInputEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_UP, mLastXKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+ mLastXKeyCode = 0;
+ }
+
+ mLastXDirection = xDirection;
+
+ if (xDirection != 0 && synthesizeNewKeys) {
+ mLastXKeyCode = xDirection > 0
+ ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
+ final KeyEvent e = new KeyEvent(time, time,
+ KeyEvent.ACTION_DOWN, mLastXKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+ enqueueInputEvent(e);
+ Message m = obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
+ m.setAsynchronous(true);
+ sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
+ }
+ }
+
+ if (yDirection != mLastYDirection) {
+ if (mLastYKeyCode != 0) {
+ removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
+ enqueueInputEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_UP, mLastYKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+ mLastYKeyCode = 0;
+ }
+
+ mLastYDirection = yDirection;
+
+ if (yDirection != 0 && synthesizeNewKeys) {
+ mLastYKeyCode = yDirection > 0
+ ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
+ final KeyEvent e = new KeyEvent(time, time,
+ KeyEvent.ACTION_DOWN, mLastYKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+ enqueueInputEvent(e);
+ Message m = obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
+ m.setAsynchronous(true);
+ sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
+ }
+ }
+ }
+
+ private int joystickAxisValueToDirection(float value) {
+ if (value >= 0.5f) {
+ return 1;
+ } else if (value <= -0.5f) {
+ return -1;
+ } else {
+ return 0;
+ }
}
- return EVENT_NOT_HANDLED;
}
- private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
- final long time = event.getEventTime();
- final int metaState = event.getMetaState();
- final int deviceId = event.getDeviceId();
- final int source = event.getSource();
+ /**
+ * Creates dpad events from unhandled touch navigation movements.
+ */
+ final class SyntheticTouchNavigationHandler extends Handler {
+ private static final String LOCAL_TAG = "SyntheticTouchNavigationHandler";
+ private static final boolean LOCAL_DEBUG = false;
+
+ // Assumed nominal width and height in millimeters of a touch navigation pad,
+ // if no resolution information is available from the input system.
+ private static final float DEFAULT_WIDTH_MILLIMETERS = 48;
+ private static final float DEFAULT_HEIGHT_MILLIMETERS = 48;
+
+ /* TODO: These constants should eventually be moved to ViewConfiguration. */
+
+ // Tap timeout in milliseconds.
+ private static final int TAP_TIMEOUT = 250;
+
+ // The maximum distance traveled for a gesture to be considered a tap in millimeters.
+ private static final int TAP_SLOP_MILLIMETERS = 5;
+
+ // The nominal distance traveled to move by one unit.
+ private static final int TICK_DISTANCE_MILLIMETERS = 12;
+
+ // Minimum and maximum fling velocity in ticks per second.
+ // The minimum velocity should be set such that we perform enough ticks per
+ // second that the fling appears to be fluid. For example, if we set the minimum
+ // to 2 ticks per second, then there may be up to half a second delay between the next
+ // to last and last ticks which is noticeably discrete and jerky. This value should
+ // probably not be set to anything less than about 4.
+ // If fling accuracy is a problem then consider tuning the tick distance instead.
+ private static final float MIN_FLING_VELOCITY_TICKS_PER_SECOND = 6f;
+ private static final float MAX_FLING_VELOCITY_TICKS_PER_SECOND = 20f;
+
+ // Fling velocity decay factor applied after each new key is emitted.
+ // This parameter controls the deceleration and overall duration of the fling.
+ // The fling stops automatically when its velocity drops below the minimum
+ // fling velocity defined above.
+ private static final float FLING_TICK_DECAY = 0.8f;
+
+ /* The input device that we are tracking. */
+
+ private int mCurrentDeviceId = -1;
+ private int mCurrentSource;
+ private boolean mCurrentDeviceSupported;
+
+ /* Configuration for the current input device. */
+
+ // The tap timeout and scaled slop.
+ private int mConfigTapTimeout;
+ private float mConfigTapSlop;
+
+ // The scaled tick distance. A movement of this amount should generally translate
+ // into a single dpad event in a given direction.
+ private float mConfigTickDistance;
+
+ // The minimum and maximum scaled fling velocity.
+ private float mConfigMinFlingVelocity;
+ private float mConfigMaxFlingVelocity;
+
+ /* Tracking state. */
+
+ // The velocity tracker for detecting flings.
+ private VelocityTracker mVelocityTracker;
+
+ // The active pointer id, or -1 if none.
+ private int mActivePointerId = -1;
+
+ // Time and location where tracking started.
+ private long mStartTime;
+ private float mStartX;
+ private float mStartY;
+
+ // Most recently observed position.
+ private float mLastX;
+ private float mLastY;
+
+ // Accumulated movement delta since the last direction key was sent.
+ private float mAccumulatedX;
+ private float mAccumulatedY;
+
+ // Set to true if any movement was delivered to the app.
+ // Implies that tap slop was exceeded.
+ private boolean mConsumedMovement;
+
+ // The most recently sent key down event.
+ // The keycode remains set until the direction changes or a fling ends
+ // so that repeated key events may be generated as required.
+ private long mPendingKeyDownTime;
+ private int mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN;
+ private int mPendingKeyRepeatCount;
+ private int mPendingKeyMetaState;
+
+ // The current fling velocity while a fling is in progress.
+ private boolean mFlinging;
+ private float mFlingVelocity;
+
+ public SyntheticTouchNavigationHandler() {
+ super(true);
+ }
+
+ public void process(MotionEvent event) {
+ // Update the current device information.
+ final long time = event.getEventTime();
+ final int deviceId = event.getDeviceId();
+ final int source = event.getSource();
+ if (mCurrentDeviceId != deviceId || mCurrentSource != source) {
+ finishKeys(time);
+ finishTracking(time);
+ mCurrentDeviceId = deviceId;
+ mCurrentSource = source;
+ mCurrentDeviceSupported = false;
+ InputDevice device = event.getDevice();
+ if (device != null) {
+ // In order to support an input device, we must know certain
+ // characteristics about it, such as its size and resolution.
+ InputDevice.MotionRange xRange = device.getMotionRange(MotionEvent.AXIS_X);
+ InputDevice.MotionRange yRange = device.getMotionRange(MotionEvent.AXIS_Y);
+ if (xRange != null && yRange != null) {
+ mCurrentDeviceSupported = true;
+
+ // Infer the resolution if it not actually known.
+ float xRes = xRange.getResolution();
+ if (xRes <= 0) {
+ xRes = xRange.getRange() / DEFAULT_WIDTH_MILLIMETERS;
+ }
+ float yRes = yRange.getResolution();
+ if (yRes <= 0) {
+ yRes = yRange.getRange() / DEFAULT_HEIGHT_MILLIMETERS;
+ }
+ float nominalRes = (xRes + yRes) * 0.5f;
+
+ // Precompute all of the configuration thresholds we will need.
+ mConfigTapTimeout = TAP_TIMEOUT;
+ mConfigTapSlop = TAP_SLOP_MILLIMETERS * nominalRes;
+ mConfigTickDistance = TICK_DISTANCE_MILLIMETERS * nominalRes;
+ mConfigMinFlingVelocity =
+ MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
+ mConfigMaxFlingVelocity =
+ MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
+
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId
+ + " (" + Integer.toHexString(mCurrentSource) + "): "
+ + "mConfigTapTimeout=" + mConfigTapTimeout
+ + ", mConfigTapSlop=" + mConfigTapSlop
+ + ", mConfigTickDistance=" + mConfigTickDistance
+ + ", mConfigMinFlingVelocity=" + mConfigMinFlingVelocity
+ + ", mConfigMaxFlingVelocity=" + mConfigMaxFlingVelocity);
+ }
+ }
+ }
+ }
+ if (!mCurrentDeviceSupported) {
+ return;
+ }
+
+ // Handle the event.
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ boolean caughtFling = mFlinging;
+ finishKeys(time);
+ finishTracking(time);
+ mActivePointerId = event.getPointerId(0);
+ mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker.addMovement(event);
+ mStartTime = time;
+ mStartX = event.getX();
+ mStartY = event.getY();
+ mLastX = mStartX;
+ mLastY = mStartY;
+ mAccumulatedX = 0;
+ mAccumulatedY = 0;
+
+ // If we caught a fling, then pretend that the tap slop has already
+ // been exceeded to suppress taps whose only purpose is to stop the fling.
+ mConsumedMovement = caughtFling;
+ break;
+ }
- int xDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_X));
- if (xDirection == 0) {
- xDirection = joystickAxisValueToDirection(event.getX());
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_UP: {
+ if (mActivePointerId < 0) {
+ break;
+ }
+ final int index = event.findPointerIndex(mActivePointerId);
+ if (index < 0) {
+ finishKeys(time);
+ finishTracking(time);
+ break;
+ }
+
+ mVelocityTracker.addMovement(event);
+ final float x = event.getX(index);
+ final float y = event.getY(index);
+ mAccumulatedX += x - mLastX;
+ mAccumulatedY += y - mLastY;
+ mLastX = x;
+ mLastY = y;
+
+ // Consume any accumulated movement so far.
+ final int metaState = event.getMetaState();
+ consumeAccumulatedMovement(time, metaState);
+
+ // Detect taps and flings.
+ if (action == MotionEvent.ACTION_UP) {
+ if (!mConsumedMovement
+ && Math.hypot(mLastX - mStartX, mLastY - mStartY) < mConfigTapSlop
+ && time <= mStartTime + mConfigTapTimeout) {
+ // It's a tap!
+ finishKeys(time);
+ sendKeyDownOrRepeat(time, KeyEvent.KEYCODE_DPAD_CENTER, metaState);
+ sendKeyUp(time);
+ } else if (mConsumedMovement
+ && mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
+ // It might be a fling.
+ mVelocityTracker.computeCurrentVelocity(1000, mConfigMaxFlingVelocity);
+ final float vx = mVelocityTracker.getXVelocity(mActivePointerId);
+ final float vy = mVelocityTracker.getYVelocity(mActivePointerId);
+ if (!startFling(time, vx, vy)) {
+ finishKeys(time);
+ }
+ }
+ finishTracking(time);
+ }
+ break;
+ }
+
+ case MotionEvent.ACTION_CANCEL: {
+ finishKeys(time);
+ finishTracking(time);
+ break;
+ }
+ }
}
- int yDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_Y));
- if (yDirection == 0) {
- yDirection = joystickAxisValueToDirection(event.getY());
+ public void cancel(MotionEvent event) {
+ if (mCurrentDeviceId == event.getDeviceId()
+ && mCurrentSource == event.getSource()) {
+ final long time = event.getEventTime();
+ finishKeys(time);
+ finishTracking(time);
+ }
}
- if (xDirection != mLastJoystickXDirection) {
- if (mLastJoystickXKeyCode != 0) {
- mHandler.removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
- enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
- deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
- mLastJoystickXKeyCode = 0;
+ private void finishKeys(long time) {
+ cancelFling();
+ sendKeyUp(time);
+ }
+
+ private void finishTracking(long time) {
+ if (mActivePointerId >= 0) {
+ mActivePointerId = -1;
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
}
+ }
- mLastJoystickXDirection = xDirection;
+ private void consumeAccumulatedMovement(long time, int metaState) {
+ final float absX = Math.abs(mAccumulatedX);
+ final float absY = Math.abs(mAccumulatedY);
+ if (absX >= absY) {
+ if (absX >= mConfigTickDistance) {
+ mAccumulatedX = consumeAccumulatedMovement(time, metaState, mAccumulatedX,
+ KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT);
+ mAccumulatedY = 0;
+ mConsumedMovement = true;
+ }
+ } else {
+ if (absY >= mConfigTickDistance) {
+ mAccumulatedY = consumeAccumulatedMovement(time, metaState, mAccumulatedY,
+ KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN);
+ mAccumulatedX = 0;
+ mConsumedMovement = true;
+ }
+ }
+ }
- if (xDirection != 0 && synthesizeNewKeys) {
- mLastJoystickXKeyCode = xDirection > 0
- ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
- final KeyEvent e = new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
- deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
- enqueueInputEvent(e);
- Message m = mHandler.obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
- m.setAsynchronous(true);
- mHandler.sendMessageDelayed(m, mViewConfiguration.getKeyRepeatTimeout());
+ private float consumeAccumulatedMovement(long time, int metaState,
+ float accumulator, int negativeKeyCode, int positiveKeyCode) {
+ while (accumulator <= -mConfigTickDistance) {
+ sendKeyDownOrRepeat(time, negativeKeyCode, metaState);
+ accumulator += mConfigTickDistance;
+ }
+ while (accumulator >= mConfigTickDistance) {
+ sendKeyDownOrRepeat(time, positiveKeyCode, metaState);
+ accumulator -= mConfigTickDistance;
}
+ return accumulator;
}
- if (yDirection != mLastJoystickYDirection) {
- if (mLastJoystickYKeyCode != 0) {
- mHandler.removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
- enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
- deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
- mLastJoystickYKeyCode = 0;
+ private void sendKeyDownOrRepeat(long time, int keyCode, int metaState) {
+ if (mPendingKeyCode != keyCode) {
+ sendKeyUp(time);
+ mPendingKeyDownTime = time;
+ mPendingKeyCode = keyCode;
+ mPendingKeyRepeatCount = 0;
+ } else {
+ mPendingKeyRepeatCount += 1;
}
+ mPendingKeyMetaState = metaState;
- mLastJoystickYDirection = yDirection;
+ // Note: Normally we would pass FLAG_LONG_PRESS when the repeat count is 1
+ // but it doesn't quite make sense when simulating the events in this way.
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Sending key down: keyCode=" + mPendingKeyCode
+ + ", repeatCount=" + mPendingKeyRepeatCount
+ + ", metaState=" + Integer.toHexString(mPendingKeyMetaState));
+ }
+ enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time,
+ KeyEvent.ACTION_DOWN, mPendingKeyCode, mPendingKeyRepeatCount,
+ mPendingKeyMetaState, mCurrentDeviceId,
+ KeyEvent.FLAG_FALLBACK, mCurrentSource));
+ }
- if (yDirection != 0 && synthesizeNewKeys) {
- mLastJoystickYKeyCode = yDirection > 0
- ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
- final KeyEvent e = new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
- deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
- enqueueInputEvent(e);
- Message m = mHandler.obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
- m.setAsynchronous(true);
- mHandler.sendMessageDelayed(m, mViewConfiguration.getKeyRepeatTimeout());
+ private void sendKeyUp(long time) {
+ if (mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Sending key up: keyCode=" + mPendingKeyCode
+ + ", metaState=" + Integer.toHexString(mPendingKeyMetaState));
+ }
+ enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time,
+ KeyEvent.ACTION_UP, mPendingKeyCode, 0, mPendingKeyMetaState,
+ mCurrentDeviceId, 0, KeyEvent.FLAG_FALLBACK,
+ mCurrentSource));
+ mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN;
}
}
- }
- private static int joystickAxisValueToDirection(float value) {
- if (value >= 0.5f) {
- return 1;
- } else if (value <= -0.5f) {
- return -1;
- } else {
- return 0;
+ private boolean startFling(long time, float vx, float vy) {
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Considering fling: vx=" + vx + ", vy=" + vy
+ + ", min=" + mConfigMinFlingVelocity);
+ }
+
+ // Flings must be oriented in the same direction as the preceding movements.
+ switch (mPendingKeyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (-vx >= mConfigMinFlingVelocity
+ && Math.abs(vy) < mConfigMinFlingVelocity) {
+ mFlingVelocity = -vx;
+ break;
+ }
+ return false;
+
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (vx >= mConfigMinFlingVelocity
+ && Math.abs(vy) < mConfigMinFlingVelocity) {
+ mFlingVelocity = vx;
+ break;
+ }
+ return false;
+
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (-vy >= mConfigMinFlingVelocity
+ && Math.abs(vx) < mConfigMinFlingVelocity) {
+ mFlingVelocity = -vy;
+ break;
+ }
+ return false;
+
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (vy >= mConfigMinFlingVelocity
+ && Math.abs(vx) < mConfigMinFlingVelocity) {
+ mFlingVelocity = vy;
+ break;
+ }
+ return false;
+ }
+
+ // Post the first fling event.
+ mFlinging = postFling(time);
+ return mFlinging;
}
+
+ private boolean postFling(long time) {
+ // The idea here is to estimate the time when the pointer would have
+ // traveled one tick distance unit given the current fling velocity.
+ // This effect creates continuity of motion.
+ if (mFlingVelocity >= mConfigMinFlingVelocity) {
+ long delay = (long)(mConfigTickDistance / mFlingVelocity * 1000);
+ postAtTime(mFlingRunnable, time + delay);
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Posted fling: velocity="
+ + mFlingVelocity + ", delay=" + delay
+ + ", keyCode=" + mPendingKeyCode);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void cancelFling() {
+ if (mFlinging) {
+ removeCallbacks(mFlingRunnable);
+ mFlinging = false;
+ }
+ }
+
+ private final Runnable mFlingRunnable = new Runnable() {
+ @Override
+ public void run() {
+ final long time = SystemClock.uptimeMillis();
+ sendKeyDownOrRepeat(time, mPendingKeyCode, mPendingKeyMetaState);
+ mFlingVelocity *= FLING_TICK_DECAY;
+ if (!postFling(time)) {
+ mFlinging = false;
+ finishKeys(time);
+ }
+ }
+ };
}
/**
@@ -3803,136 +4814,6 @@ public final class ViewRootImpl implements ViewParent,
return false;
}
- private int deliverKeyEvent(QueuedInputEvent q) {
- final KeyEvent event = (KeyEvent)q.mEvent;
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onKeyEvent(event, 0);
- }
-
- int result = EVENT_POST_IME;
- if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
- if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
-
- // Perform predispatching before the IME.
- if (mView.dispatchKeyEventPreIme(event)) {
- return EVENT_HANDLED;
- }
-
- // Dispatch to the IME before propagating down the view hierarchy.
- result = dispatchImeInputEvent(q);
- }
- return result;
- }
-
- private int deliverKeyEventPostIme(QueuedInputEvent q) {
- final KeyEvent event = (KeyEvent)q.mEvent;
-
- // If the view went away, then the event will not be handled.
- if (mView == null || !mAdded) {
- return EVENT_NOT_HANDLED;
- }
-
- // If the key's purpose is to exit touch mode then we consume it and consider it handled.
- if (checkForLeavingTouchModeAndConsume(event)) {
- return EVENT_HANDLED;
- }
-
- // Make sure the fallback event policy sees all keys that will be delivered to the
- // view hierarchy.
- mFallbackEventHandler.preDispatchKeyEvent(event);
-
- // Deliver the key to the view hierarchy.
- if (mView.dispatchKeyEvent(event)) {
- return EVENT_HANDLED;
- }
-
- // If the Control modifier is held, try to interpret the key as a shortcut.
- if (event.getAction() == KeyEvent.ACTION_DOWN
- && event.isCtrlPressed()
- && event.getRepeatCount() == 0
- && !KeyEvent.isModifierKey(event.getKeyCode())) {
- if (mView.dispatchKeyShortcutEvent(event)) {
- return EVENT_HANDLED;
- }
- }
-
- // Apply the fallback event policy.
- if (mFallbackEventHandler.dispatchKeyEvent(event)) {
- return EVENT_HANDLED;
- }
-
- // Handle automatic focus changes.
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- int direction = 0;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_DPAD_LEFT:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_LEFT;
- }
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_RIGHT;
- }
- break;
- case KeyEvent.KEYCODE_DPAD_UP:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_UP;
- }
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_DOWN;
- }
- break;
- case KeyEvent.KEYCODE_TAB:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_FORWARD;
- } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
- direction = View.FOCUS_BACKWARD;
- }
- break;
- }
- if (direction != 0) {
- View focused = mView.findFocus();
- if (focused != null) {
- View v = focused.focusSearch(direction);
- if (v != null && v != focused) {
- // do the math the get the interesting rect
- // of previous focused into the coord system of
- // newly focused view
- focused.getFocusedRect(mTempRect);
- if (mView instanceof ViewGroup) {
- ((ViewGroup) mView).offsetDescendantRectToMyCoords(
- focused, mTempRect);
- ((ViewGroup) mView).offsetRectIntoDescendantCoords(
- v, mTempRect);
- }
- if (v.requestFocus(direction, mTempRect)) {
- playSoundEffect(SoundEffectConstants
- .getContantForFocusDirection(direction));
- return EVENT_HANDLED;
- }
- }
-
- // Give the focused view a last chance to handle the dpad key.
- if (mView.dispatchUnhandledMove(focused, direction)) {
- return EVENT_HANDLED;
- }
- } else {
- // find the best view to give focus to in this non-touch-mode with no-focus
- View v = focusSearch(null, direction);
- if (v != null && v.requestFocus(direction)) {
- return EVENT_HANDLED;
- }
- }
- }
- }
-
- // Key was unhandled.
- return EVENT_NOT_HANDLED;
- }
-
/* drag/drop */
void setLocalDragState(Object obj) {
mLocalDragState = obj;
@@ -4042,7 +4923,7 @@ public final class ViewRootImpl implements ViewParent,
public void handleDispatchDoneAnimating() {
if (mWindowsAnimating) {
mWindowsAnimating = false;
- if (!mDirty.isEmpty() || mIsAnimating) {
+ if (!mDirty.isEmpty() || mIsAnimating || mFullRedrawNeeded) {
scheduleTraversals();
}
}
@@ -4364,13 +5245,25 @@ public final class ViewRootImpl implements ViewParent,
* needing a queue on the application's side.
*/
private static final class QueuedInputEvent {
- public static final int FLAG_DELIVER_POST_IME = 1;
+ public static final int FLAG_DELIVER_POST_IME = 1 << 0;
+ public static final int FLAG_DEFERRED = 1 << 1;
+ public static final int FLAG_FINISHED = 1 << 2;
+ public static final int FLAG_FINISHED_HANDLED = 1 << 3;
+ public static final int FLAG_RESYNTHESIZED = 1 << 4;
public QueuedInputEvent mNext;
public InputEvent mEvent;
public InputEventReceiver mReceiver;
public int mFlags;
+
+ public boolean shouldSkipIme() {
+ if ((mFlags & FLAG_DELIVER_POST_IME) != 0) {
+ return true;
+ }
+ return mEvent instanceof MotionEvent
+ && mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER);
+ }
}
private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
@@ -4443,14 +5336,7 @@ public final class ViewRootImpl implements ViewParent,
}
void doProcessInputEvents() {
- // Handle all of the available pending input events. Currently this will immediately
- // process all of the events it can until it encounters one that must go through the IME.
- // After that it will continue adding events to the active input queue but will wait for a
- // response from the IME, regardless of whether that particular event needs it or not, in
- // order to guarantee ordering consistency. This could be slightly improved by only
- // queueing events whose source has previously encountered something that needs to be
- // handled by the IME, and otherwise handling them immediately since we only need to
- // guarantee ordering within a given source.
+ // Deliver all pending input events in the queue.
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
@@ -4463,25 +5349,7 @@ public final class ViewRootImpl implements ViewParent,
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
- int result = deliverInputEvent(q);
-
- if (result == EVENT_HANDLED || result == EVENT_NOT_HANDLED) {
- finishInputEvent(q, result == EVENT_HANDLED);
- } else if (result == EVENT_PENDING_IME) {
- enqueueActiveInputEvent(q);
- } else {
- q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME;
- // If the IME decided not to handle this event, and we have no events already being
- // handled by the IME, go ahead and handle this one and then continue to the next
- // input event. Otherwise, queue it up and handle it after whatever in front of it
- // in the queue has been handled.
- if (mActiveInputEventHead == null) {
- result = deliverInputEventPostIme(q);
- finishInputEvent(q, result == EVENT_HANDLED);
- } else {
- enqueueActiveInputEvent(q);
- }
- }
+ deliverInputEvent(q);
}
// We are done processing all input events that we can process right now
@@ -4492,114 +5360,27 @@ public final class ViewRootImpl implements ViewParent,
}
}
- private void enqueueActiveInputEvent(QueuedInputEvent q) {
- if (mActiveInputEventHead == null) {
- mActiveInputEventHead = q;
- mActiveInputEventTail = q;
- } else {
- mActiveInputEventTail.mNext = q;
- mActiveInputEventTail = q;
- }
- mActiveInputEventCount += 1;
- Trace.traceCounter(Trace.TRACE_TAG_INPUT, mActiveInputEventQueueLengthCounterName,
- mActiveInputEventCount);
- }
-
- private QueuedInputEvent dequeueActiveInputEvent() {
- return dequeueActiveInputEvent(mActiveInputEventHead);
- }
-
-
- private QueuedInputEvent dequeueActiveInputEvent(QueuedInputEvent q) {
- QueuedInputEvent curr = mActiveInputEventHead;
- QueuedInputEvent prev = null;
- while (curr != null && curr != q) {
- prev = curr;
- curr = curr.mNext;
- }
- if (curr != null) {
- if (mActiveInputEventHead == curr) {
- mActiveInputEventHead = curr.mNext;
- } else {
- prev.mNext = curr.mNext;
- }
- if (mActiveInputEventTail == curr) {
- mActiveInputEventTail = prev;
- }
- curr.mNext = null;
-
- mActiveInputEventCount -= 1;
- Trace.traceCounter(Trace.TRACE_TAG_INPUT, mActiveInputEventQueueLengthCounterName,
- mActiveInputEventCount);
- }
- return curr;
- }
-
- private QueuedInputEvent findActiveInputEvent(int seq) {
- QueuedInputEvent q = mActiveInputEventHead;
- while (q != null && q.mEvent.getSequenceNumber() != seq) {
- q = q.mNext;
- }
- return q;
- }
-
- int dispatchImeInputEvent(QueuedInputEvent q) {
- if (mLastWasImTarget) {
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- final InputEvent event = q.mEvent;
- final int seq = event.getSequenceNumber();
- if (DEBUG_IMF)
- Log.v(TAG, "Sending input event to IME: seq=" + seq + " event=" + event);
- return imm.dispatchInputEvent(mView.getContext(), seq, event,
- mInputMethodCallback);
- }
- }
- return EVENT_POST_IME;
- }
-
- void handleImeFinishedEvent(int seq, boolean handled) {
- QueuedInputEvent q = findActiveInputEvent(seq);
- if (q != null) {
- if (DEBUG_IMF) {
- Log.v(TAG, "IME finished event: seq=" + seq
- + " handled=" + handled + " event=" + q);
+ private void deliverInputEvent(QueuedInputEvent q) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
+ try {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
- if (handled) {
- dequeueActiveInputEvent(q);
- finishInputEvent(q, true);
+ InputStage stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
+ if (stage != null) {
+ stage.deliver(q);
} else {
- q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME;
- }
-
-
- // Flush all of the input events that are no longer waiting on the IME
- while (mActiveInputEventHead != null && (mActiveInputEventHead.mFlags &
- QueuedInputEvent.FLAG_DELIVER_POST_IME) != 0) {
- q = dequeueActiveInputEvent();
- // If the window doesn't currently have input focus, then drop
- // this event. This could be an event that came back from the
- // IME dispatch but the window has lost focus in the meantime.
- handled = false;
- if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
- Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
- } else {
- handled = (deliverInputEventPostIme(q) == EVENT_HANDLED);
- }
- finishInputEvent(q, handled);
- }
- } else {
- if (DEBUG_IMF) {
- Log.v(TAG, "IME finished event: seq=" + seq
- + " handled=" + handled + ", event not found!");
+ finishInputEvent(q);
}
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
-
}
- private void finishInputEvent(QueuedInputEvent q, boolean handled) {
+ private void finishInputEvent(QueuedInputEvent q) {
if (q.mReceiver != null) {
+ boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
q.mReceiver.finishInputEvent(q.mEvent, handled);
} else {
q.mEvent.recycleIfNeededAfterDispatch();
@@ -4608,7 +5389,7 @@ public final class ViewRootImpl implements ViewParent,
recycleQueuedInputEvent(q);
}
- private static boolean isTerminalInputEvent(InputEvent event) {
+ static boolean isTerminalInputEvent(InputEvent event) {
if (event instanceof KeyEvent) {
final KeyEvent keyEvent = (KeyEvent)event;
return keyEvent.getAction() == KeyEvent.ACTION_UP;
@@ -5144,22 +5925,6 @@ public final class ViewRootImpl implements ViewParent,
((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn);
}
}
-
- static final class InputMethodCallback implements InputMethodManager.FinishedEventCallback {
- private WeakReference<ViewRootImpl> mViewAncestor;
-
- public InputMethodCallback(ViewRootImpl viewAncestor) {
- mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
- }
-
- @Override
- public void finishedEvent(int seq, boolean handled) {
- final ViewRootImpl viewAncestor = mViewAncestor.get();
- if (viewAncestor != null) {
- viewAncestor.handleImeFinishedEvent(seq, handled);
- }
- }
- }
static class W extends IWindow.Stub {
private final WeakReference<ViewRootImpl> mViewAncestor;
@@ -5307,176 +6072,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- /**
- * Maintains state information for a single trackball axis, generating
- * discrete (DPAD) movements based on raw trackball motion.
- */
- static final class TrackballAxis {
- /**
- * The maximum amount of acceleration we will apply.
- */
- static final float MAX_ACCELERATION = 20;
-
- /**
- * The maximum amount of time (in milliseconds) between events in order
- * for us to consider the user to be doing fast trackball movements,
- * and thus apply an acceleration.
- */
- static final long FAST_MOVE_TIME = 150;
-
- /**
- * Scaling factor to the time (in milliseconds) between events to how
- * much to multiple/divide the current acceleration. When movement
- * is < FAST_MOVE_TIME this multiplies the acceleration; when >
- * FAST_MOVE_TIME it divides it.
- */
- static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
-
- float position;
- float absPosition;
- float acceleration = 1;
- long lastMoveTime = 0;
- int step;
- int dir;
- int nonAccelMovement;
-
- void reset(int _step) {
- position = 0;
- acceleration = 1;
- lastMoveTime = 0;
- step = _step;
- dir = 0;
- }
-
- /**
- * Add trackball movement into the state. If the direction of movement
- * has been reversed, the state is reset before adding the
- * movement (so that you don't have to compensate for any previously
- * collected movement before see the result of the movement in the
- * new direction).
- *
- * @return Returns the absolute value of the amount of movement
- * collected so far.
- */
- float collect(float off, long time, String axis) {
- long normTime;
- if (off > 0) {
- normTime = (long)(off * FAST_MOVE_TIME);
- if (dir < 0) {
- if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!");
- position = 0;
- step = 0;
- acceleration = 1;
- lastMoveTime = 0;
- }
- dir = 1;
- } else if (off < 0) {
- normTime = (long)((-off) * FAST_MOVE_TIME);
- if (dir > 0) {
- if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!");
- position = 0;
- step = 0;
- acceleration = 1;
- lastMoveTime = 0;
- }
- dir = -1;
- } else {
- normTime = 0;
- }
-
- // The number of milliseconds between each movement that is
- // considered "normal" and will not result in any acceleration
- // or deceleration, scaled by the offset we have here.
- if (normTime > 0) {
- long delta = time - lastMoveTime;
- lastMoveTime = time;
- float acc = acceleration;
- if (delta < normTime) {
- // The user is scrolling rapidly, so increase acceleration.
- float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR;
- if (scale > 1) acc *= scale;
- if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off="
- + off + " normTime=" + normTime + " delta=" + delta
- + " scale=" + scale + " acc=" + acc);
- acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION;
- } else {
- // The user is scrolling slowly, so decrease acceleration.
- float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR;
- if (scale > 1) acc /= scale;
- if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off="
- + off + " normTime=" + normTime + " delta=" + delta
- + " scale=" + scale + " acc=" + acc);
- acceleration = acc > 1 ? acc : 1;
- }
- }
- position += off;
- return (absPosition = Math.abs(position));
- }
-
- /**
- * Generate the number of discrete movement events appropriate for
- * the currently collected trackball movement.
- *
- * @param precision The minimum movement required to generate the
- * first discrete movement.
- *
- * @return Returns the number of discrete movements, either positive
- * or negative, or 0 if there is not enough trackball movement yet
- * for a discrete movement.
- */
- int generate(float precision) {
- int movement = 0;
- nonAccelMovement = 0;
- do {
- final int dir = position >= 0 ? 1 : -1;
- switch (step) {
- // If we are going to execute the first step, then we want
- // to do this as soon as possible instead of waiting for
- // a full movement, in order to make things look responsive.
- case 0:
- if (absPosition < precision) {
- return movement;
- }
- movement += dir;
- nonAccelMovement += dir;
- step = 1;
- break;
- // If we have generated the first movement, then we need
- // to wait for the second complete trackball motion before
- // generating the second discrete movement.
- case 1:
- if (absPosition < 2) {
- return movement;
- }
- movement += dir;
- nonAccelMovement += dir;
- position += dir > 0 ? -2 : 2;
- absPosition = Math.abs(position);
- step = 2;
- break;
- // After the first two, we generate discrete movements
- // consistently with the trackball, applying an acceleration
- // if the trackball is moving quickly. This is a simple
- // acceleration on top of what we already compute based
- // on how quickly the wheel is being turned, to apply
- // a longer increasing acceleration to continuous movement
- // in one direction.
- default:
- if (absPosition < 1) {
- return movement;
- }
- movement += dir;
- position += dir >= 0 ? -1 : 1;
- absPosition = Math.abs(position);
- float acc = acceleration;
- acc *= 1.1f;
- acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
- break;
- }
- } while (true);
- }
- }
-
public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
public CalledFromWrongThreadException(String msg) {
super(msg);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 96ef0b4..48630a4 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1012,6 +1012,12 @@ public interface WindowManager extends ViewManager {
public static final int PRIVATE_FLAG_FORCE_SHOW_NAV_BAR = 0x00000020;
/**
+ * Never animate position changes of the window.
+ *
+ * {@hide} */
+ public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 0x00000040;
+
+ /**
* Control flags that are private to the platform.
* @hide
*/
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 7eb26fa..0ff46e9 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -136,11 +136,11 @@ public final class WindowManagerGlobal {
}
}
- public static IWindowSession getWindowSession(Looper mainLooper) {
+ public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
- InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
+ InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
imm.getClient(), imm.getInputContext());
@@ -351,7 +351,7 @@ public final class WindowManagerGlobal {
View view = root.getView();
if (view != null) {
- InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
+ InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews[index].getWindowToken());
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 855b6d4..4df4734 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -34,9 +34,11 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
-import android.os.SystemClock;
+import android.os.Trace;
import android.text.style.SuggestionSpan;
import android.util.Log;
+import android.util.Pools.Pool;
+import android.util.Pools.SimplePool;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.view.InputChannel;
@@ -45,6 +47,7 @@ import android.view.InputEventSender;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewRootImpl;
+import android.util.SparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -200,8 +203,9 @@ public final class InputMethodManager {
static final boolean DEBUG = false;
static final String TAG = "InputMethodManager";
- static final Object mInstanceSync = new Object();
- static InputMethodManager mInstance;
+ static final String PENDING_EVENT_COUNTER = "aq:imm";
+
+ static InputMethodManager sInstance;
/**
* @hide Flag for IInputMethodManager.windowGainedFocus: a view in
@@ -232,7 +236,14 @@ public final class InputMethodManager {
*/
static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500;
- private static final int MAX_PENDING_EVENT_POOL_SIZE = 4;
+ /** @hide */
+ public static final int DISPATCH_IN_PROGRESS = -1;
+
+ /** @hide */
+ public static final int DISPATCH_NOT_HANDLED = 0;
+
+ /** @hide */
+ public static final int DISPATCH_HANDLED = 1;
final IInputMethodManager mService;
final Looper mMainLooper;
@@ -323,10 +334,8 @@ public final class InputMethodManager {
InputChannel mCurChannel;
ImeInputEventSender mCurSender;
- PendingEvent mPendingEventPool;
- int mPendingEventPoolSize;
- PendingEvent mPendingEventHead;
- PendingEvent mPendingEventTail;
+ final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
+ final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
// -----------------------------------------------------------
@@ -334,8 +343,10 @@ public final class InputMethodManager {
static final int MSG_BIND = 2;
static final int MSG_UNBIND = 3;
static final int MSG_SET_ACTIVE = 4;
- static final int MSG_EVENT_TIMEOUT = 5;
-
+ static final int MSG_SEND_INPUT_EVENT = 5;
+ static final int MSG_TIMEOUT_INPUT_EVENT = 6;
+ static final int MSG_FLUSH_INPUT_EVENT = 7;
+
class H extends Handler {
H(Looper looper) {
super(looper, null, true);
@@ -453,15 +464,16 @@ public final class InputMethodManager {
}
return;
}
- case MSG_EVENT_TIMEOUT: {
- // Even though the message contains both the sequence number
- // and the PendingEvent object itself, we only pass the
- // sequence number to the timeoutEvent function because it's
- // possible for the PendingEvent object to be dequeued and
- // recycled concurrently. To avoid a possible race, we make
- // a point of always looking up the PendingEvent within the
- // queue given only the sequence number of the event.
- timeoutEvent(msg.arg1);
+ case MSG_SEND_INPUT_EVENT: {
+ sendInputEventAndReportResultOnMainLooper((PendingEvent)msg.obj);
+ return;
+ }
+ case MSG_TIMEOUT_INPUT_EVENT: {
+ finishedInputEvent(msg.arg1, false, true);
+ return;
+ }
+ case MSG_FLUSH_INPUT_EVENT: {
+ finishedInputEvent(msg.arg1, false, false);
return;
}
}
@@ -538,10 +550,6 @@ public final class InputMethodManager {
mH = new H(looper);
mIInputContext = new ControlledInputConnectionWrapper(looper,
mDummyInputConnection, this);
-
- if (mInstance == null) {
- mInstance = this;
- }
}
/**
@@ -549,25 +557,15 @@ public final class InputMethodManager {
* doesn't already exist.
* @hide
*/
- static public InputMethodManager getInstance(Context context) {
- return getInstance(context.getMainLooper());
- }
-
- /**
- * Internally, the input method manager can't be context-dependent, so
- * we have this here for the places that need it.
- * @hide
- */
- static public InputMethodManager getInstance(Looper mainLooper) {
- synchronized (mInstanceSync) {
- if (mInstance != null) {
- return mInstance;
+ public static InputMethodManager getInstance() {
+ synchronized (InputMethodManager.class) {
+ if (sInstance == null) {
+ IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
+ IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
+ sInstance = new InputMethodManager(service, Looper.getMainLooper());
}
- IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
- IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
- mInstance = new InputMethodManager(service, mainLooper);
+ return sInstance;
}
- return mInstance;
}
/**
@@ -575,8 +573,8 @@ public final class InputMethodManager {
* if it exists.
* @hide
*/
- static public InputMethodManager peekInstance() {
- return mInstance;
+ public static InputMethodManager peekInstance() {
+ return sInstance;
}
/** @hide */
@@ -1585,13 +1583,18 @@ public final class InputMethodManager {
}
/**
+ * Dispatches an input event to the IME.
+ *
+ * Returns {@link #DISPATCH_HANDLED} if the event was handled.
+ * Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled.
+ * Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the
+ * callback will be invoked later.
+ *
* @hide
*/
- public int dispatchInputEvent(Context context, int seq, InputEvent event,
- FinishedEventCallback callback) {
+ public int dispatchInputEvent(InputEvent event, Object token,
+ FinishedInputEventCallback callback, Handler handler) {
synchronized (mH) {
- if (DEBUG) Log.d(TAG, "dispatchInputEvent");
-
if (mCurMethod != null) {
if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent)event;
@@ -1599,142 +1602,138 @@ public final class InputMethodManager {
&& keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
&& keyEvent.getRepeatCount() == 0) {
showInputMethodPickerLocked();
- return ViewRootImpl.EVENT_HANDLED;
+ return DISPATCH_HANDLED;
}
}
if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
- final long startTime = SystemClock.uptimeMillis();
- if (mCurChannel != null) {
- if (mCurSender == null) {
- mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
- }
- if (mCurSender.sendInputEvent(seq, event)) {
- enqueuePendingEventLocked(startTime, seq, mCurId, callback);
- return ViewRootImpl.EVENT_PENDING_IME;
- } else {
- Log.w(TAG, "Unable to send input event to IME: "
- + mCurId + " dropping: " + event);
- }
+
+ PendingEvent p = obtainPendingEventLocked(
+ event, token, mCurId, callback, handler);
+ if (mMainLooper.isCurrentThread()) {
+ // Already running on the IMM thread so we can send the event immediately.
+ return sendInputEventOnMainLooperLocked(p);
}
+
+ // Post the event to the IMM thread.
+ Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p);
+ msg.setAsynchronous(true);
+ mH.sendMessage(msg);
+ return DISPATCH_IN_PROGRESS;
}
}
- return ViewRootImpl.EVENT_POST_IME;
+ return DISPATCH_NOT_HANDLED;
}
- void finishedEvent(int seq, boolean handled) {
- final FinishedEventCallback callback;
+ // Must be called on the main looper
+ void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
+ final boolean handled;
synchronized (mH) {
- PendingEvent p = dequeuePendingEventLocked(seq);
- if (p == null) {
- return; // spurious, event already finished or timed out
+ int result = sendInputEventOnMainLooperLocked(p);
+ if (result == DISPATCH_IN_PROGRESS) {
+ return;
}
- mH.removeMessages(MSG_EVENT_TIMEOUT, p);
- callback = p.mCallback;
- recyclePendingEventLocked(p);
+
+ handled = (result == DISPATCH_HANDLED);
+ }
+
+ invokeFinishedInputEventCallback(p, handled);
+ }
+
+ // Must be called on the main looper
+ int sendInputEventOnMainLooperLocked(PendingEvent p) {
+ if (mCurChannel != null) {
+ if (mCurSender == null) {
+ mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
+ }
+
+ final InputEvent event = p.mEvent;
+ final int seq = event.getSequenceNumber();
+ if (mCurSender.sendInputEvent(seq, event)) {
+ mPendingEvents.put(seq, p);
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
+ mPendingEvents.size());
+
+ Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p);
+ msg.setAsynchronous(true);
+ mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
+ return DISPATCH_IN_PROGRESS;
+ }
+
+ Log.w(TAG, "Unable to send input event to IME: "
+ + mCurId + " dropping: " + event);
}
- callback.finishedEvent(seq, handled);
+ return DISPATCH_NOT_HANDLED;
}
- void timeoutEvent(int seq) {
- final FinishedEventCallback callback;
+ void finishedInputEvent(int seq, boolean handled, boolean timeout) {
+ final PendingEvent p;
synchronized (mH) {
- PendingEvent p = dequeuePendingEventLocked(seq);
- if (p == null) {
+ int index = mPendingEvents.indexOfKey(seq);
+ if (index < 0) {
return; // spurious, event already finished or timed out
}
- long delay = SystemClock.uptimeMillis() - p.mStartTime;
- Log.w(TAG, "Timeout waiting for IME to handle input event after "
- + delay + "ms: " + p.mInputMethodId);
- callback = p.mCallback;
- recyclePendingEventLocked(p);
+
+ p = mPendingEvents.valueAt(index);
+ mPendingEvents.removeAt(index);
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size());
+
+ if (timeout) {
+ Log.w(TAG, "Timeout waiting for IME to handle input event after "
+ + INPUT_METHOD_NOT_RESPONDING_TIMEOUT + " ms: " + p.mInputMethodId);
+ } else {
+ mH.removeMessages(MSG_TIMEOUT_INPUT_EVENT, p);
+ }
}
- callback.finishedEvent(seq, false);
+
+ invokeFinishedInputEventCallback(p, handled);
}
- private void enqueuePendingEventLocked(
- long startTime, int seq, String inputMethodId, FinishedEventCallback callback) {
- PendingEvent p = obtainPendingEventLocked(startTime, seq, inputMethodId, callback);
- if (mPendingEventTail != null) {
- mPendingEventTail.mNext = p;
- mPendingEventTail = p;
+ // Assumes the event has already been removed from the queue.
+ void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
+ p.mHandled = handled;
+ if (p.mHandler.getLooper().isCurrentThread()) {
+ // Already running on the callback handler thread so we can send the
+ // callback immediately.
+ p.run();
} else {
- mPendingEventHead = p;
- mPendingEventTail = p;
+ // Post the event to the callback handler thread.
+ // In this case, the callback will be responsible for recycling the event.
+ Message msg = Message.obtain(p.mHandler, p);
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
}
-
- Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, seq, 0, p);
- msg.setAsynchronous(true);
- mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
}
- private PendingEvent dequeuePendingEventLocked(int seq) {
- PendingEvent p = mPendingEventHead;
- if (p == null) {
- return null;
- }
- if (p.mSeq == seq) {
- mPendingEventHead = p.mNext;
- if (mPendingEventHead == null) {
- mPendingEventTail = null;
- }
- } else {
- PendingEvent prev;
- do {
- prev = p;
- p = p.mNext;
- if (p == null) {
- return null;
- }
- } while (p.mSeq != seq);
- prev.mNext = p.mNext;
- if (mPendingEventTail == p) {
- mPendingEventTail = prev;
- }
+ private void flushPendingEventsLocked() {
+ mH.removeMessages(MSG_FLUSH_INPUT_EVENT);
+
+ final int count = mPendingEvents.size();
+ for (int i = 0; i < count; i++) {
+ int seq = mPendingEvents.keyAt(i);
+ Message msg = mH.obtainMessage(MSG_FLUSH_INPUT_EVENT, seq, 0);
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
}
- p.mNext = null;
- return p;
}
- private PendingEvent obtainPendingEventLocked(
- long startTime, int seq, String inputMethodId, FinishedEventCallback callback) {
- PendingEvent p = mPendingEventPool;
- if (p != null) {
- mPendingEventPoolSize -= 1;
- mPendingEventPool = p.mNext;
- p.mNext = null;
- } else {
+ private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
+ String inputMethodId, FinishedInputEventCallback callback, Handler handler) {
+ PendingEvent p = mPendingEventPool.acquire();
+ if (p == null) {
p = new PendingEvent();
}
-
- p.mStartTime = startTime;
- p.mSeq = seq;
+ p.mEvent = event;
+ p.mToken = token;
p.mInputMethodId = inputMethodId;
p.mCallback = callback;
+ p.mHandler = handler;
return p;
}
private void recyclePendingEventLocked(PendingEvent p) {
- p.mInputMethodId = null;
- p.mCallback = null;
-
- if (mPendingEventPoolSize < MAX_PENDING_EVENT_POOL_SIZE) {
- mPendingEventPoolSize += 1;
- p.mNext = mPendingEventPool;
- mPendingEventPool = p;
- }
- }
-
- private void flushPendingEventsLocked() {
- mH.removeMessages(MSG_EVENT_TIMEOUT);
-
- PendingEvent p = mPendingEventHead;
- while (p != null) {
- Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, p.mSeq, 0, p);
- msg.setAsynchronous(true);
- mH.sendMessage(msg);
- p = p.mNext;
- }
+ p.recycle();
+ mPendingEventPool.release(p);
}
public void showInputMethodPicker() {
@@ -1946,8 +1945,8 @@ public final class InputMethodManager {
* the IME has been finished.
* @hide
*/
- public interface FinishedEventCallback {
- public void finishedEvent(int seq, boolean handled);
+ public interface FinishedInputEventCallback {
+ public void onFinishedInputEvent(Object token, boolean handled);
}
private final class ImeInputEventSender extends InputEventSender {
@@ -1957,16 +1956,34 @@ public final class InputMethodManager {
@Override
public void onInputEventFinished(int seq, boolean handled) {
- finishedEvent(seq, handled);
+ finishedInputEvent(seq, handled, false);
}
}
- private static final class PendingEvent {
- public PendingEvent mNext;
-
- public long mStartTime;
- public int mSeq;
+ private final class PendingEvent implements Runnable {
+ public InputEvent mEvent;
+ public Object mToken;
public String mInputMethodId;
- public FinishedEventCallback mCallback;
+ public FinishedInputEventCallback mCallback;
+ public Handler mHandler;
+ public boolean mHandled;
+
+ public void recycle() {
+ mEvent = null;
+ mToken = null;
+ mInputMethodId = null;
+ mCallback = null;
+ mHandler = null;
+ mHandled = false;
+ }
+
+ @Override
+ public void run() {
+ mCallback.onFinishedInputEvent(mToken, mHandled);
+
+ synchronized (mH) {
+ recyclePendingEventLocked(this);
+ }
+ }
}
}
diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java
index 684ec07..17eb2df 100644
--- a/core/java/android/webkit/HTML5Audio.java
+++ b/core/java/android/webkit/HTML5Audio.java
@@ -19,6 +19,7 @@ package android.webkit;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
+import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -84,6 +85,7 @@ class HTML5Audio extends Handler
// See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
private Timer mTimer;
private final class TimeupdateTask extends TimerTask {
+ @Override
public void run() {
HTML5Audio.this.obtainMessage(TIMEUPDATE).sendToTarget();
}
@@ -139,11 +141,13 @@ class HTML5Audio extends Handler
// (i.e. the webviewcore thread here)
// MediaPlayer.OnBufferingUpdateListener
+ @Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
nativeOnBuffering(percent, mNativePointer);
}
// MediaPlayer.OnCompletionListener;
+ @Override
public void onCompletion(MediaPlayer mp) {
mState = COMPLETE;
mProcessingOnEnd = true;
@@ -156,6 +160,7 @@ class HTML5Audio extends Handler
}
// MediaPlayer.OnErrorListener
+ @Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mState = ERROR;
resetMediaPlayer();
@@ -164,6 +169,7 @@ class HTML5Audio extends Handler
}
// MediaPlayer.OnPreparedListener
+ @Override
public void onPrepared(MediaPlayer mp) {
mState = PREPARED;
if (mTimer != null) {
@@ -178,6 +184,7 @@ class HTML5Audio extends Handler
}
// MediaPlayer.OnSeekCompleteListener
+ @Override
public void onSeekComplete(MediaPlayer mp) {
nativeOnTimeupdate(mp.getCurrentPosition(), mNativePointer);
}
@@ -231,7 +238,7 @@ class HTML5Audio extends Handler
headers.put(HIDE_URL_LOGS, "true");
}
- mMediaPlayer.setDataSource(url, headers);
+ mMediaPlayer.setDataSource(mContext, Uri.parse(url), headers);
mState = INITIALIZED;
mMediaPlayer.prepareAsync();
} catch (IOException e) {
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index e93db09..21b0578 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -69,7 +69,9 @@ public class WebChromeClient {
/**
* Notify the host application that the current page would
- * like to show a custom View.
+ * like to show a custom View. This is used for Fullscreen
+ * video playback; see "HTML5 Video support" documentation on
+ * {@link WebView}.
* @param view is the View object to be shown.
* @param callback is the callback to be invoked if and when the view
* is dismissed.
@@ -84,7 +86,10 @@ public class WebChromeClient {
* {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}.
* @param callback is the callback to be invoked if and when the view
* is dismissed.
+ * @deprecated This method supports the obsolete plugin mechanism,
+ * and will not be invoked in future
*/
+ @Deprecated
public void onShowCustomView(View view, int requestedOrientation,
CustomViewCallback callback) {};
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index d901d0a..8ae0021 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1004,6 +1004,7 @@ public abstract class WebSettings {
* @param flag true if plugins should be enabled
* @deprecated This method has been deprecated in favor of
* {@link #setPluginState}
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
public synchronized void setPluginsEnabled(boolean flag) {
@@ -1032,6 +1033,7 @@ public abstract class WebSettings {
* @param pluginsPath a String path to the directory containing plugins
* @deprecated This method is no longer used as plugins are loaded from
* their own APK via the system's package manager.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
public synchronized void setPluginsPath(String pluginsPath) {
@@ -1224,6 +1226,7 @@ public abstract class WebSettings {
* @return true if plugins are enabled
* @see #setPluginsEnabled
* @deprecated This method has been replaced by {@link #getPluginState}
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
public synchronized boolean getPluginsEnabled() {
@@ -1249,6 +1252,7 @@ public abstract class WebSettings {
* @return an empty string
* @deprecated This method is no longer used as plugins are loaded from
* their own APK via the system's package manager.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
public synchronized String getPluginsPath() {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1f00c9c..afa4894 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -26,6 +26,7 @@ import android.graphics.Picture;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.http.SslCertificate;
+import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.os.Message;
@@ -241,6 +242,11 @@ public class WebView extends AbsoluteLayout
private static final String LOGTAG = "webview_proxy";
+ // Throwing an exception for incorrect thread usage if the
+ // build target is JB MR2 or newer. Defaults to false, and is
+ // set in the WebView constructor.
+ private static Boolean sEnforceThreadChecking = false;
+
/**
* Transportation object for returning WebView across thread boundaries.
*/
@@ -483,6 +489,8 @@ public class WebView extends AbsoluteLayout
if (context == null) {
throw new IllegalArgumentException("Invalid context argument");
}
+ sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >=
+ Build.VERSION_CODES.JELLY_BEAN_MR2;
checkThread();
ensureProviderCreated();
@@ -1915,6 +1923,10 @@ public class WebView extends AbsoluteLayout
"Future versions of WebView may not support use on other threads.");
Log.w(LOGTAG, Log.getStackTraceString(throwable));
StrictMode.onWebViewMethodCalledOnWrongThread(throwable);
+
+ if (sEnforceThreadChecking) {
+ throw new RuntimeException(throwable);
+ }
}
}
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index e08052a..99e0ffb 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -50,8 +50,10 @@ public class WebViewDatabase {
*
* @return true if there are any saved username/password pairs
* @see WebView#savePassword
- * @see #clearUsernamePassword
+ * @see #clearUsernamePassworda
+ * @deprecated Saving passwords in WebView will not be supported in future versions.
*/
+ @Deprecated
public boolean hasUsernamePassword() {
throw new MustOverrideException();
}
@@ -62,7 +64,9 @@ public class WebViewDatabase {
*
* @see WebView#savePassword
* @see #hasUsernamePassword
+ * @deprecated Saving passwords in WebView will not be supported in future versions.
*/
+ @Deprecated
public void clearUsernamePassword() {
throw new MustOverrideException();
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 18df0b1..00d87bd 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -25,8 +25,13 @@ import dalvik.system.PathClassLoader;
/**
* Top level factory, used creating all the main WebView implementation classes.
+ *
+ * @hide
*/
-class WebViewFactory {
+public final class WebViewFactory {
+ public static final String WEBVIEW_EXPERIMENTAL_PROPERTY = "persist.sys.webview.exp";
+ private static final String DEPRECATED_CHROMIUM_PROPERTY = "webview.use_chromium";
+
// Default Provider factory class name.
// TODO: When the Chromium powered WebView is ready, it should be the default factory class.
private static final String DEFAULT_WEBVIEW_FACTORY = "android.webkit.WebViewClassic$Factory";
@@ -43,16 +48,17 @@ class WebViewFactory {
private static WebViewFactoryProvider sProviderInstance;
private static final Object sProviderLock = new Object();
+ public static boolean isExperimentalWebViewAvailable() {
+ return Build.IS_DEBUGGABLE && (new java.io.File(CHROMIUM_WEBVIEW_JAR).exists());
+ }
+
static WebViewFactoryProvider getProvider() {
synchronized (sProviderLock) {
// For now the main purpose of this function (and the factory abstraction) is to keep
// us honest and minimize usage of WebViewClassic internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
- // For debug builds, we allow a system property to specify that we should use the
- // Chromium powered WebView. This enables us to switch between implementations
- // at runtime. For user (release) builds, don't allow this.
- if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("webview.use_chromium", false)) {
+ if (isExperimentalWebViewEnabled()) {
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
try {
sProviderInstance = loadChromiumProvider();
@@ -76,6 +82,20 @@ class WebViewFactory {
}
}
+ // For debug builds, we allow a system property to specify that we should use the
+ // experimtanl Chromium powered WebView. This enables us to switch between
+ // implementations at runtime. For user (release) builds, don't allow this.
+ private static boolean isExperimentalWebViewEnabled() {
+ if (!isExperimentalWebViewAvailable())
+ return false;
+ if (SystemProperties.getBoolean(DEPRECATED_CHROMIUM_PROPERTY, false)) {
+ Log.w(LOGTAG, String.format("The property %s has been deprecated. Please use %s.",
+ DEPRECATED_CHROMIUM_PROPERTY, WEBVIEW_EXPERIMENTAL_PROPERTY));
+ return true;
+ }
+ return SystemProperties.getBoolean(WEBVIEW_EXPERIMENTAL_PROPERTY, false);
+ }
+
// TODO: This allows us to have the legacy and Chromium WebView coexist for development
// and side-by-side testing. After transition, remove this when no longer required.
private static WebViewFactoryProvider loadChromiumProvider() {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3fa0940..94dadb4 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2606,7 +2606,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mGlobalLayoutListenerAddedFilter = false;
}
- if (mAdapter != null) {
+ if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
mDataSetObserver = null;
}
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index b2073b1..34cfea5 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -20,6 +20,7 @@ import com.android.internal.R;
import android.app.AlertDialog;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -77,6 +78,7 @@ public class AppSecurityPermissions {
private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator();
private final List<MyPermissionInfo> mPermsList = new ArrayList<MyPermissionInfo>();
private final CharSequence mNewPermPrefix;
+ private String mPackageName;
static class MyPermissionGroupInfo extends PermissionGroupInfo {
CharSequence mLabel;
@@ -138,6 +140,8 @@ public class AppSecurityPermissions {
MyPermissionGroupInfo mGroup;
MyPermissionInfo mPerm;
AlertDialog mDialog;
+ private boolean mShowRevokeUI = false;
+ private String mPackageName;
public PermissionItemView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -145,9 +149,12 @@ public class AppSecurityPermissions {
}
public void setPermission(MyPermissionGroupInfo grp, MyPermissionInfo perm,
- boolean first, CharSequence newPermPrefix) {
+ boolean first, CharSequence newPermPrefix, String packageName,
+ boolean showRevokeUI) {
mGroup = grp;
mPerm = perm;
+ mShowRevokeUI = showRevokeUI;
+ mPackageName = packageName;
ImageView permGrpIcon = (ImageView) findViewById(R.id.perm_icon);
TextView permNameView = (TextView) findViewById(R.id.perm_name);
@@ -206,6 +213,7 @@ public class AppSecurityPermissions {
}
builder.setCancelable(true);
builder.setIcon(mGroup.loadGroupIcon(pm));
+ addRevokeUIIfNecessary(builder);
mDialog = builder.show();
mDialog.setCanceledOnTouchOutside(true);
}
@@ -218,6 +226,30 @@ public class AppSecurityPermissions {
mDialog.dismiss();
}
}
+
+ private void addRevokeUIIfNecessary(AlertDialog.Builder builder) {
+ if (!mShowRevokeUI) {
+ return;
+ }
+
+ final boolean isRequired =
+ ((mPerm.mExistingReqFlags & PackageInfo.REQUESTED_PERMISSION_REQUIRED) != 0);
+
+ if (isRequired) {
+ return;
+ }
+
+ DialogInterface.OnClickListener ocl = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ PackageManager pm = getContext().getPackageManager();
+ pm.revokePermission(mPackageName, mPerm.name);
+ PermissionItemView.this.setVisibility(View.GONE);
+ }
+ };
+ builder.setNegativeButton(R.string.revoke, ocl);
+ builder.setPositiveButton(R.string.ok, null);
+ }
}
private AppSecurityPermissions(Context context) {
@@ -230,6 +262,7 @@ public class AppSecurityPermissions {
public AppSecurityPermissions(Context context, String packageName) {
this(context);
+ mPackageName = packageName;
Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
PackageInfo pkgInfo;
try {
@@ -252,6 +285,7 @@ public class AppSecurityPermissions {
if(info == null) {
return;
}
+ mPackageName = info.packageName;
// Convert to a PackageInfo
PackageInfo installedPkgInfo = null;
@@ -419,15 +453,23 @@ public class AppSecurityPermissions {
}
public View getPermissionsView() {
- return getPermissionsView(WHICH_ALL);
+ return getPermissionsView(WHICH_ALL, false);
+ }
+
+ public View getPermissionsViewWithRevokeButtons() {
+ return getPermissionsView(WHICH_ALL, true);
}
public View getPermissionsView(int which) {
+ return getPermissionsView(which, false);
+ }
+
+ private View getPermissionsView(int which, boolean showRevokeUI) {
LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list);
View noPermsView = permsView.findViewById(R.id.no_permissions);
- displayPermissions(mPermGroupsList, displayList, which);
+ displayPermissions(mPermGroupsList, displayList, which, showRevokeUI);
if (displayList.getChildCount() <= 0) {
noPermsView.setVisibility(View.VISIBLE);
}
@@ -440,7 +482,7 @@ public class AppSecurityPermissions {
* list of permission descriptions.
*/
private void displayPermissions(List<MyPermissionGroupInfo> groups,
- LinearLayout permListView, int which) {
+ LinearLayout permListView, int which, boolean showRevokeUI) {
permListView.removeAllViews();
int spacing = (int)(8*mContext.getResources().getDisplayMetrics().density);
@@ -451,7 +493,7 @@ public class AppSecurityPermissions {
for (int j=0; j<perms.size(); j++) {
MyPermissionInfo perm = perms.get(j);
View view = getPermissionItemView(grp, perm, j == 0,
- which != WHICH_NEW ? mNewPermPrefix : null);
+ which != WHICH_NEW ? mNewPermPrefix : null, showRevokeUI);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
@@ -470,18 +512,19 @@ public class AppSecurityPermissions {
}
private PermissionItemView getPermissionItemView(MyPermissionGroupInfo grp,
- MyPermissionInfo perm, boolean first, CharSequence newPermPrefix) {
- return getPermissionItemView(mContext, mInflater, grp, perm, first, newPermPrefix);
+ MyPermissionInfo perm, boolean first, CharSequence newPermPrefix, boolean showRevokeUI) {
+ return getPermissionItemView(mContext, mInflater, grp, perm, first, newPermPrefix,
+ mPackageName, showRevokeUI);
}
private static PermissionItemView getPermissionItemView(Context context, LayoutInflater inflater,
MyPermissionGroupInfo grp, MyPermissionInfo perm, boolean first,
- CharSequence newPermPrefix) {
- PermissionItemView permView = (PermissionItemView)inflater.inflate(
+ CharSequence newPermPrefix, String packageName, boolean showRevokeUI) {
+ PermissionItemView permView = (PermissionItemView)inflater.inflate(
(perm.flags & PermissionInfo.FLAG_COSTS_MONEY) != 0
? R.layout.app_permission_item_money : R.layout.app_permission_item,
null);
- permView.setPermission(grp, perm, first, newPermPrefix);
+ permView.setPermission(grp, perm, first, newPermPrefix, packageName, showRevokeUI);
return permView;
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index cde6ceb..33fd8ce 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -348,7 +348,7 @@ public class ImageView extends View {
* {@link #setImageBitmap(android.graphics.Bitmap)} and
* {@link android.graphics.BitmapFactory} instead.</p>
*
- * @param resId the resource identifier of the the drawable
+ * @param resId the resource identifier of the drawable
*
* @attr ref android.R.styleable#ImageView_src
*/
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 4b62c2d..c7914f3 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2433,7 +2433,7 @@ public class ListView extends AbsListView {
mFirstPosition;
} else {
final int lastPos = mFirstPosition + getChildCount() - 1;
- nextSelected = selectedPos != INVALID_POSITION && selectedPos < lastPos?
+ nextSelected = selectedPos != INVALID_POSITION && selectedPos <= lastPos ?
selectedPos - 1 :
lastPos;
}
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 529de2e..3df7258 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -220,28 +220,29 @@ public class RelativeLayout extends ViewGroup {
// with MeasureSpec value overflow and RelativeLayout was one source of them.
// Some apps came to rely on them. :(
private boolean mAllowBrokenMeasureSpecs = false;
+ // Compatibility hack. Old versions of the platform would not take
+ // margins and padding into account when generating the height measure spec
+ // for children during the horizontal measure pass.
+ private boolean mMeasureVerticalWithPaddingMargin = false;
// A default width used for RTL measure pass
- private static int DEFAULT_WIDTH = Integer.MAX_VALUE / 2;
+ private static final int DEFAULT_WIDTH = Integer.MAX_VALUE / 2;
public RelativeLayout(Context context) {
super(context);
- mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
- Build.VERSION_CODES.JELLY_BEAN_MR1;
+ queryCompatibilityModes(context);
}
public RelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initFromAttributes(context, attrs);
- mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
- Build.VERSION_CODES.JELLY_BEAN_MR1;
+ queryCompatibilityModes(context);
}
public RelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initFromAttributes(context, attrs);
- mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
- Build.VERSION_CODES.JELLY_BEAN_MR1;
+ queryCompatibilityModes(context);
}
private void initFromAttributes(Context context, AttributeSet attrs) {
@@ -251,6 +252,12 @@ public class RelativeLayout extends ViewGroup {
a.recycle();
}
+ private void queryCompatibilityModes(Context context) {
+ int version = context.getApplicationInfo().targetSdkVersion;
+ mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1;
+ mMeasureVerticalWithPaddingMargin = version >= Build.VERSION_CODES.JELLY_BEAN_MR2;
+ }
+
@Override
public boolean shouldDelayChildPressedState() {
return false;
@@ -692,6 +699,11 @@ public class RelativeLayout extends ViewGroup {
params.leftMargin, params.rightMargin,
mPaddingLeft, mPaddingRight,
myWidth);
+ int maxHeight = myHeight;
+ if (mMeasureVerticalWithPaddingMargin) {
+ maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom -
+ params.topMargin - params.bottomMargin);
+ }
int childHeightMeasureSpec;
if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
if (params.height >= 0) {
@@ -704,9 +716,9 @@ public class RelativeLayout extends ViewGroup {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
} else if (params.width == LayoutParams.MATCH_PARENT) {
- childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY);
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
} else {
- childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST);
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index b6895a5..e3de0b9 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -494,20 +494,23 @@ public class Spinner extends AbsSpinner implements OnClickListener {
// Make selected view and position it
mFirstPosition = mSelectedPosition;
- View sel = makeAndAddView(mSelectedPosition);
- int width = sel.getMeasuredWidth();
- int selectedOffset = childrenLeft;
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.CENTER_HORIZONTAL:
- selectedOffset = childrenLeft + (childrenWidth / 2) - (width / 2);
- break;
- case Gravity.RIGHT:
- selectedOffset = childrenLeft + childrenWidth - width;
- break;
+
+ if (mAdapter != null) {
+ View sel = makeAndAddView(mSelectedPosition);
+ int width = sel.getMeasuredWidth();
+ int selectedOffset = childrenLeft;
+ final int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
+ switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ selectedOffset = childrenLeft + (childrenWidth / 2) - (width / 2);
+ break;
+ case Gravity.RIGHT:
+ selectedOffset = childrenLeft + childrenWidth - width;
+ break;
+ }
+ sel.offsetLeftAndRight(selectedOffset);
}
- sel.offsetLeftAndRight(selectedOffset);
// Flush any cached views that did not get reused above
mRecycler.clear();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 52b7a81..1246051 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5921,6 +5921,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
Layout.Alignment alignment = getLayoutAlignment();
+ final boolean testDirChange = mSingleLine && mLayout != null &&
+ (alignment == Layout.Alignment.ALIGN_NORMAL ||
+ alignment == Layout.Alignment.ALIGN_OPPOSITE);
+ int oldDir = 0;
+ if (testDirChange) oldDir = mLayout.getParagraphDirection(0);
boolean shouldEllipsize = mEllipsize != null && getKeyListener() == null;
final boolean switchEllipsize = mEllipsize == TruncateAt.MARQUEE &&
mMarqueeFadeMode != MARQUEE_FADE_NORMAL;
@@ -6009,7 +6014,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- if (bringIntoView) {
+ if (bringIntoView || (testDirChange && oldDir != mLayout.getParagraphDirection(0))) {
registerForPreDraw();
}
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index db20549..acbb2b1 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -812,6 +812,26 @@ public class ActionBarImpl extends ActionBar {
return mActionView != null && mActionView.isTitleTruncated();
}
+ @Override
+ public void setHomeAsUpIndicator(Drawable indicator) {
+ mActionView.setHomeAsUpIndicator(indicator);
+ }
+
+ @Override
+ public void setHomeAsUpIndicator(int resId) {
+ mActionView.setHomeAsUpIndicator(resId);
+ }
+
+ @Override
+ public void setHomeActionContentDescription(CharSequence description) {
+ mActionView.setHomeActionContentDescription(description);
+ }
+
+ @Override
+ public void setHomeActionContentDescription(int resId) {
+ mActionView.setHomeActionContentDescription(resId);
+ }
+
/**
* @hide
*/
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index f173327..043964f 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -38,6 +38,7 @@ import android.widget.TextView;
import java.text.Collator;
import java.util.Arrays;
import java.util.Locale;
+import java.util.ArrayList;
public class LocalePicker extends ListFragment {
private static final String TAG = "LocalePicker";
@@ -48,6 +49,10 @@ public class LocalePicker extends ListFragment {
public void onLocaleSelected(Locale locale);
}
+ protected boolean isInDeveloperMode() {
+ return false;
+ }
+
LocaleSelectionListener mListener; // default to null
public static class LocaleInfo implements Comparable<LocaleInfo> {
@@ -85,13 +90,39 @@ public class LocalePicker extends ListFragment {
* {@link LocaleInfo#label}.
*/
public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) {
- return constructAdapter(context, R.layout.locale_picker_item, R.id.locale);
+ return constructAdapter(context, false /* disable pesudolocales */);
+ }
+
+ public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
+ final boolean isInDeveloperMode) {
+ return constructAdapter(context, R.layout.locale_picker_item, R.id.locale,
+ isInDeveloperMode);
}
public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
final int layoutId, final int fieldId) {
+ return constructAdapter(context, layoutId, fieldId, false /* disable pseudolocales */);
+ }
+
+ public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
+ final int layoutId, final int fieldId, final boolean isInDeveloperMode) {
final Resources resources = context.getResources();
- final String[] locales = Resources.getSystem().getAssets().getLocales();
+
+ ArrayList<String> localeList = new ArrayList<String>(Arrays.asList(
+ Resources.getSystem().getAssets().getLocales()));
+ if (isInDeveloperMode) {
+ if (!localeList.contains("zz_ZZ")) {
+ localeList.add("zz_ZZ");
+ }
+ /** - TODO: Enable when zz_ZY Pseudolocale is complete
+ * if (!localeList.contains("zz_ZY")) {
+ * localeList.add("zz_ZY");
+ * }
+ */
+ }
+ String[] locales = new String[localeList.size()];
+ locales = localeList.toArray(locales);
+
final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
Arrays.sort(locales);
@@ -118,7 +149,8 @@ public class LocalePicker extends ListFragment {
// insert ours with full name
// diff lang -> insert ours with lang-only name
if (preprocess[finalSize-1].locale.getLanguage().equals(
- language)) {
+ language) &&
+ !preprocess[finalSize-1].locale.getLanguage().equals("zz")) {
if (DEBUG) {
Log.v(TAG, "backing up and fixing "+
preprocess[finalSize-1].label+" to "+
@@ -139,7 +171,9 @@ public class LocalePicker extends ListFragment {
} else {
String displayName;
if (s.equals("zz_ZZ")) {
- displayName = "Pseudo...";
+ displayName = "[Developer] Accented English";
+ } else if (s.equals("zz_ZY")) {
+ displayName = "[Developer] Fake Bi-Directional";
} else {
displayName = toTitleCase(l.getDisplayLanguage(l));
}
@@ -206,7 +240,8 @@ public class LocalePicker extends ListFragment {
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- final ArrayAdapter<LocaleInfo> adapter = constructAdapter(getActivity());
+ final ArrayAdapter<LocaleInfo> adapter = constructAdapter(getActivity(),
+ isInDeveloperMode());
setListAdapter(adapter);
}
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
new file mode 100644
index 0000000..e26b27d
--- /dev/null
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -0,0 +1,146 @@
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.internal.os;
+
+import java.io.PrintStream;
+
+public abstract class BaseCommand {
+
+ protected String[] mArgs;
+ private int mNextArg;
+ private String mCurArgData;
+
+ // These are magic strings understood by the Eclipse plugin.
+ public static final String FATAL_ERROR_CODE = "Error type 1";
+ public static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
+ public static final String NO_CLASS_ERROR_CODE = "Error type 3";
+
+ /**
+ * Call to run the command.
+ */
+ public void run(String[] args) {
+ if (args.length < 1) {
+ onShowUsage(System.out);
+ return;
+ }
+
+ mArgs = args;
+ mNextArg = 0;
+ mCurArgData = null;
+
+ try {
+ onRun();
+ } catch (IllegalArgumentException e) {
+ onShowUsage(System.err);
+ System.err.println();
+ System.err.println("Error: " + e.getMessage());
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Convenience to show usage information to error output.
+ */
+ public void showUsage() {
+ onShowUsage(System.err);
+ }
+
+ /**
+ * Convenience to show usage information to error output along
+ * with an error message.
+ */
+ public void showError(String message) {
+ onShowUsage(System.err);
+ System.err.println();
+ System.err.println(message);
+ }
+
+ /**
+ * Implement the command.
+ */
+ public abstract void onRun() throws Exception;
+
+ /**
+ * Print help text for the command.
+ */
+ public abstract void onShowUsage(PrintStream out);
+
+ /**
+ * Return the next option on the command line -- that is an argument that
+ * starts with '-'. If the next argument is not an option, null is returned.
+ */
+ public String nextOption() {
+ if (mCurArgData != null) {
+ String prev = mArgs[mNextArg - 1];
+ throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
+ }
+ if (mNextArg >= mArgs.length) {
+ return null;
+ }
+ String arg = mArgs[mNextArg];
+ if (!arg.startsWith("-")) {
+ return null;
+ }
+ mNextArg++;
+ if (arg.equals("--")) {
+ return null;
+ }
+ if (arg.length() > 1 && arg.charAt(1) != '-') {
+ if (arg.length() > 2) {
+ mCurArgData = arg.substring(2);
+ return arg.substring(0, 2);
+ } else {
+ mCurArgData = null;
+ return arg;
+ }
+ }
+ mCurArgData = null;
+ return arg;
+ }
+
+ /**
+ * Return the next argument on the command line, whatever it is; if there are
+ * no arguments left, return null.
+ */
+ public String nextArg() {
+ if (mCurArgData != null) {
+ String arg = mCurArgData;
+ mCurArgData = null;
+ return arg;
+ } else if (mNextArg < mArgs.length) {
+ return mArgs[mNextArg++];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return the next argument on the command line, whatever it is; if there are
+ * no arguments left, throws an IllegalArgumentException to report this to the user.
+ */
+ public String nextArgRequired() {
+ String arg = nextArg();
+ if (arg == null) {
+ String prev = mArgs[mNextArg - 1];
+ throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
+ }
+ return arg;
+ }
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 780f5b3..58b15e2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -17,7 +17,7 @@
package com.android.internal.statusbar;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
+import android.service.notification.StatusBarNotification;
/** @hide */
oneway interface IStatusBar
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 04e5bc9..c98ba8d 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -19,7 +19,7 @@ package com.android.internal.statusbar;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
-import com.android.internal.statusbar.StatusBarNotification;
+import android.service.notification.StatusBarNotification;
/** @hide */
interface IStatusBarService
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 9143c61..14afe21 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -84,7 +84,7 @@ public final class InputBindResult implements Parcelable {
dest.writeStrongInterface(method);
if (channel != null) {
dest.writeInt(1);
- channel.writeToParcel(dest, 0);
+ channel.writeToParcel(dest, flags);
} else {
dest.writeInt(0);
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java
index 2685046..7ca6c1b 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItem.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java
@@ -107,7 +107,7 @@ public class ActionMenuItem implements MenuItem {
}
public CharSequence getTitleCondensed() {
- return mTitleCondensed;
+ return mTitleCondensed != null ? mTitleCondensed : mTitle;
}
public boolean hasSubMenu() {
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index b99b39a..59ff597 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -93,6 +93,8 @@ public class ActionBarView extends AbsActionBarView {
private CharSequence mSubtitle;
private Drawable mIcon;
private Drawable mLogo;
+ private CharSequence mHomeDescription;
+ private int mHomeDescriptionRes;
private HomeView mHomeLayout;
private HomeView mExpandedHomeLayout;
@@ -288,6 +290,10 @@ public class ActionBarView extends AbsActionBarView {
initTitle();
}
+ if (mHomeDescriptionRes != 0) {
+ setHomeActionContentDescription(mHomeDescriptionRes);
+ }
+
if (mTabScrollView != null && mIncludeTabs) {
ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams();
if (lp != null) {
@@ -589,14 +595,43 @@ public class ActionBarView extends AbsActionBarView {
mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
} else {
mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ mUpGoerFive.setContentDescription(buildHomeContentDescription());
+ }
+ }
+
+ /**
+ * Compose a content description for the Home/Up affordance.
+ *
+ * <p>As this encompasses the icon/logo, title and subtitle all in one, we need
+ * a description for the whole wad of stuff that can be localized properly.</p>
+ */
+ private CharSequence buildHomeContentDescription() {
+ final CharSequence homeDesc;
+ if (mHomeDescription != null) {
+ homeDesc = mHomeDescription;
+ } else {
if ((mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
- mUpGoerFive.setContentDescription(mContext.getResources().getText(
- R.string.action_bar_up_description));
+ homeDesc = mContext.getResources().getText(R.string.action_bar_up_description);
} else {
- mUpGoerFive.setContentDescription(mContext.getResources().getText(
- R.string.action_bar_home_description));
+ homeDesc = mContext.getResources().getText(R.string.action_bar_home_description);
+ }
+ }
+
+ final CharSequence title = getTitle();
+ final CharSequence subtitle = getSubtitle();
+ if (!TextUtils.isEmpty(title)) {
+ final String result;
+ if (!TextUtils.isEmpty(subtitle)) {
+ result = getResources().getString(
+ R.string.action_bar_home_subtitle_description_format,
+ title, subtitle, homeDesc);
+ } else {
+ result = getResources().getString(R.string.action_bar_home_description_format,
+ title, homeDesc);
}
+ return result;
}
+ return homeDesc;
}
public void setDisplayOptions(int options) {
@@ -1305,6 +1340,23 @@ public class ActionBarView extends AbsActionBarView {
}
}
+ public void setHomeAsUpIndicator(Drawable indicator) {
+ mHomeLayout.setUpIndicator(indicator);
+ }
+
+ public void setHomeAsUpIndicator(int resId) {
+ mHomeLayout.setUpIndicator(resId);
+ }
+
+ public void setHomeActionContentDescription(CharSequence description) {
+ mHomeDescription = description;
+ }
+
+ public void setHomeActionContentDescription(int resId) {
+ mHomeDescriptionRes = resId;
+ mHomeDescription = getResources().getText(resId);
+ }
+
static class SavedState extends BaseSavedState {
int expandedMenuItemId;
boolean isOverflowOpen;
@@ -1339,9 +1391,11 @@ public class ActionBarView extends AbsActionBarView {
}
private static class HomeView extends FrameLayout {
- private View mUpView;
+ private ImageView mUpView;
private ImageView mIconView;
private int mUpWidth;
+ private int mUpIndicatorRes;
+ private Drawable mDefaultUpIndicator;
private static final long DEFAULT_TRANSITION_DURATION = 150;
@@ -1366,6 +1420,25 @@ public class ActionBarView extends AbsActionBarView {
mIconView.setImageDrawable(icon);
}
+ public void setUpIndicator(Drawable d) {
+ mUpView.setImageDrawable(d != null ? d : mDefaultUpIndicator);
+ mUpIndicatorRes = 0;
+ }
+
+ public void setUpIndicator(int resId) {
+ mUpIndicatorRes = resId;
+ mUpView.setImageDrawable(resId != 0 ? getResources().getDrawable(resId) : null);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mUpIndicatorRes != 0) {
+ // Reload for config change
+ setUpIndicator(mUpIndicatorRes);
+ }
+ }
+
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
onPopulateAccessibilityEvent(event);
@@ -1389,8 +1462,9 @@ public class ActionBarView extends AbsActionBarView {
@Override
protected void onFinishInflate() {
- mUpView = findViewById(com.android.internal.R.id.up);
+ mUpView = (ImageView) findViewById(com.android.internal.R.id.up);
mIconView = (ImageView) findViewById(com.android.internal.R.id.home);
+ mDefaultUpIndicator = mUpView.getDrawable();
}
public int getStartOffset() {
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 1b088b3..b066d70 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -38,7 +38,6 @@ import android.view.View;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.R;
-import com.android.internal.widget.LockPatternView.Cell;
import java.util.ArrayList;
import java.util.List;
@@ -51,7 +50,6 @@ import java.util.List;
* "correct" states.
*/
public class LockPatternView extends View {
- private static final String TAG = "LockPatternView";
// Aspect to use when rendering this view
private static final int ASPECT_SQUARE = 0; // View will be the minimum of width/height
private static final int ASPECT_LOCK_WIDTH = 1; // Fixed width; height will be minimum of (w,h)
@@ -63,9 +61,6 @@ public class LockPatternView extends View {
private Paint mPaint = new Paint();
private Paint mPathPaint = new Paint();
- // TODO: make this common with PhoneWindow
- static final int STATUS_BAR_HEIGHT = 25;
-
/**
* How many milliseconds we spend animating each circle of a lock pattern
* if the animating mode is set. The entire animation should take this
@@ -124,6 +119,7 @@ public class LockPatternView extends View {
private final Path mCurrentPath = new Path();
private final Rect mInvalidate = new Rect();
+ private final Rect mTmpInvalidateRect = new Rect();
private int mBitmapWidth;
private int mBitmapHeight;
@@ -132,7 +128,6 @@ public class LockPatternView extends View {
private final Matrix mArrowMatrix = new Matrix();
private final Matrix mCircleMatrix = new Matrix();
-
/**
* Represents a cell in the 3 X 3 matrix of the unlock pattern view.
*/
@@ -680,8 +675,9 @@ public class LockPatternView extends View {
private void handleActionMove(MotionEvent event) {
// Handle all recent motion events so we don't skip any cells even when the device
// is busy...
+ final float radius = (mSquareWidth * mDiameterFactor * 0.5f);
final int historySize = event.getHistorySize();
- Rect invalidateRect = mInvalidate;
+ mTmpInvalidateRect.setEmpty();
boolean invalidateNow = false;
for (int i = 0; i < historySize + 1; i++) {
final float x = i < historySize ? event.getHistoricalX(i) : event.getX();
@@ -702,30 +698,30 @@ public class LockPatternView extends View {
if (mPatternInProgress && patternSize > 0) {
final ArrayList<Cell> pattern = mPattern;
final Cell lastCell = pattern.get(patternSize - 1);
- float startX = getCenterXForColumn(lastCell.column);
- float startY = getCenterYForRow(lastCell.row);
+ float lastCellCenterX = getCenterXForColumn(lastCell.column);
+ float lastCellCenterY = getCenterYForRow(lastCell.row);
- // Adjust for current position. Radius accounts for line width.
- final float radius = (mSquareWidth * mDiameterFactor * 0.5f);
- float left = Math.min(startX, x) - radius;
- float right = Math.max(startX, x) + radius;
- float top = Math.min(startY, y) - radius;
- float bottom = Math.max(startY, y) + radius;
+ // Adjust for drawn segment from last cell to (x,y). Radius accounts for line width.
+ float left = Math.min(lastCellCenterX, x) - radius;
+ float right = Math.max(lastCellCenterX, x) + radius;
+ float top = Math.min(lastCellCenterY, y) - radius;
+ float bottom = Math.max(lastCellCenterY, y) + radius;
// Invalidate between the pattern's new cell and the pattern's previous cell
- if (hitCell != null && patternSize >= 2) {
+ if (hitCell != null) {
final float width = mSquareWidth * 0.5f;
final float height = mSquareHeight * 0.5f;
- final float x2 = getCenterXForColumn(hitCell.column);
- final float y2 = getCenterYForRow(hitCell.row);
- left = Math.min(x2, left - width);
- right = Math.max(x2, right + width);
- top = Math.min(y2, top - height);
- bottom = Math.max(y2, bottom + height);
+ final float hitCellCenterX = getCenterXForColumn(hitCell.column);
+ final float hitCellCenterY = getCenterYForRow(hitCell.row);
+
+ left = Math.min(hitCellCenterX - width, left);
+ right = Math.max(hitCellCenterX + width, right);
+ top = Math.min(hitCellCenterY - height, top);
+ bottom = Math.max(hitCellCenterY + height, bottom);
}
// Invalidate between the pattern's last cell and the previous location
- invalidateRect.union(Math.round(left), Math.round(top),
+ mTmpInvalidateRect.union(Math.round(left), Math.round(top),
Math.round(right), Math.round(bottom));
}
}
@@ -734,8 +730,9 @@ public class LockPatternView extends View {
// To save updates, we only invalidate if the user moved beyond a certain amount.
if (invalidateNow) {
- invalidate(invalidateRect);
- invalidateRect.setEmpty();
+ mInvalidate.union(mTmpInvalidateRect);
+ invalidate(mInvalidate);
+ mInvalidate.set(mTmpInvalidateRect);
}
}
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index b620568..04931e7 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -179,6 +179,9 @@ public class ScrollingTabContainerView extends HorizontalScrollView
animateToTab(position);
}
}
+ if (mTabSpinner != null && position >= 0) {
+ mTabSpinner.setSelection(position);
+ }
}
public void setContentHeight(int contentHeight) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 66cea9d7..3e5586e 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -179,6 +179,7 @@ LOCAL_SHARED_LIBRARIES := \
libandroidfw \
libexpat \
libnativehelper \
+ liblog \
libcutils \
libutils \
libbinder \
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 9a9f6c8..11c7053 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -77,7 +77,14 @@ public:
}
static SkCanvas* initRaster(JNIEnv* env, jobject, SkBitmap* bitmap) {
- return bitmap ? new SkCanvas(*bitmap) : new SkCanvas;
+ if (bitmap) {
+ return new SkCanvas(*bitmap);
+ } else {
+ // Create an empty bitmap device to prevent callers from crashing
+ // if they attempt to draw into this canvas.
+ SkBitmap emptyBitmap;
+ return new SkCanvas(emptyBitmap);
+ }
}
static void copyCanvasState(JNIEnv* env, jobject clazz,
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 3083cb1..6374494 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -31,7 +31,7 @@
static struct {
jclass clazz;
jmethodID dispatchSensorEvent;
-} gSensorEventQueueClassInfo;
+} gBaseEventQueueClassInfo;
namespace android {
@@ -145,7 +145,7 @@ private:
env->SetFloatArrayRegion(mScratch, 0, 16, buffer[i].data);
env->CallVoidMethod(mReceiverObject,
- gSensorEventQueueClassInfo.dispatchSensorEvent,
+ gBaseEventQueueClassInfo.dispatchSensorEvent,
buffer[i].sensor,
mScratch,
buffer[i].vector.status,
@@ -209,9 +209,9 @@ static JNINativeMethod gSystemSensorManagerMethods[] = {
(void*)nativeGetNextSensor },
};
-static JNINativeMethod gSensorEventQueueMethods[] = {
- {"nativeInitSensorEventQueue",
- "(Landroid/hardware/SystemSensorManager$SensorEventQueue;Landroid/os/MessageQueue;[F)I",
+static JNINativeMethod gBaseEventQueueMethods[] = {
+ {"nativeInitBaseEventQueue",
+ "(Landroid/hardware/SystemSensorManager$BaseEventQueue;Landroid/os/MessageQueue;[F)I",
(void*)nativeInitSensorEventQueue },
{"nativeEnableSensor",
@@ -245,13 +245,13 @@ int register_android_hardware_SensorManager(JNIEnv *env)
jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager",
gSystemSensorManagerMethods, NELEM(gSystemSensorManagerMethods));
- jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager$SensorEventQueue",
- gSensorEventQueueMethods, NELEM(gSensorEventQueueMethods));
+ jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager$BaseEventQueue",
+ gBaseEventQueueMethods, NELEM(gBaseEventQueueMethods));
- FIND_CLASS(gSensorEventQueueClassInfo.clazz, "android/hardware/SystemSensorManager$SensorEventQueue");
+ FIND_CLASS(gBaseEventQueueClassInfo.clazz, "android/hardware/SystemSensorManager$BaseEventQueue");
- GET_METHOD_ID(gSensorEventQueueClassInfo.dispatchSensorEvent,
- gSensorEventQueueClassInfo.clazz,
+ GET_METHOD_ID(gBaseEventQueueClassInfo.dispatchSensorEvent,
+ gBaseEventQueueClassInfo.clazz,
"dispatchSensorEvent", "(I[FIJ)V");
return 0;
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index f028c86..1315291 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -18,12 +18,27 @@
#include <JNIHelp.h>
#include <ScopedUtfChars.h>
+#include <ScopedStringChars.h>
+
+#include <utils/String8.h>
#include <cutils/trace.h>
#include <cutils/log.h>
namespace android {
+static void sanitizeString(String8& utf8Chars) {
+ size_t size = utf8Chars.size();
+ char* str = utf8Chars.lockBuffer(size);
+ for (size_t i = 0; i < size; i++) {
+ char c = str[i];
+ if (c == '\0' || c == '\n' || c == '|') {
+ str[i] = ' ';
+ }
+ }
+ utf8Chars.unlockBuffer();
+}
+
static jlong android_os_Trace_nativeGetEnabledTags(JNIEnv* env, jclass clazz) {
return atrace_get_enabled_tags();
}
@@ -36,8 +51,11 @@ static void android_os_Trace_nativeTraceCounter(JNIEnv* env, jclass clazz,
static void android_os_Trace_nativeTraceBegin(JNIEnv* env, jclass clazz,
jlong tag, jstring nameStr) {
- ScopedUtfChars name(env, nameStr);
- atrace_begin(tag, name.c_str());
+ const size_t MAX_SECTION_NAME_LEN = 127;
+ ScopedStringChars jchars(env, nameStr);
+ String8 utf8Chars(reinterpret_cast<const char16_t*>(jchars.get()), jchars.size());
+ sanitizeString(utf8Chars);
+ atrace_begin(tag, utf8Chars.string());
}
static void android_os_Trace_nativeTraceEnd(JNIEnv* env, jclass clazz,
@@ -45,6 +63,29 @@ static void android_os_Trace_nativeTraceEnd(JNIEnv* env, jclass clazz,
atrace_end(tag);
}
+static void android_os_Trace_nativeAsyncTraceBegin(JNIEnv* env, jclass clazz,
+ jlong tag, jstring nameStr, jint cookie) {
+ const size_t MAX_SECTION_NAME_LEN = 127;
+ ScopedStringChars jchars(env, nameStr);
+ String8 utf8Chars(reinterpret_cast<const char16_t*>(jchars.get()), jchars.size());
+ sanitizeString(utf8Chars);
+ atrace_async_begin(tag, utf8Chars.string(), cookie);
+}
+
+static void android_os_Trace_nativeAsyncTraceEnd(JNIEnv* env, jclass clazz,
+ jlong tag, jstring nameStr, jint cookie) {
+ const size_t MAX_SECTION_NAME_LEN = 127;
+ ScopedStringChars jchars(env, nameStr);
+ String8 utf8Chars(reinterpret_cast<const char16_t*>(jchars.get()), jchars.size());
+ sanitizeString(utf8Chars);
+ atrace_async_end(tag, utf8Chars.string(), cookie);
+}
+
+static void android_os_Trace_nativeSetAppTracingAllowed(JNIEnv* env,
+ jclass clazz, jboolean allowed) {
+ atrace_set_debuggable(allowed);
+}
+
static JNINativeMethod gTraceMethods[] = {
/* name, signature, funcPtr */
{ "nativeGetEnabledTags",
@@ -59,6 +100,15 @@ static JNINativeMethod gTraceMethods[] = {
{ "nativeTraceEnd",
"(J)V",
(void*)android_os_Trace_nativeTraceEnd },
+ { "nativeAsyncTraceBegin",
+ "(JLjava/lang/String;I)V",
+ (void*)android_os_Trace_nativeAsyncTraceBegin },
+ { "nativeAsyncTraceEnd",
+ "(JLjava/lang/String;I)V",
+ (void*)android_os_Trace_nativeAsyncTraceEnd },
+ { "nativeSetAppTracingAllowed",
+ "(Z)V",
+ (void*)android_os_Trace_nativeSetAppTracingAllowed },
};
int register_android_os_Trace(JNIEnv* env) {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 785bf13..dc4d945 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -1642,7 +1642,7 @@ static JNINativeMethod gAssetManagerMethods[] = {
(void*) android_content_AssetManager_getAssetLength },
{ "getAssetRemainingLength", "(I)J",
(void*) android_content_AssetManager_getAssetRemainingLength },
- { "addAssetPath", "(Ljava/lang/String;)I",
+ { "addAssetPathNative", "(Ljava/lang/String;)I",
(void*) android_content_AssetManager_addAssetPath },
{ "isUpToDate", "()Z",
(void*) android_content_AssetManager_isUpToDate },
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 9c44a59..9fa9fe4 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -246,6 +246,15 @@ static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj)
return name;
}
+static void android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jobject otherObj) {
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, obj);
+ if (nativeInputChannel) {
+ android_view_InputChannel_setNativeInputChannel(env, otherObj,
+ new NativeInputChannel(nativeInputChannel->getInputChannel()->dup()));
+ }
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gInputChannelMethods[] = {
@@ -262,6 +271,8 @@ static JNINativeMethod gInputChannelMethods[] = {
(void*)android_view_InputChannel_nativeWriteToParcel },
{ "nativeGetName", "()Ljava/lang/String;",
(void*)android_view_InputChannel_nativeGetName },
+ { "nativeDup", "(Landroid/view/InputChannel;)V",
+ (void*)android_view_InputChannel_nativeDup },
};
#define FIND_CLASS(var, className) \
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 576f831..e3a54a8 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -62,8 +62,8 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
for (size_t i = 0; i < ranges.size(); i++) {
const InputDeviceInfo::MotionRange& range = ranges.itemAt(i);
- env->CallVoidMethod(inputDeviceObj.get(), gInputDeviceClassInfo.addMotionRange,
- range.axis, range.source, range.min, range.max, range.flat, range.fuzz);
+ env->CallVoidMethod(inputDeviceObj.get(), gInputDeviceClassInfo.addMotionRange, range.axis,
+ range.source, range.min, range.max, range.flat, range.fuzz, range.resolution);
if (env->ExceptionCheck()) {
return NULL;
}
@@ -90,7 +90,7 @@ int register_android_view_InputDevice(JNIEnv* env)
"<init>", "(IILjava/lang/String;Ljava/lang/String;ZIILandroid/view/KeyCharacterMap;Z)V");
GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz,
- "addMotionRange", "(IIFFFF)V");
+ "addMotionRange", "(IIFFFFF)V");
return 0;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 666d1c6..6b4fe79 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -616,6 +616,14 @@
android:label="@string/permlab_installLocationProvider"
android:description="@string/permdesc_installLocationProvider" />
+ <!-- Allows an application to use location features in hardware,
+ such as the geofencing api
+ Protected by signature|system protection level -->
+ <permission android:name="android.permission.LOCATION_HARDWARE"
+ android:permissionGroup="android.permission-group.LOCATION"
+ android:protectionLevel="signature|system" />
+ <uses-permission android:name="android.permission.LOCATION_HARDWARE"/>
+
<!-- ======================================= -->
<!-- Permissions for accessing networks -->
<!-- ======================================= -->
@@ -2193,6 +2201,14 @@
android:description="@string/permdesc_accessNotifications"
android:protectionLevel="signature|system" />
+ <!-- Must be required by an {@link
+ android.service.notification.NotificationListenerService},
+ to ensure that only the system can bind to it. -->
+ <permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
+ android:label="@string/permlab_bindNotificationListenerService"
+ android:description="@string/permdesc_bindNotificationListenerService"
+ android:protectionLevel="signature" />
+
<!-- The system process is explicitly the only one allowed to launch the
confirmation UI for full backup/restore -->
<uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
@@ -2256,6 +2272,14 @@
android:process=":ui">
</activity>
+ <activity android:name="android.accounts.CantAddAccountActivity"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@android:style/Theme.Holo.Dialog"
+ android:label="@string/error_message_title"
+ android:process=":ui">
+ </activity>
+
<activity android:name="android.accounts.GrantCredentialsPermissionActivity"
android:excludeFromRecents="true"
android:exported="true"
@@ -2301,6 +2325,12 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.updates.IntentFirewallInstallReceiver" >
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_INTENT_FIREWALL" />
+ </intent-filter>
+ </receiver>
+
<receiver android:name="com.android.server.updates.SmsShortCodesInstallReceiver" >
<intent-filter>
<action android:name="android.intent.action.UPDATE_SMS_SHORT_CODES" />
@@ -2336,6 +2366,9 @@
android:permission="android.permission.MASTER_CLEAR"
android:exported="true" />
+ <service android:name="android.hardware.location.GeofenceHardwareService"
+ android:permission="android.permission.LOCATION_HARDWARE"
+ android:exported="false" />
</application>
</manifest>
diff --git a/core/res/res/anim/rotation_animation_xfade_exit.xml b/core/res/res/anim/rotation_animation_xfade_exit.xml
index 7300724..1dedde4 100644
--- a/core/res/res/anim/rotation_animation_xfade_exit.xml
+++ b/core/res/res/anim/rotation_animation_xfade_exit.xml
@@ -18,6 +18,6 @@
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:duration="500"
+ android:duration="150"
android:interpolator="@interpolator/decelerate_quad" />
</set>
diff --git a/core/res/res/drawable-hdpi/menu_popup_panel_holo_dark.9.png b/core/res/res/drawable-hdpi/menu_popup_panel_holo_dark.9.png
new file mode 100644
index 0000000..e5ff886
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menu_popup_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_popup_panel_holo_light.9.png b/core/res/res/drawable-hdpi/menu_popup_panel_holo_light.9.png
new file mode 100644
index 0000000..06d1653
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menu_popup_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_dark.9.png
new file mode 100644
index 0000000..7c5826f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_light.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_light.9.png
new file mode 100644
index 0000000..974a292
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_dark.9.png
new file mode 100644
index 0000000..b3196c9
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_light.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_light.9.png
new file mode 100644
index 0000000..1f833d3
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_dark.9.png
new file mode 100644
index 0000000..e969abc
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_light.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_light.9.png
new file mode 100644
index 0000000..3adbc84
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_dark.9.png
new file mode 100644
index 0000000..a321836
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_light.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_light.9.png
new file mode 100644
index 0000000..4c5d692
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_dark.9.png
new file mode 100644
index 0000000..6199dc5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_light.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_light.9.png
new file mode 100644
index 0000000..1b0905a
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_dark.9.png
new file mode 100644
index 0000000..c6d7868
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_light.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_light.9.png
new file mode 100644
index 0000000..179644c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_dark.9.png
new file mode 100644
index 0000000..039a056
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_light.9.png
new file mode 100644
index 0000000..c8d68c5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_dark.9.png
new file mode 100644
index 0000000..1fef1ad
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_light.9.png
new file mode 100644
index 0000000..6b22d44
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_dark.9.png
new file mode 100644
index 0000000..c219527
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_light.9.png
new file mode 100644
index 0000000..2a1d508
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_popup_panel_holo_dark.9.png b/core/res/res/drawable-mdpi/menu_popup_panel_holo_dark.9.png
new file mode 100644
index 0000000..2020a42
--- /dev/null
+++ b/core/res/res/drawable-mdpi/menu_popup_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_popup_panel_holo_light.9.png b/core/res/res/drawable-mdpi/menu_popup_panel_holo_light.9.png
new file mode 100644
index 0000000..7cae402
--- /dev/null
+++ b/core/res/res/drawable-mdpi/menu_popup_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/menu_popup_panel_holo_dark.9.png b/core/res/res/drawable-xhdpi/menu_popup_panel_holo_dark.9.png
new file mode 100644
index 0000000..e85b0c2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/menu_popup_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/menu_popup_panel_holo_light.9.png b/core/res/res/drawable-xhdpi/menu_popup_panel_holo_light.9.png
new file mode 100644
index 0000000..eea215d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/menu_popup_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable/menu_panel_holo_dark.xml b/core/res/res/drawable/menu_panel_holo_dark.xml
new file mode 100644
index 0000000..658a3ac
--- /dev/null
+++ b/core/res/res/drawable/menu_panel_holo_dark.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_above_anchor="true" android:drawable="@android:drawable/menu_popup_panel_holo_dark" />
+ <item android:drawable="@android:drawable/menu_dropdown_panel_holo_dark" />
+</selector>
diff --git a/core/res/res/drawable/menu_panel_holo_light.xml b/core/res/res/drawable/menu_panel_holo_light.xml
new file mode 100644
index 0000000..a37e934
--- /dev/null
+++ b/core/res/res/drawable/menu_panel_holo_light.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_above_anchor="true" android:drawable="@android:drawable/menu_popup_panel_holo_light" />
+ <item android:drawable="@android:drawable/menu_dropdown_panel_holo_light" />
+</selector>
diff --git a/core/res/res/layout/select_dialog.xml b/core/res/res/layout/select_dialog.xml
index 80d22f6..eb4d8d9 100644
--- a/core/res/res/layout/select_dialog.xml
+++ b/core/res/res/layout/select_dialog.xml
@@ -32,4 +32,5 @@
android:cacheColorHint="@null"
android:divider="?android:attr/listDividerAlertDialog"
android:scrollbars="vertical"
- android:overScrollMode="ifContentScrolls" />
+ android:overScrollMode="ifContentScrolls"
+ android:textAlignment="viewStart" />
diff --git a/core/res/res/layout/select_dialog_holo.xml b/core/res/res/layout/select_dialog_holo.xml
index 06a5d96..8a92283 100644
--- a/core/res/res/layout/select_dialog_holo.xml
+++ b/core/res/res/layout/select_dialog_holo.xml
@@ -30,4 +30,5 @@
android:cacheColorHint="@null"
android:divider="?android:attr/listDividerAlertDialog"
android:scrollbars="vertical"
- android:overScrollMode="ifContentScrolls" />
+ android:overScrollMode="ifContentScrolls"
+ android:textAlignment="viewStart" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index f72fddb..ea194fc 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Laat die program toe om te verander hoe netwerkgebruik teenoor programme gemeet word. Nie vir gebruik deur normale programme nie."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"kry toegang tot kennisgewings"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Laat die program toe om kennisgewings op te haal, te bestudeer en te verwyder, insluitende die kennisgewings wat deur ander programme geplaas is."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bind aan \'n kennisgewingluisteraardiens"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Laat die houer toe om aan die top-koppelvlak van \'n kennisgewingluisteraardiens te bind. Behoort nooit vir gewone programme nodig te wees nie."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Stel wagwoordreëls"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Beheer lengte en watter karakters wat in die skermontsluit-wagwoorde gebruik word."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitor pogings om skerm te ontsluit"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksaksies"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Bergingspasie word min"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sommige stelselfunksies werk moontlik nie"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop tans"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop tans"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Raak vir meer inligting of om die program te stop."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Kanselleer"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Toeganklikheid"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Muurpapier"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Verander muurpapier"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Kennisgewingluisteraar"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN geaktiveer"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is geaktiveer deur <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Raak om die netwerk te bestuur."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Navigeer tuis"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Navigeer op"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Meer opsies"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s - %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s-%2$s%3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Interne geheue"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kaart"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB-berging"</string>
@@ -1476,10 +1481,8 @@
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Toeganklikheid gekanselleer."</string>
<string name="user_switched" msgid="3768006783166984410">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g> ."</string>
<string name="owner_name" msgid="2716755460376028154">"Eienaar"</string>
- <!-- no translation found for error_message_title (4510373083082500195) -->
- <skip />
- <!-- no translation found for app_no_restricted_accounts (5322164210667258876) -->
- <skip />
- <!-- no translation found for app_not_found (3429141853498927379) -->
- <skip />
+ <string name="error_message_title" msgid="4510373083082500195">"Fout"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Hierdie program werk nie met rekeninge vir beperkte gebruikers nie"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"Geen program gevind om hierdie handeling te hanteer nie"</string>
+ <string name="revoke" msgid="5404479185228271586">"Herroep"</string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 73c323c..84eba57 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"ከመተግበሪያዎች በተለየ መልኩ እንዴት የአውታረ መረብ አጠቃቀም እንደተመዘገበ ለመቀየር ለመተግበሪያው ይፈቅዳሉ።ለመደበኛ መተግበሪያዎች አገልግሎት አይውልም።"</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"ማሳወቂያዎችን ይድረሱ"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"መተግበሪያው ማሳወቂያዎችን እንዲያስመጣ፣ እንዲመረምር እና እንዲያጸዳ ያስችለዋል፣ በሌሎች መተግበሪያዎች የተለጠፉትንም ጨምሮ።"</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"ከአንድ የማሳወቂያ አዳማጭ አገልግሎት ጋር ይሰሩ"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"ያዢው የማሳወቂያ አዳማጭ አገልግሎቱን ከከፍተኛ-ደረጃ በይነገጹ ጋር እንዲያስር ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ አያስፈልግም።"</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"የይለፍ ቃል ድንቦች አዘጋጅ"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"በማያ-መክፈት የተፈቀዱ የይለፍ ቃል ርዝመት እና ቁምፊዎች ተቆጣጠር።"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"የማሳያ-ክፈት ሙከራዎችን አሳይ"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"የፅሁፍ እርምጃዎች"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"የማከማቻ ቦታ እያለቀ ነው"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> በማሄድ ላይ"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> በአሁኑ ጊዜ እያሄደ ነው"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> እያሄደ ነው"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"ተጨማሪ መረጃ ለማግኘት ወይም መተግበሪያውን ለማቆም ይንኩ።"</string>
<string name="ok" msgid="5970060430562524910">"እሺ"</string>
<string name="cancel" msgid="6442560571259935130">"ይቅር"</string>
<string name="yes" msgid="5362982303337969312">"እሺ"</string>
@@ -1098,7 +1100,7 @@
<string name="new_app_description" msgid="1932143598371537340">"የድሮውን ትግበራ ሳታስቀምጥ አቁም።"</string>
<string name="sendText" msgid="5209874571959469142">"ለፅሁፍ ድርጊት ምረጥ"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"የስልክ ጥሪ ድምፅ"</string>
- <string name="volume_music" msgid="5421651157138628171">"ማህደረመረጃ ክፍልፍል"</string>
+ <string name="volume_music" msgid="5421651157138628171">" ማህደረ መረጃ ክፍልፍል"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"በብሉቱዝ በኩል ማጫወት"</string>
<string name="volume_music_hint_silent_ringtone_selected" msgid="8310739960973156272">"የፀጥታ የስልክ የደውል ድምፅ ተዘጋጅቷል"</string>
<string name="volume_call" msgid="3941680041282788711">"የጥሪ ላይ ድም ፅ መጨመሪያ/መቀነሻ"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"ተደራሽነት"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ልጣፍ"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ልጣፍ ለውጥ"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"ማሳወቂያ አዳማጭ"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ነቅቷል።"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN በ<xliff:g id="APP">%s</xliff:g>ገብሯል"</string>
<string name="vpn_text" msgid="3011306607126450322">"አውታረመረብ ለማደራጀት ንካ።"</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"መነሻ ዳስስ"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"አስስ"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ተጨማሪ አማራጮች"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s፣ %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s፣ %2$s፣ %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"ውስጣዊ ማከማቻ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD ካርድ"</string>
<string name="storage_usb" msgid="3017954059538517278">"የUSB ማከማቻ"</string>
@@ -1386,7 +1391,7 @@
<string name="data_usage_mobile_limit_snoozed_title" msgid="279240572165412168">"የተንቀሳቃሽ ውሂብ ወሰን አልፏል"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Wi-Fi ውሂብ ገደብ ታልፏል"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> ከተወሰነለት በላይ።"</string>
- <string name="data_usage_restricted_title" msgid="5965157361036321914">"በስተጀርባ ውሂብ የተገደበ ነው"</string>
+ <string name="data_usage_restricted_title" msgid="5965157361036321914">"ዳራ ውሂብ የተገደበ ነው"</string>
<string name="data_usage_restricted_body" msgid="6741521330997452990">"ገደብ ለማስወገድ ንካ።"</string>
<string name="ssl_certificate" msgid="6510040486049237639">"የደህንነት ዕውቅና ማረጋገጫ"</string>
<string name="ssl_certificate_is_valid" msgid="6825263250774569373">"ይህ የዐዕውቅና ማረጋገጫ ትክክል ነው።"</string>
@@ -1476,10 +1481,8 @@
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"ተደራሽነት ተሰርዟል።"</string>
<string name="user_switched" msgid="3768006783166984410">"የአሁኑ ተጠቃሚ <xliff:g id="NAME">%1$s</xliff:g>።"</string>
<string name="owner_name" msgid="2716755460376028154">"ባለቤት"</string>
- <!-- no translation found for error_message_title (4510373083082500195) -->
- <skip />
- <!-- no translation found for app_no_restricted_accounts (5322164210667258876) -->
- <skip />
- <!-- no translation found for app_not_found (3429141853498927379) -->
- <skip />
+ <string name="error_message_title" msgid="4510373083082500195">"ስህተት"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"ይህ መተግበሪያ የተገደቡ ተጠቃሚዎች መለያዎችን አይደግፍም"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"ይህን እርምጃ የሚያከናውን ምንም መተግበሪያ አልተገኘም"</string>
+ <string name="revoke" msgid="5404479185228271586">"ሻር"</string>
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 942968b..d134ca4 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"للسماح للتطبيق بتعديل كيفية حساب استخدام الشبكة في التطبيقات. ليس للاستخدام بواسطة التطبيقات العادية."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"إشعارات الدخول"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"يتيح للتطبيق استرجاع الإشعارات وفحصها ومسحها، بما في ذلك تلك التي نشرتها تطبيقات أخرى."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"الربط بخدمة تلقّي الإشعارات الصوتية"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"يتيح للمالك الربط بواجهة المستوى العلوي لخدمة تلقّي الإشعارات الصوتية. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"تعيين قواعد كلمة المرور"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"يمكنك التحكم في الطول والأحرف المسموح بها في كلمات مرور إلغاء تأمين الشاشة."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"مراقبة محاولات إلغاء قفل الشاشة"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"إجراءات النص"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"مساحة التخزين منخفضة"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"قد لا تعمل بعض وظائف النظام"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> قيد التشغيل"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> قيد التشغيل حاليًا"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> قيد التشغيل"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"المس للحصول على مزيد من المعلومات أو لإيقاف التطبيق."</string>
<string name="ok" msgid="5970060430562524910">"موافق"</string>
<string name="cancel" msgid="6442560571259935130">"إلغاء"</string>
<string name="yes" msgid="5362982303337969312">"موافق"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"إمكانية الدخول"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"الخلفية"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"تغيير الخلفية"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"برنامج تلقّي الإشعارات الصوتية"</string>
<string name="vpn_title" msgid="19615213552042827">"تم تنشيط الشبكة الظاهرية الخاصة (VPN)"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"تم تنشيط VPN بواسطة <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"المس لإدارة الشبكة."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"التنقل إلى الشاشة الرئيسية"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"التنقل إلى أعلى"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"المزيد من الخيارات"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s، %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s، %2$s، %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"وحدة تخزين داخلية"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"بطاقة SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"وحدة تخزين USB"</string>
@@ -1476,10 +1481,8 @@
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"تم إلغاء تسهيل الدخول."</string>
<string name="user_switched" msgid="3768006783166984410">"المستخدم الحالي <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"المالك"</string>
- <!-- no translation found for error_message_title (4510373083082500195) -->
- <skip />
- <!-- no translation found for app_no_restricted_accounts (5322164210667258876) -->
- <skip />
- <!-- no translation found for app_not_found (3429141853498927379) -->
- <skip />
+ <string name="error_message_title" msgid="4510373083082500195">"خطأ"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"لا يوفر هذا التطبيق حسابات للمستخدمين المقيّدين"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"لم يتم العثور على تطبيق يمكنه التعامل مع هذا الإجراء."</string>
+ <string name="revoke" msgid="5404479185228271586">"إلغاء"</string>
</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index e6c9ee7..9f3d3e6 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Дазваляе прыкладанням змяняць метад уліку выкарыстання сеткі прыкладаннямі. Не для выкарыстання звычайнымі прыкладаннямі."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"доступ да паведамленняў"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Дазваляе прыкладанню атрымлiваць, правяраць i выдаляць апавяшчэннi, у тым лiку апублiкаваныя iншымi прыкладаннямi."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"прывязка да службы апавяшчэння слухача"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Дазваляе ўладальніку прывязвацца да верхняга ўзроўню інтэрфейсу службы апавяшчэння слухачоў. Ніколі не патрэбнае для звычайных прыкладанняў."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Устанавіць правілы паролю"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Кіраванне даўжынёй і колькасцю знакаў у паролі разблакоўкі экрана."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Сачыць за спробамі разблакоўкі экрана"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Дзеянні з тэкстам"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Месца для захавання на зыходзе"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некаторыя сістэмныя функцыі могуць не працаваць"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g> працуе"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g> зараз працуе"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"Прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g> працуе"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Націсніце, каб атрымаць дадатковую інфармацыю або спыніць праграму."</string>
<string name="ok" msgid="5970060430562524910">"ОК"</string>
<string name="cancel" msgid="6442560571259935130">"Адмяніць"</string>
<string name="yes" msgid="5362982303337969312">"ОК"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Спецыяльныя магчымасці"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Шпалеры"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Змена шпалер"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Слухач апавяшчэння"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN актываваны"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN актывуецца прыкладаннем <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Дакраніцеся, каб кіраваць сеткай."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Перайсці да пачатковай старонкі"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Перайсці ўверх"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Больш налад"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Унутраная памяць"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-карта"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB-назапашвальнік"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Памылка"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Гэтае прыкладанне не падтрымлівае уліковыя запісы для карыстальнікаў з абмежаванымі правамі"</string>
<string name="app_not_found" msgid="3429141853498927379">"Прыкладанне для гэтага дзеяння не знойдзенае"</string>
+ <string name="revoke" msgid="5404479185228271586">"Ануляваць"</string>
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 79600f7..0983b5e 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Разрешава на приложението да променя това как употребата на мрежа се отчита спрямо приложенията. Не е предназначено за нормални приложения."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"достъп до известията"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Разрешава на приложението да извлича, преглежда и изчиства известия, включително публикуваните от други приложения."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"обвързване с услуга за слушател на известия"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Разрешава на притежателя да се обвърже с интерфейса от първо ниво на услуга за слушател на известия. Нормалните приложения не би трябвало никога да се нуждаят от това."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Задаване на правила за паролата"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролирайте дължината и разрешените знаци за паролите за отключване на екрана."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Наблюдаване на опитите за отключване на екрана"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Действия с текста"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Мястото в хранилището е на изчерпване"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Възможно е някои функции на системата да не работят"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> се изпълнява"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Понастоящем <xliff:g id="APP_NAME">%1$s</xliff:g> се изпълнява"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> се изпълнява"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Докоснете за още информация или за да спрете приложението."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Отказ"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Достъпност"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Тапет"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Промяна на тапета"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Слушател на известия"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN е активирана"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN е активирана от <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Докоснете за управление на мрежата."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Придвижване към „Начало“"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Придвижване нагоре"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Още опции"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"„%1$s“ – %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"„%1$s“, „%2$s“ – %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Вътрешно хранилище"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD карта"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB хранилище"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Грешка"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Това приложение не поддържа профили за потребители с ограничения"</string>
<string name="app_not_found" msgid="3429141853498927379">"Няма намерено приложение за извършване на това действие"</string>
+ <string name="revoke" msgid="5404479185228271586">"Отмяна"</string>
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 975dc40..ccea866 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permet que l\'aplicació modifiqui la manera com es calcula l\'ús de la xarxa per part de les aplicacions. No indicat per a les aplicacions normals."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"accedeix a les notificacions"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permet que l\'aplicació recuperi, examini i esborri les notificacions, incloses les que han publicat altres aplicacions."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincula a un servei de processament de notificacions"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permet que el titular vinculi la interfície de nivell superior d\'un servei de processament de notificacions. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Defineix 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">"Control d\'intents de desbloqueig de pantalla"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Accions de text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"S\'està acabant l\'espai d\'emmagatzematge"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"És possible que algunes funcions del sistema no funcionin"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'està executant"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'està executant en aquests moments"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'està executant"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Toca per obtenir més informació o bé per aturar l\'aplicació."</string>
<string name="ok" msgid="5970060430562524910">"D\'acord"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel·la"</string>
<string name="yes" msgid="5362982303337969312">"D\'acord"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Accessibilitat"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fons de pantalla"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Canvia el fons de pantalla"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Processador de notificacions"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> ha activat VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca per gestionar la xarxa."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Torna a la pàgina d\'inici"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Mou cap a dalt"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Més opcions"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Emmagatzematge intern"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Targeta SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Emmagatzematge USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Error"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Aquesta aplicació no admet comptes per a usuaris limitats"</string>
<string name="app_not_found" msgid="3429141853498927379">"No s\'ha trobat cap aplicació per processar aquesta acció"</string>
+ <string name="revoke" msgid="5404479185228271586">"Revoca"</string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 0adebe9..c9a84cb 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Umožňuje aplikaci upravit způsob výpočtu využití sítě aplikacemi. Toto oprávnění není určeno pro běžné aplikace."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"přístup k oznámením"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Umožňuje aplikacím načítat, zobrazovat a mazat oznámení včetně těch přidaných jinými aplikacemi."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"navázání na službu pro poslouchání oznámení"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Umožňuje držiteli navázat se na nejvyšší úroveň služby pro poslouchání oznámení. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Nastavit pravidla pro heslo"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Řídit délku hesel pro odemčení obrazovky a povolené znaky."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Sledovat pokusy o odemčení obrazovky"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Operace s textem"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"V úložišti je málo místa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Některé systémové funkce nemusí fungovat"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je spuštěna"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je aktuálně spuštěná"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je spuštěna"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Klepnutím zobrazíte další informace nebo ukončíte aplikaci."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Zrušit"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Usnadnění"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapeta"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Změnit tapetu"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Aplikace poslouchající oznámení"</string>
<string name="vpn_title" msgid="19615213552042827">"Síť VPN je aktivována"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikace <xliff:g id="APP">%s</xliff:g> aktivovala síť VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotykem zobrazíte správu sítě."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Přejít na plochu"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Přejít nahoru"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Další možnosti"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s – %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s – %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Interní úložiště"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Karta SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Úložiště USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Chyba"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Tato aplikace u omezeného počtu uživatelů nepodporuje účty"</string>
<string name="app_not_found" msgid="3429141853498927379">"Aplikace potřebná k provedení této akce nebyla nalezena"</string>
+ <string name="revoke" msgid="5404479185228271586">"Zrušit"</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index bb4eb72..0c7cf72 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Tillader, at appen kan ændre den måde, som netværksforbrug udregnes på i forhold til apps. Anvendes ikke af normale apps."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"adgang til underretninger"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Tillader, at appen kan hente, undersøge og rydde underretninger, herunder dem, der er sendt af andre apps."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"forpligte sig til en underretningslyttertjeneste"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Tillader brugeren at forpligte sig til en underretningslyttertjenestes grænseflade på øverste niveau. Bør aldrig være nødvendigt til almindelige apps."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Indstil regler for adgangskode"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontroller længden samt tilladte tegn i adgangskoder til oplåsning af skærmen."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Overvåg forsøg på oplåsning af skærm"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der er snart ikke mere lagerplads"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nogle systemfunktioner virker måske ikke"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> kører"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> kører i øjeblikket"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> kører"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Tryk for at få flere oplysninger eller for at stoppe appen."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Annuller"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Tilgængelighed"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapet"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Skift tapet"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Underretningslytter"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN er aktiveret."</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN aktiveres af <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tryk for at administrere netværket."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Naviger hjem"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Naviger op"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Flere valgmuligheder"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Internt lager"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kort"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB-lager"</string>
@@ -1476,10 +1481,8 @@
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Tilgængelighed er annulleret."</string>
<string name="user_switched" msgid="3768006783166984410">"Nuværende bruger <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Ejer"</string>
- <!-- no translation found for error_message_title (4510373083082500195) -->
- <skip />
- <!-- no translation found for app_no_restricted_accounts (5322164210667258876) -->
- <skip />
- <!-- no translation found for app_not_found (3429141853498927379) -->
- <skip />
+ <string name="error_message_title" msgid="4510373083082500195">"Fejl"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Denne applikation understøtter ikke konti for brugere med begrænsede rettigheder"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"Der blev ikke fundet nogen applikation, der kan håndtere denne handling"</string>
+ <string name="revoke" msgid="5404479185228271586">"Tilbagekald"</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 6a488c7..c03aa70 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -114,7 +114,7 @@
<string name="httpErrorLookup" msgid="4711687456111963163">"URL wurde nicht gefunden."</string>
<string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"Das Authentifizierungsschema für die Website wird nicht unterstützt."</string>
<string name="httpErrorAuth" msgid="1435065629438044534">"Bei der Authentifizierung ist ein Fehler aufgetreten."</string>
- <string name="httpErrorProxyAuth" msgid="1788207010559081331">"Authentifizierung via Proxy-Server ist fehlgeschlagen."</string>
+ <string name="httpErrorProxyAuth" msgid="1788207010559081331">"Authentifizierung via Proxyserver ist fehlgeschlagen."</string>
<string name="httpErrorConnect" msgid="8714273236364640549">"Verbindung zum Server konnte nicht hergestellt werden."</string>
<string name="httpErrorIO" msgid="2340558197489302188">"Kommunikation mit dem Server konnte nicht hergestellt werden. Bitte versuchen Sie es später erneut."</string>
<string name="httpErrorTimeout" msgid="4743403703762883954">"Zeitüberschreitung bei Serververbindung."</string>
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Ermöglicht der App, die Art und Weise zu ändern, wie der Netzwerkverbrauch im Hinblick auf Apps berechnet wird. Nicht für normale Apps vorgesehen."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"Auf Benachrichtigungen zugreifen"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Ermöglicht der App das Abrufen, Überprüfen und Löschen von Benachrichtigungen, einschließlich Benachrichtigungen, die von anderen Apps gepostet wurden"</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"An Benachrichtigungs-Listener-Dienst binden"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ermöglicht dem Inhaber, sich an die Oberfläche der obersten Ebene eines Benachrichtigungs-Listener-Dienstes zu binden. Sollte nie für normale Apps benötigt werden."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Passwortregeln festlegen"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Zulässige Länge und Zeichen für Passwörter zum Entsperren des Bildschirms festlegen"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Versuche zum Entsperren des Displays überwachen"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Textaktionen"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der Speicherplatz wird knapp"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Einige Systemfunktionen funktionieren möglicherweise nicht."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> wird ausgeführt."</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> wird gerade ausgeführt."</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> wird ausgeführt"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Für weitere Informationen oder zum Anhalten der App tippen"</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Abbrechen"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1066,7 +1068,7 @@
<string name="noApplications" msgid="2991814273936504689">"Diese Aktion kann von keiner App ausgeführt werden."</string>
<string name="aerr_title" msgid="1905800560317137752"></string>
<string name="aerr_application" msgid="932628488013092776">"\"<xliff:g id="APPLICATION">%1$s</xliff:g>\" wurde beendet."</string>
- <string name="aerr_process" msgid="4507058997035697579">"Der Prozess <xliff:g id="PROCESS">%1$s</xliff:g> wurde beendet."</string>
+ <string name="aerr_process" msgid="4507058997035697579">"Der Prozess \"<xliff:g id="PROCESS">%1$s</xliff:g>\" wurde beendet."</string>
<string name="anr_title" msgid="4351948481459135709"></string>
<string name="anr_activity_application" msgid="1904477189057199066">"<xliff:g id="APPLICATION">%2$s</xliff:g> reagiert nicht."\n\n"Möchten Sie die App schließen?"</string>
<string name="anr_activity_process" msgid="5776209883299089767">"Aktivität \"<xliff:g id="ACTIVITY">%1$s</xliff:g>\" reagiert nicht."\n\n"Möchten Sie sie beenden?"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Bedienungshilfen"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Hintergrund"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Hintergrund ändern"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Benachrichtigungs-Listener"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktiviert"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN wurde von <xliff:g id="APP">%s</xliff:g> aktiviert."</string>
<string name="vpn_text" msgid="3011306607126450322">"Zum Verwalten des Netzwerks berühren"</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Zur Startseite navigieren"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Nach oben navigieren"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Weitere Optionen"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s. %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s. %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Interner Speicher"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-Karte"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB-Speicher"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Fehler"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Diese App unterstützt keine Konten für eingeschränkte Nutzer."</string>
<string name="app_not_found" msgid="3429141853498927379">"Für diese Aktion wurde keine App gefunden."</string>
+ <string name="revoke" msgid="5404479185228271586">"Aufheben"</string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 21b014c..6dcdbf1 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Επιτρέπει στην εφαρμογή την τροποποίηση του τρόπου υπολογισμού της χρήσης δικτύου έναντι των εφαρμογών. Δεν προορίζεται για χρήση από συνήθεις εφαρμογές."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"πρόσβαση στις ειδοποιήσεις"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Επιτρέπει στην εφαρμογή να ανακτά, να εξετάζει και να απαλείφει ειδοποιήσεις, συμπεριλαμβανομένων εκείνων που δημοσιεύονται από άλλες εφαρμογές."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"δέσμευση σε υπηρεσία ακρόασης ειδοποίησης"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας υπηρεσίας ακρόασης ειδοποιήσεων. Δεν απαιτείται σε κανονικές εφαρμογές."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Ορισμός κανόνων κωδικού πρόσβασης"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Έλεγχος του μεγέθους και των χαρακτήρων που επιτρέπονται στους κωδικούς πρόσβασης ξεκλειδώματος οθόνης."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Παρακολούθηση προσπαθειών ξεκλειδώματος οθόνης"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Ενέργειες κειμένου"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ο χώρος αποθήκευσης εξαντλείται"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εκτελείται"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εκτελείται τώρα."</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εκτελείται"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Αγγίξτε για περισσότερες πληροφορίες ή για να διακόψετε την εκτέλεση της εφαρμογής."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Ακύρωση"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Προσβασιμότητα"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Ταπετσαρία"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Αλλαγή ταπετσαρίας"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Υπηρεσία ακρόασης ειδοποίησης"</string>
<string name="vpn_title" msgid="19615213552042827">"Το VPN ενεργοποιήθηκε"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Το VPN ενεργοποιήθηκε από την εφαρμογή <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Αγγίξτε για τη διαχείριση του δικτύου."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Πλοήγηση στην αρχική σελίδα"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Πλοήγηση προς τα επάνω"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Περισσότερες επιλογές"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Εσωτερικός χώρος αποθήκευσης"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Κάρτα SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Χώρος αποθήκευσης USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Σφάλμα"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Αυτή η εφαρμογή δεν υποστηρίζει λογαριασμούς για περιορισμένους χρήστες"</string>
<string name="app_not_found" msgid="3429141853498927379">"Δεν υπάρχει εφαρμογή για τη διαχείριση αυτής της ενέργειας"</string>
+ <string name="revoke" msgid="5404479185228271586">"Ανάκληση"</string>
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index ae698e1..08283c4 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Allows the app to modify how network usage is accounted against apps. Not for use by normal apps."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"access notifications"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Allows the app to retrieve, examine, and clear notifications, including those posted by other apps."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bind to a notification listener service"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Allows the holder to bind to the top-level interface of a notification listener service. Should never be needed for normal apps."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Set password rules"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Control the length and the characters allowed in screen-unlock passwords."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitor screen-unlock attempts"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Text actions"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> running"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> is currently running"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> is running"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Touch for more information or to stop the app."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Accessibility"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Change wallpaper"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Notification listener"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activated"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is activated by <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Touch to manage the network."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Navigate home"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Navigate up"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"More options"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Internal storage"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD card"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB storage"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Error"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"This application does not support accounts for limited users"</string>
<string name="app_not_found" msgid="3429141853498927379">"No application found to handle this action"</string>
+ <string name="revoke" msgid="5404479185228271586">"Revoke"</string>
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 61d768d..98528f7 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que la aplicación modifique cómo se registra el uso de red en relación con las aplicaciones. Las aplicaciones normales no deben usar este permiso."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"acceder a las notificaciones"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que la aplicación recupere, examine y elimine notificaciones, incluidas aquellas publicadas por otras aplicaciones."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"Vincular a un servicio de agente de escucha de notificaciones"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite al propietario vincularse a la interfaz de nivel superior de un servicio de agente de escucha de notificaciones. 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>
<string name="policylab_watchLogin" msgid="914130646942199503">"Supervisa los intentos para desbloquear la pantalla"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio de almacenamiento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no estén disponibles."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> en ejecución"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando en este momento."</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Toca para obtener más información o para detener la aplicación."</string>
<string name="ok" msgid="5970060430562524910">"Aceptar"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"Aceptar"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Accesibilidad"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Papel tapiz"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Cambiar fondo de pantalla"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Agente de escucha de notificaciones"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN está activado por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca para administrar la red."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Desplazarse hasta la página principal"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Desplazarse hacia arriba"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Más opciones"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Almacenamiento interno"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Tarjeta SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Almacenamiento USB"</string>
@@ -1476,10 +1481,8 @@
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Se canceló la accesibilidad."</string>
<string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="owner_name" msgid="2716755460376028154">"Propietario"</string>
- <!-- no translation found for error_message_title (4510373083082500195) -->
- <skip />
- <!-- no translation found for app_no_restricted_accounts (5322164210667258876) -->
- <skip />
- <!-- no translation found for app_not_found (3429141853498927379) -->
- <skip />
+ <string name="error_message_title" msgid="4510373083082500195">"Error"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Esta aplicación no admite cuentas para usuarios restringidos."</string>
+ <string name="app_not_found" msgid="3429141853498927379">"No se encontró una aplicación para manejar esta acción."</string>
+ <string name="revoke" msgid="5404479185228271586">"Revocar"</string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index bdeed44..485e1bf 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -559,8 +559,8 @@
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Permite que la aplicación modifique el estado de la conectividad de red."</string>
<string name="permlab_changeTetherState" msgid="5952584964373017960">"cambiar conectividad de anclaje a red"</string>
<string name="permdesc_changeTetherState" msgid="1524441344412319780">"Permite que la aplicación cambie el estado de la conectividad de red de anclaje."</string>
- <string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"cambiar configuración de uso de datos de referencia"</string>
- <string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"Permite que la aplicación cambie los ajustes de uso de datos de referencia."</string>
+ <string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"cambiar configuración de uso de conexiones automáticas"</string>
+ <string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"Permite que la aplicación cambie los ajustes de uso de conexiones automáticas."</string>
<string name="permlab_accessWifiState" msgid="5202012949247040011">"ver conexiones Wi-Fi"</string>
<string name="permdesc_accessWifiState" msgid="5002798077387803726">"Permite que la aplicación vea información sobre conexión a redes Wi-Fi (por ejemplo, si está habilitada la conexión Wi-Fi y el nombre de los dispositivos Wi-Fi conectados)."</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"conectarse a redes Wi-Fi y desconectarse"</string>
@@ -569,14 +569,14 @@
<string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"Permite que la aplicación reciba paquetes enviados a todos los dispositivos de una red Wi-Fi que utilicen direcciones de multidifusión, no solo al tablet. Utiliza más batería que el modo de no multidifusión."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Permite que la aplicación reciba paquetes enviados a todos los dispositivos de una red Wi-Fi que utilicen direcciones de multidifusión, no solo al teléfono. Utiliza más batería que el modo de no multidifusión."</string>
<string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"acceder a los ajustes de Bluetooth"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Permite que la aplicación configure el tablet Bluetooth local y que detecte dispositivos remotos y se sincronice con ellos."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permite que la aplicación configure el teléfono Bluetooth local y que detecte dispositivos remotos y se sincronice con ellos."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Permite que la aplicación configure el tablet Bluetooth local y que detecte dispositivos remotos y se vincule con ellos."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permite que la aplicación configure el teléfono Bluetooth local y que detecte dispositivos remotos y se vincule con ellos."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"conectarse a WiMAX y desconectarse de esta red"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permite que la aplicación determine si está habilitada la conexión WiMAX y obtenga información sobre las redes WiMAX que están conectadas."</string>
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Cambiar estado de WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite que la aplicación conecte el tablet a redes WiMAX y lo desconecte de ellas."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite que la aplicación conecte el teléfono a redes WiMAX y lo desconecte de ellas."</string>
- <string name="permlab_bluetooth" msgid="6127769336339276828">"sincronizarse con dispositivos Bluetooth"</string>
+ <string name="permlab_bluetooth" msgid="6127769336339276828">"vincular con dispositivos Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Permite que la aplicación acceda a la configuración de Bluetooth del tablet y que establezca y acepte conexiones con los dispositivos sincronizados."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Permite que la aplicación acceda a la configuración de Bluetooth del teléfono y que establezca y acepte conexiones con los dispositivos sincronizados."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"controlar Comunicación de campo cercano (NFC)"</string>
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que la aplicación modifique cómo se registra el uso de red en relación con las aplicaciones. Las aplicaciones normales no deben usar este permiso."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"acceder a las notificaciones"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que la aplicación recupere, examine y borre notificaciones, incluidas las que han publicado otras aplicaciones."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"enlazar con un servicio de detector de notificaciones"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite enlazar con la interfaz de nivel superior de un servicio de detector de notificaciones. No debe ser necesario para las aplicaciones normales."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Establecimiento de reglas de contraseña"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar la longitud y los caracteres permitidos en las contraseñas de bloqueo de pantalla"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Control de intentos de bloqueo de pantalla"</string>
@@ -700,7 +702,7 @@
<string name="phoneTypeIsdn" msgid="8022453193171370337">"RDSI"</string>
<string name="phoneTypeMain" msgid="6766137010628326916">"Principal"</string>
<string name="phoneTypeOtherFax" msgid="8587657145072446565">"Otro fax"</string>
- <string name="phoneTypeRadio" msgid="4093738079908667513">"Radio"</string>
+ <string name="phoneTypeRadio" msgid="4093738079908667513">"Señal móvil"</string>
<string name="phoneTypeTelex" msgid="3367879952476250512">"Télex"</string>
<string name="phoneTypeTtyTdd" msgid="8606514378585000044">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Móvil del trabajo"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no funcionen."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando."</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Toca para obtener más información o para detener la aplicación."</string>
<string name="ok" msgid="5970060430562524910">"Aceptar"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"Aceptar"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Accesibilidad"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fondo de pantalla"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Cambiar fondo de pantalla"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Detector de notificaciones"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN activada por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca para administrar la red."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Ir al escritorio"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Desplazarse hacia arriba"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Más opciones"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Almacenamiento interno"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Tarjeta SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Almacenamiento USB"</string>
@@ -1386,7 +1391,7 @@
<string name="data_usage_mobile_limit_snoozed_title" msgid="279240572165412168">"Límite de datos móviles superado"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Límite de datos Wi-Fi superado"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"Límite superado en <xliff:g id="SIZE">%s</xliff:g>"</string>
- <string name="data_usage_restricted_title" msgid="5965157361036321914">"Datos de referencia restringidos"</string>
+ <string name="data_usage_restricted_title" msgid="5965157361036321914">"Conexiones automáticas restringidas"</string>
<string name="data_usage_restricted_body" msgid="6741521330997452990">"Toca para eliminar la restricción."</string>
<string name="ssl_certificate" msgid="6510040486049237639">"Certificado de seguridad"</string>
<string name="ssl_certificate_is_valid" msgid="6825263250774569373">"Este certificado es válido."</string>
@@ -1415,7 +1420,7 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Teléfono"</string>
<string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Auriculares"</string>
- <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altavoces del conector"</string>
+ <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altavoces de la base"</string>
<string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
@@ -1476,10 +1481,8 @@
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accesibilidad cancelada"</string>
<string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="owner_name" msgid="2716755460376028154">"Propietario"</string>
- <!-- no translation found for error_message_title (4510373083082500195) -->
- <skip />
- <!-- no translation found for app_no_restricted_accounts (5322164210667258876) -->
- <skip />
- <!-- no translation found for app_not_found (3429141853498927379) -->
- <skip />
+ <string name="error_message_title" msgid="4510373083082500195">"Error"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Esta aplicación no admite cuentas de usuarios limitados."</string>
+ <string name="app_not_found" msgid="3429141853498927379">"No se ha encontrado ninguna aplicación que pueda realizar esta acción."</string>
+ <string name="revoke" msgid="5404479185228271586">"Revocar"</string>
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 9c7a28f..d2d4d63 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Võimaldab rakendusel muuta võrgukasutuse loendamist rakenduste suhtes. Mitte kasutada tavarakenduste puhul."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"juurdepääsu märguanded"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Võimaldab rakendusel tuua, kontrollida ja kustutada märguandeid, sh neid, mille on postitanud teised rakendused."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"seo märguannete kuulamisteenusega"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Võimaldab omanikul siduda märguannete kuulamisteenuse ülemise taseme kasutajaliidese. Seda ei tohiks tavarakenduste puhul kunagi vaja olla."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Parooli reeglite määramine"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrollige ekraaniluku avamise paroolide pikkust ja tähemärke."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Ekraani avamiskatsed"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstitoimingud"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Talletusruum saab täis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Mõned süsteemifunktsioonid ei pruugi töötada"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> töötab"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> töötab praegu"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> töötab"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Puudutage lisateabe saamiseks või rakenduse peatamiseks."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Tühista"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Juurdepääsetavus"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Taustapilt"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Muutke taustapilti"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Märguannete kuulamisteenus"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN on aktiveeritud"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN-i aktiveeris <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Võrgu haldamiseks puudutage."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Liigu avalehele"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Liigu üles"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Rohkem valikuid"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Sisemine salvestusruum"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kaart"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB-mäluseade"</string>
@@ -1477,6 +1482,7 @@
<string name="user_switched" msgid="3768006783166984410">"Praegune kasutaja <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Omanik"</string>
<string name="error_message_title" msgid="4510373083082500195">"Viga"</string>
- <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Rakendus ei toeta piiratud arvu kasutajate kontot"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Rakendus ei toeta piiratud õigustega kasutajate kontosid"</string>
<string name="app_not_found" msgid="3429141853498927379">"Selle toimingu käsitlemiseks ei leitud ühtegi rakendust"</string>
+ <string name="revoke" msgid="5404479185228271586">"Tühista"</string>
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 4a09361..f723c37 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"به برنامه اجازه می‎دهد تا نحوه محاسبه کاربرد شبکه در برنامه را تغییر دهد. برای استفاده برنامه‎های عادی نیست."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"اعلان‌های دسترسی"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"به برنامه اجازه می‌دهد به بازیابی، بررسی و پاک کردن اعلان‌ها از جمله موارد پست شده توسط سایر برنامه‌ها بپردازد."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"اتصال به یک سرویس شنونده اعلان"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"به دارنده اجازه می‌دهد به یک رابط سطح بالای سرویس شنونده اعلان متصل شود. این هرگز برای برنامه‌های عادی ضروری نیست."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین رمز ورود"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"طول و نویسه‎های مجاز در گذرواژه‌های بازکردن قفل صفحه را کنترل کنید."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"نمایش تلاش‌های قفل گشایی صفحه"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"عملکردهای متنی"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"فضای ذخیره‌سازی رو به اتمام است"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> در حال اجرا"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> در حال حاضر در حال اجرا است"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> در حال اجرا است"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"برای کسب اطلاعات بیشتر یا توقف برنامه لمس کنید."</string>
<string name="ok" msgid="5970060430562524910">"تأیید"</string>
<string name="cancel" msgid="6442560571259935130">"لغو"</string>
<string name="yes" msgid="5362982303337969312">"تأیید"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"قابلیت دسترسی"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"تصویر زمینه"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"تغییر تصویر زمینه"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"شنونده اعلان"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN فعال شد"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN توسط <xliff:g id="APP">%s</xliff:g> فعال شده است"</string>
<string name="vpn_text" msgid="3011306607126450322">"برای مدیریت شبکه لمس کنید."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"رفتن به صفحهٔ اصلی"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"حرکت به بالا"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"سایر گزینه‌ها"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"‎%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"‎%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"حافظهٔ داخلی"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"کارت SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"حافظهٔ USB"</string>
@@ -1476,10 +1481,8 @@
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"قابلیت دسترسی لغو شد."</string>
<string name="user_switched" msgid="3768006783166984410">"کاربر کنونی <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"دارنده"</string>
- <!-- no translation found for error_message_title (4510373083082500195) -->
- <skip />
- <!-- no translation found for app_no_restricted_accounts (5322164210667258876) -->
- <skip />
- <!-- no translation found for app_not_found (3429141853498927379) -->
- <skip />
+ <string name="error_message_title" msgid="4510373083082500195">"خطا"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"این برنامه حساب‌های تعداد محدودی از کاربران را پشتیبانی نمی‌کند"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"برنامه‌ای برای انجام این عملکرد موجود نیست"</string>
+ <string name="revoke" msgid="5404479185228271586">"لغو"</string>
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 0f71020..7f7f07a 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Antaa sovelluksen muokata, miten sovellusten verkonkäyttöä lasketaan. Ei tavallisten sovellusten käyttöön."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"käytä ilmoituksia"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Antaa sovelluksen noutaa, tutkia ja tyhjentää ilmoituksia (myös muiden sovelluksien lähettämiä)."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"sido ilmoituskuuntelijapalveluun"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Antaa sovelluksen sitoutua ilmoituskuuntelijan ylimmän tason käyttöliittymään. Ei tavallisten sovelluksien käyttöön."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Aseta salasanasäännöt"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Hallinnoi ruudun lukituksenpoistosalasanoissa sallittuja merkkejä ja salasanan pituutta."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Tarkkaile ruudun lukituksen poistoyrityksiä"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstitoiminnot"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Tallennustila loppumassa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kaikki järjestelmätoiminnot eivät välttämättä toimi"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> on käynnissä"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> on käynnissä"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> on käynnissä"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Hanki lisätietoja tai sulje sovellus koskettamalla."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Peruuta"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Esteettömyys"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Taustakuva"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Vaihda taustakuvaa"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Ilmoituskuuntelija"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN on aktivoitu"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> on aktivoinut VPN-yhteyden"</string>
<string name="vpn_text" msgid="3011306607126450322">"Voit hallinnoida verkkoa koskettamalla."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Siirry etusivulle"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Siirry ylös"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Lisää asetuksia"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Sisäinen tallennustila"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kortti"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB-tallennustila"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Virhe"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Tämä sovellus ei tue rajoitettujen käyttäjien tilejä"</string>
<string name="app_not_found" msgid="3429141853498927379">"Tätä toimintoa käsittelevää sovellusta ei löydy"</string>
+ <string name="revoke" msgid="5404479185228271586">"Peruuta"</string>
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 2026f56..65c4ce5 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permet à l\'application de modifier l\'utilisation du réseau par les autres applications. Les applications standards ne doivent pas utiliser cette fonctionnalité."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"accéder aux notifications"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permet aux applications de récupérer, d\'examiner et d\'autoriser les notifications, y compris celles envoyées par d\'autres applications."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"s\'associer à l\'interface de niveau supérieur d\'un service d\'écoute des notifications"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permet à l\'application de s\'associer à l\'interface de niveau supérieur d\'un service d\'écoute des notifications. Ne devrait jamais être nécessaire pour les applications normales."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Définir les règles du mot de passe"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Choisir le nombre et le type de caractères autorisés dans les mots de passe de déverrouillage de l\'écran"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Gérer les tentatives de déverrouillage de l\'écran"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Actions sur le texte"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> en cours d\'exécution"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> est en cours d\'exécution."</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> en cours d\'exécution"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Appuyez ici pour en savoir plus ou arrêter l\'application."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Annuler"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Accessibilité"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fond d\'écran"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Changer de fond d\'écran"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Outil d\'écoute des notifications"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activé"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN activé par <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Appuyez ici pour gérer le réseau."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Retour à l\'accueil"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Parcourir vers le haut"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Plus d\'options"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Mémoire de stockage interne"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Carte SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Mémoire de stockage USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Erreur"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Les comptes des utilisateurs en accès limité ne sont pas acceptés pour cette application."</string>
<string name="app_not_found" msgid="3429141853498927379">"Aucune application trouvée pour gérer cette action."</string>
+ <string name="revoke" msgid="5404479185228271586">"Révoquer"</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b70bbc0..79dbfff 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"एप्लिकेशन को यह संशोधित करने देता है कि एप्‍लिकेशन की तुलना में नेटवर्क उपयोग का मूल्यांकन कैसे किया जाता है. सामान्‍य एप्‍लिकेशन द्वारा उपयोग करने के लिए नहीं."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"सूचनाओं तक पहुंचें"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"एप्लिकेशन को सूचनाओं को प्राप्त करने, जांच करने, और साफ़ करने देता है, जिनमें अन्य एप्लिकेशन के द्वारा पोस्ट की गई सूचनाएं भी शामिल हैं."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"सूचना श्रवणकर्ता सेवा से जुड़ें"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"धारक को सूचना श्रवणकर्ता सेवा के शीर्ष स्तरीय इंटरफ़ेस से जुड़ने देती है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यक नहीं होनी चाहिए."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"पासवर्ड नियम सेट करें"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"स्‍क्रीन-अनलॉक पासवर्ड में अनुमति प्राप्त लंबाई और वर्णों को नियंत्रित करें."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"स्‍क्रीन-अनलॉक के प्रयासों पर निगरानी रखें"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"पाठ क्रियाएं"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"संग्रहण स्‍थान समाप्‍त हो रहा है"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"हो सकता है कुछ सिस्टम फ़ंक्शन कार्य न करें"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> चल रहा है"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> वर्तमान में चल रहा है"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> चल रहा है"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"अधिक जानकारी के लिए या एप्लिकेशन रोकने के लिए स्पर्श करें."</string>
<string name="ok" msgid="5970060430562524910">"ठीक"</string>
<string name="cancel" msgid="6442560571259935130">"रद्द करें"</string>
<string name="yes" msgid="5362982303337969312">"ठीक"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"पहुंच-योग्यता"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"वॉलपेपर"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"वॉलपेपर बदलें"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"सूचना श्रवणकर्ता"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN सक्रिय"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN को <xliff:g id="APP">%s</xliff:g> द्वारा सक्रिय किया गया है"</string>
<string name="vpn_text" msgid="3011306607126450322">"नेटवर्क प्रबंधित करने के लिए स्‍पर्श करें."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"होम पर नेविगेट करें"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"ऊपर नेविगेट करें"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"अधिक विकल्प"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"आंतरिक संग्रहण"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD कार्ड"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB संग्रहण"</string>
@@ -1476,10 +1481,8 @@
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"पहुंच-योग्यता रद्द की गई."</string>
<string name="user_switched" msgid="3768006783166984410">"वर्तमान उपयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"स्वामी"</string>
- <!-- no translation found for error_message_title (4510373083082500195) -->
- <skip />
- <!-- no translation found for app_no_restricted_accounts (5322164210667258876) -->
- <skip />
- <!-- no translation found for app_not_found (3429141853498927379) -->
- <skip />
+ <string name="error_message_title" msgid="4510373083082500195">"त्रुटि"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"यह एप्लिकेशन सीमित उपयोगकर्ताओं के खातों का समर्थन नहीं करता है"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"इस कार्यवाही को प्रबंधित करने के लिए कोई एप्लिकेशन नहीं मिला"</string>
+ <string name="revoke" msgid="5404479185228271586">"निरस्‍त करें"</string>
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 7b1eee3..90b44d2 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Omogućuje aplikaciji izmjenu načina upotrebe mreže u odnosu na aplikacije. Nije namijenjeno uobičajenim aplikacijama."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"pristup obavijestima"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Omogućuje aplikaciji dohvaćanje, pregledavanje i brisanje obavijesti, uključujući obavijesti drugih aplikacija."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vezanje uz uslugu slušatelja obavijesti"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Nositelju omogućuje vezanje uz sučelje najviše razine usluge slušatelja obavijesti. Ne bi smjelo biti potrebno za uobičajene aplikacije."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Postavi pravila zaporke"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Upravljajte duljinom zaporki za otključavanje zaslona i dopuštenim znakovima u tim zaporkama."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Nadgledaj pokušaje otključavanja zaslona"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Radnje s tekstom"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke sistemske funkcije možda neće raditi"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Izvodi se aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Trenutačno se izvodi aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> pokrenuta je"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Dodirnite za više informacija ili da biste zaustavili aplikaciju."</string>
<string name="ok" msgid="5970060430562524910">"U redu"</string>
<string name="cancel" msgid="6442560571259935130">"Odustani"</string>
<string name="yes" msgid="5362982303337969312">"U redu"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Dostupnost"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Pozadinska slika"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Promjena pozadinske slike"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Slušatelj obavijesti"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktiviran"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikacija <xliff:g id="APP">%s</xliff:g> aktivirala je VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dodirnite za upravljanje mrežom."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Kreni na početnu"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Kreni gore"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Više opcija"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Interna pohrana"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kartica"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB pohrana"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Pogreška"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Aplikacija ne podržava račune za ograničene korisnike"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nije pronađena aplikacija za upravljanje ovom radnjom"</string>
+ <string name="revoke" msgid="5404479185228271586">"Opozovi"</string>
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index fc1d663..d71ac23 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Lehetővé teszi az alkalmazás számára annak módosítását, hogy a hálózathasználatot hogyan számolják el az alkalmazások esetében. Normál alkalmazások nem használhatják."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"hozzáférési értesítések"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Lehetővé teszi, hogy az alkalmazás értesítéseket kérdezzen le, vizsgáljon és tisztítson meg, beleértve az egyéb alkalmazások által közzétett értesítéseket is."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"csatlakozzon értesítésfigyelő szolgáltatáshoz"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Lehetővé teszi a használó számára, hogy csatlakozzon egy értesítésfigyelő szolgáltatás legfelső szintű felületéhez. A normál alkalmazásoknak erre soha nincs szükségük."</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>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Műveletek szöveggel"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kevés a szabad terület"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Előfordulhat, hogy néhány rendszerfunkció nem működik."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> fut"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> jelenleg fut"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> jelenleg fut"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"További információért, illetve az alkalmazás leállításához érintse meg."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Mégse"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Kisegítő lehetőségek"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Háttérkép"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Háttérkép megváltoztatása"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Értesítésfigyelő"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktiválva"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"A(z) <xliff:g id="APP">%s</xliff:g> aktiválta a VPN-t"</string>
<string name="vpn_text" msgid="3011306607126450322">"Érintse meg a hálózat kezeléséhez."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Ugrás a főoldalra"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Felfele mozgás"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"További lehetőségek"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Belső tárhely"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kártya"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB-tár"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Hiba"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ez az alkalmazás nem támogatja a korlátozott jogokkal rendelkező felhasználói fiókokat."</string>
<string name="app_not_found" msgid="3429141853498927379">"Nincs megfelelő alkalmazás a művelet elvégzésére."</string>
+ <string name="revoke" msgid="5404479185228271586">"Visszavonás"</string>
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 2a9f2fc..a9b33de 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Mengizinkan apl memodifikasi cara penggunaan jaringan diperhitungkan terhadap apl. Tidak untuk digunakan oleh apl normal."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"mengakses pemberitahuan"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Mengizinkan aplikasi mengambil, memeriksa, dan menghapus pemberitahuan, termasuk pemberitahuan yang diposkan oleh aplikasi lain."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"mengikat layanan pendengar pemberitahuan"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Memungkinkan pemegang mengikat antarmuka tingkat teratas dari suatu layanan pendengar pemberitahuan. Tidak pernah diperlukan oleh aplikasi normal."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Setel aturan sandi"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrol panjang dan karakter yang diizinkan dalam sandi pembuka layar."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Upaya pembukaan kunci layar monitor"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tindakan teks"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang penyimpanan hampir habis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> berjalan"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang berjalan"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang berjalan"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Sentuh untuk informasi selengkapnya atau hentikan aplikasi."</string>
<string name="ok" msgid="5970060430562524910">"Oke"</string>
<string name="cancel" msgid="6442560571259935130">"Batal"</string>
<string name="yes" msgid="5362982303337969312">"Oke"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Aksesibilitas"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Ubah wallpaper"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Pendengar pemberitahuan"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN diaktifkan"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN diaktifkan oleh <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Sentuh untuk mengelola jaringan."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Navigasi ke beranda"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Navigasi naik"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Opsi lainnya"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Penyimpanan internal"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kartu SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Penyimpanan USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Kesalahan"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Aplikasi ini tidak mendukung akun untuk pengguna terbatas"</string>
<string name="app_not_found" msgid="3429141853498927379">"Tidak ada aplikasi yang ditemukan untuk menangani tindakan ini"</string>
+ <string name="revoke" msgid="5404479185228271586">"Cabut"</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 2273459..560b049 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Consente all\'applicazione di modificare il calcolo dell\'utilizzo della rete tra le applicazioni. Da non usare per normali applicazioni."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"accesso a notifiche"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Consente all\'app di recuperare, esaminare e cancellare notifiche, comprese quelle pubblicate da altre app."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincolo a un servizio listener di notifica"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Consente al titolare di vincolarsi all\'interfaccia di primo livello di un servizio listener di notifica. Non dovrebbe mai essere necessaria per le normali applicazioni."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Imposta regole password"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlla la lunghezza e i caratteri ammessi nelle password di sblocco dello schermo."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitora tentativi di sblocco dello schermo"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Azioni testo"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spazio di archiviazione in esaurimento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Alcune funzioni di sistema potrebbero non funzionare"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> in esecuzione"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> è attualmente in esecuzione"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> è in esecuzione"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Tocca per ulteriori informazioni o per interrompere l\'app."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Annulla"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Accessibilità"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Sfondo"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Cambia sfondo"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Listener di notifica"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN attiva"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN attivata da <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tocca per gestire la rete."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Vai alla home page"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Vai in alto"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Altre opzioni"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Memoria interna"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Scheda SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Archivio USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Errore"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Questa applicazione non supporta account di utenti con limitazioni"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nessuna applicazione trovata in grado di gestire questa azione"</string>
+ <string name="revoke" msgid="5404479185228271586">"Revoca"</string>
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 8c7c74e..42f0e2d 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"הרשאה זו מאפשרת ליישום לשנות את אופן החישוב של נתוני שימוש ברשת מול כל יישום. לא מיועד לשימוש ביישומים רגילים."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"גישה להתראות"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"מאפשר ליישום לאחזר, לבדוק ולמחוק התראות, כולל כאלה שפורסמו על ידי יישומים אחרים."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"איגוד לשירות של מאזין להתראות"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"הרשאה זו מאפשרת למשתמש לבצע איגוד לממשק הרמה העליונה של שירות מאזין להתראות. הרשאה זו אף פעם אינה נחוצה ליישומים רגילים."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"הגדר כללי סיסמה"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"שלוט באורך ובתווים המותרים בסיסמאות לביטול נעילת מסך."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"עקוב אחר ניסיונות לביטול נעילת מסך"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"פעולות טקסט"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"שטח האחסון אוזל"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> פועל"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> פועל כרגע"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> פועל"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"גע לקבלת מידע נוסף או כדי לעצור את היישום."</string>
<string name="ok" msgid="5970060430562524910">"אישור"</string>
<string name="cancel" msgid="6442560571259935130">"ביטול"</string>
<string name="yes" msgid="5362982303337969312">"אישור"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"נגישות"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"טפט"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"שנה טפט"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"מאזין להתראות"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN מופעל"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN מופעל על ידי <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"גע כדי לנהל את הרשת."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"נווט לדף הבית"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"נווט למעלה"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"אפשרויות נוספות"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s‏, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s‏, %2$s‏, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"אחסון פנימי"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"כרטיס SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"אחסון USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"שגיאה"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"היישום הזה לא תומך בחשבונות עבור משתמשים מוגבלים"</string>
<string name="app_not_found" msgid="3429141853498927379">"לא נמצא יישום שתומך בפעולה זו"</string>
+ <string name="revoke" msgid="5404479185228271586">"בטל"</string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 4ec756e..f6b93f9 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"アプリに対するネットワーク利用の計算方法を変更することをアプリに許可します。通常のアプリでは使用しません。"</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"通知にアクセス"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"通知(他のアプリから投稿されたものも含む)を取得、調査、クリアすることをアプリに許可します。"</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"通知リスナーサービスにバインド"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"通知リスナーサービスのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"パスワードルールの設定"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"画面ロック解除パスワードの長さと使用できる文字を制御します。"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"画面ロック解除試行の監視"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"テキスト操作"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"空き容量わずか"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"一部のシステム機能が動作しない可能性があります"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g>を実行中"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"現在<xliff:g id="APP_NAME">%1$s</xliff:g>を実行しています"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g>を実行しています"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"タップすると詳細が表示されるか、アプリが停止します。"</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"キャンセル"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"ユーザー補助"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"壁紙"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"壁紙を変更"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"通知リスナー"</string>
<string name="vpn_title" msgid="19615213552042827">"VPNが有効になりました"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPNが<xliff:g id="APP">%s</xliff:g>により有効化されました"</string>
<string name="vpn_text" msgid="3011306607126450322">"タップしてネットワークを管理します。"</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"ホームへ移動"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"上へ移動"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"その他のオプション"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s、%2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s、%2$s、%3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"内部ストレージ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SDカード"</string>
<string name="storage_usb" msgid="3017954059538517278">"USBストレージ"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"エラー"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"このアプリでは限定ユーザー用のアカウントはサポートしていません"</string>
<string name="app_not_found" msgid="3429141853498927379">"この操作を行うアプリが見つかりません"</string>
+ <string name="revoke" msgid="5404479185228271586">"取り消し"</string>
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index e6010ac..5f50ac4 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"애플리케이션이 애플리케이션의 네트워크 사용량을 계산하는 방식을 수정할 수 있도록 허용합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"알림 액세스"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"앱이 다른 앱에서 게시한 알림을 비롯하여 알림을 검색하고 살펴보며 삭제할 수 있도록 허용합니다."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"알림 수신기 서비스 사용"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"권한을 가진 프로그램이 알림 수신기 서비스에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"비밀번호 규칙 설정"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"화면 잠금해제 비밀번호에 허용되는 길이 및 문자 수를 제어합니다."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"화면 잠금해제 시도 모니터링"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"텍스트 작업"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"저장 공간이 부족함"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"일부 시스템 기능이 작동하지 않을 수 있습니다."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> 실행 중"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g>이(가) 현재 실행 중입니다."</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g>이(가) 실행 중입니다."</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"자세한 정보를 보거나 앱을 중지하려면 터치하세요."</string>
<string name="ok" msgid="5970060430562524910">"확인"</string>
<string name="cancel" msgid="6442560571259935130">"취소"</string>
<string name="yes" msgid="5362982303337969312">"확인"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"접근성"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"배경화면"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"배경화면 변경"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"알림 수신기"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN이 활성화됨"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN이 <xliff:g id="APP">%s</xliff:g>에 의해 활성화됨"</string>
<string name="vpn_text" msgid="3011306607126450322">"네트워크를 관리하려면 터치하세요."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"홈 탐색"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"위로 탐색"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"옵션 더보기"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"내부 저장소"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD 카드"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB 저장소"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"오류"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"이 애플리케이션은 제한된 사용자를 위한 계정을 지원하지 않습니다."</string>
<string name="app_not_found" msgid="3429141853498927379">"이 작업을 처리하는 애플리케이션을 찾을 수 없습니다."</string>
+ <string name="revoke" msgid="5404479185228271586">"취소"</string>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index e8d7c26..9237a2c 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Leidžiama programai keisti, kaip tinklas naudojamas, palyginti su programomis. Neskirta naudoti įprastoms programoms."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"pasiekti pranešimus"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Programai leidžiama gauti, patikrinti ir išvalyti pranešimus, įskaitant pranešimus, kuriuos paskelbė kitos programos."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"susisaistyti su pranešimų skaitymo priemonės paslauga"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Leidžiama turėtojui susisaistyti su pranešimų skaitymo priemonės paslaugos aukščiausio lygio sąsaja. Įprastoms programoms to neturėtų prireikti."</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>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksto veiksmai"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Mažėja laisvos saugyklos vietos"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kai kurios sistemos funkcijos gali neveikti"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ paleista"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ šiuo metu paleista"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ vykdoma"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Palieskite, jei norite gauti daugiau informacijos arba sustabdyti programą."</string>
<string name="ok" msgid="5970060430562524910">"Gerai"</string>
<string name="cancel" msgid="6442560571259935130">"Atšaukti"</string>
<string name="yes" msgid="5362982303337969312">"Gerai"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Pasiekiamumas"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Darbalaukio fonas"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Keisti darbalaukio foną"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Pranešimų skaitymo priemonė"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN suaktyvintas"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN suaktyvino „<xliff:g id="APP">%s</xliff:g>“"</string>
<string name="vpn_text" msgid="3011306607126450322">"Palieskite, kad valdytumėte tinklą."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Naršyti pagrindinį puslapį"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Naršyti į viršų"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Daugiau parinkčių"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Vidinė atmintis"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kortelė"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB atmintis"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Klaida"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ši programa nepalaiko apribotų naudotojų paskyrų"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nerasta programa šiam veiksmui apdoroti"</string>
+ <string name="revoke" msgid="5404479185228271586">"Anuliuoti"</string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index f134005..1de841e 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Ļauj lietotnei mainīt to, kā tīkla lietojums tiek uzskaitīts saistībā ar lietotnēm. Atļauja neattiecas uz parastām lietotnēm."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"piekļuve paziņojumiem"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Ļauj lietotnei izgūt, pārbaudīt un dzēst paziņojumus, tostarp lietotņu publicētos paziņojumus."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"saites izveidošana ar paziņojumu uztvērēja pakalpojumu"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ļauj īpašniekam izveidot saiti ar paziņojumu uztvērēja pakalpojuma augšējā līmeņa saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Paroles kārtulu iestatīšana"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrolē ekrāna atbloķēšanas parolē atļautās rakstzīmes un garumu."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Ekrāna atbloķēšanas mēģinājumu pārraudzīšana"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksta darbības"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Paliek maz brīvas vietas"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Dažas sistēmas funkcijas var nedarboties."</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> darbojas"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pašlaik darbojas"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> darbojas"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Pieskarieties, lai iegūtu plašāku informāciju vai apturētu lietotnes darbību."</string>
<string name="ok" msgid="5970060430562524910">"Labi"</string>
<string name="cancel" msgid="6442560571259935130">"Atcelt"</string>
<string name="yes" msgid="5362982303337969312">"Labi"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Pieejamība"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fona tapete"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Tapetes maiņa"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Paziņojumu uztvērējs"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ir aktivizēts."</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Lietojumprogramma <xliff:g id="APP">%s</xliff:g> aktivizēja VPN."</string>
<string name="vpn_text" msgid="3011306607126450322">"Pieskarieties, lai pārvaldītu tīklu."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Pārvietoties uz sākuma ekrānu"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Pārvietoties augšup"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Vairāk opciju"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s: %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s: %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Iekšējā atmiņa"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD karte"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB atmiņa"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Kļūda"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Šajā lietojumprogrammā netiek atbalstīti ierobežotu lietotāju konti."</string>
<string name="app_not_found" msgid="3429141853498927379">"Netika atrasta neviena lietojumprogramma, kas var veikt šo darbību."</string>
+ <string name="revoke" msgid="5404479185228271586">"Atsaukt"</string>
</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 3091e7d..af5b866 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Membenarkan apl untuk mengubah suai bagaimana penggunaan rangkaian diambil kira terhadap apl. Bukan untuk digunakan oleh apl biasa."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"pemberitahuan akses"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Membenarkan apl untuk mendapatkan semula, memeriksa dan memadam bersih pemberitahuan, termasuk yang disiarkan oleh apl lain."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"ikat kepada perkhidmatan pendengar pemberitahuan"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi perkhidmatan pendengar pemberitahuan. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Tetapkan peraturan kata laluan"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Mengawal panjang dan aksara yang dibenarkan dalam kata laluan buka kunci skrin."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Memantau percubaan buka kunci skrin"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tindakan teks"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang storan semakin berkurangan"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak berfungsi"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> berjalan"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang berjalan"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang berjalan"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Sentuh untuk maklumat lanjut atau untuk menghentikan apl."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Batal"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Kebolehaksesan"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Kertas dinding"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Tukar kertas dinding"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Pendengar pemberitahuan"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN diaktifkan"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN diaktifkan oleh <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Sentuh untuk mengurus rangkaian."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Navigasi laman utama"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Navigasi ke atas"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Lagi pilihan"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Storan dalaman"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kad SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Storan USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Ralat"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Aplikasi ini tidak menyokong akaun untuk pengguna terhad"</string>
<string name="app_not_found" msgid="3429141853498927379">"Tidak menemui aplikasi untuk mengendalikan tindakan ini"</string>
+ <string name="revoke" msgid="5404479185228271586">"Batalkan"</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 9d39ac8..e0d9bf2 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Lar appen endre hvordan nettverksbruk regnes ut for apper. Ikke beregnet på vanlige apper."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"varseltilgang"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Lar appen hente, gjennomgå og fjerne varsler, inkludert de som sendes fra andre apper."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"binding til en varsellyttertjeneste"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Lar innehaveren binde seg til det øverste grensesnittnivået for en varsellyttertjeneste. Skal aldri være nødvendig for vanlige apper."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Angi passordregler"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontroller tillatt lengde og tillatte tegn i passord for opplåsing av skjerm."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Overvåk forsøk på opplåsing av skjerm"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lite ledig lagringsplass"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> kjører"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> kjører for øyeblikket"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> kjører"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Trykk for mer informasjon, eller for å stoppe appen."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Tilgjengelighet"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Bakgrunnsbilde"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Velg bakgrunnsbilde"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Varsellytteren"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN er aktivert"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN er aktivert av <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Trykk for å administrere nettverket."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Gå til startsiden"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Gå opp"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Flere alternativer"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s – %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s – %2$s – %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Intern lagring"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kort"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB-lagring"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Feil"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Denne appen støtter ikke kontoer for brukere med begrensninger"</string>
<string name="app_not_found" msgid="3429141853498927379">"Finner ingen apper som kan utføre denne handlingen"</string>
+ <string name="revoke" msgid="5404479185228271586">"Opphev"</string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index b0a6d30..2e92761 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Hiermee kan een app aanpassen hoe het netwerkgebruik wordt toegekend aan apps. Dit wordt niet gebruikt door normale apps."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"toegang tot meldingen"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Hiermee kan de app meldingen ophalen, onderzoeken en wissen, waaronder meldingen die zijn verzonden door andere apps."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"koppelen aan een listener-service voor meldingen"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Hiermee kan de houder koppelen aan de hoofdinterface van een listener-service voor meldingen. Nooit vereist voor normale apps."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Wachtwoordregels instellen"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"De lengte en tekens beheren die zijn toegestaan in wachtwoorden voor schermontgrendeling."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Pogingen voor schermontgrendeling bijhouden"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstacties"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Opslagruimte is bijna vol"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bepaalde systeemfuncties werken mogelijk niet"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> is actief"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> is momenteel actief"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> wordt uitgevoerd"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Raak aan voor meer informatie of om de app te stoppen."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Annuleren"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Toegankelijkheid"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Achtergrond"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Achtergrond wijzigen"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Listener voor meldingen"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN is geactiveerd"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN wordt geactiveerd door <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Raak aan om het netwerk te beheren."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Navigeren naar startpositie"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Omhoog navigeren"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Meer opties"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Interne opslag"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kaart"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB-opslag"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Fout"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Deze app ondersteunt geen accounts voor beperkte gebruikers"</string>
<string name="app_not_found" msgid="3429141853498927379">"Er is geen app gevonden om deze actie uit te voeren"</string>
+ <string name="revoke" msgid="5404479185228271586">"Intrekken"</string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 8d3122f..d8a4bf9 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Pozwala aplikacji na zmienianie sposobu rozliczania wykorzystania sieci przez aplikacje. Nieprzeznaczone dla zwykłych aplikacji."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"dostęp do powiadomień"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Umożliwia aplikacji pobieranie, sprawdzanie i usuwanie powiadomień, także tych, które pochodzą z innych aplikacji."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"utwórz połączenie z usługą odbiornika powiadomień"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Zezwala na tworzenie powiązania z interfejsem najwyższego poziomu usługi odbiornika powiadomień. Nie powinno być nigdy potrzebne dla zwykłych aplikacji."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Określ reguły hasła"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrolowanie długości haseł odblokowania ekranu i dozwolonych w nich znaków"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitoruj próby odblokowania ekranu"</string>
@@ -744,7 +746,7 @@
<string name="relationTypeDomesticPartner" msgid="6904807112121122133">"Partner życiowy"</string>
<string name="relationTypeFather" msgid="5228034687082050725">"Ojciec"</string>
<string name="relationTypeFriend" msgid="7313106762483391262">"Znajomy"</string>
- <string name="relationTypeManager" msgid="6365677861610137895">"Kierownik"</string>
+ <string name="relationTypeManager" msgid="6365677861610137895">"Menedżer"</string>
<string name="relationTypeMother" msgid="4578571352962758304">"Matka"</string>
<string name="relationTypeParent" msgid="4755635567562925226">"Rodzic"</string>
<string name="relationTypePartner" msgid="7266490285120262781">"Partner"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Działania na tekście"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kończy się miejsce"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektóre funkcje systemu mogą nie działać"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> uruchomiona"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest uruchomiona"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest uruchomiona"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Kliknij, aby uzyskać więcej informacji lub zatrzymać aplikację."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Anuluj"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Ułatwienia dostępu"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapeta"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Zmień tapetę"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Odbiornik powiadomień"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktywny"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Obsługa sieci VPN została włączona przez aplikację <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotknij, aby zarządzać siecią."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Przejdź do strony głównej"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Przejdź wyżej"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Więcej opcji"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Pamięć wewnętrzna"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Karta SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Nośnik USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Błąd"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ta aplikacja nie obsługuje kont użytkowników z ograniczeniami"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nie znaleziono aplikacji do obsługi tej akcji"</string>
+ <string name="revoke" msgid="5404479185228271586">"Cofnij"</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5eba00c..66d50e6 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que a aplicação modifique o modo como a utilização da rede é contabilizada em relação a aplicações. Nunca é necessário para aplicações normais."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"aceder às notificações"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que a aplicação obtenha, examine e limpe notificações, incluindo as que foram publicadas por outras aplicações."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincular a um serviço de escuta de notificações"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite que o titular vincule a interface de nível superior de um serviço de escuta de notificações. Nunca deverá ser necessário para aplicações normais."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Definir regras de palavra-passe"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar o comprimento e os caracteres permitidos nas palavras-passe de desbloqueio do ecrã."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitorizar tentativas de desbloqueio do ecrã"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Acções de texto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Está quase sem espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema poderão não funcionar"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> em execução"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> está a ser executado"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> em execução"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Toque para obter mais informações ou para parar a aplicação."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Acessibilidade"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Imagem de fundo"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Alterar imagem de fundo"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Serviço de escuta de notificações"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ativada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"A VPN foi ativada pelo <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toque para gerir a rede."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Navegar para página inicial"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Navegar para cima"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"memória de armazenamento interno"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Cartão SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Armazenamento USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Erro"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Esta aplicação não suporta contas de utilizadores limitados"</string>
<string name="app_not_found" msgid="3429141853498927379">"Não foram encontradas aplicações para executar esta ação"</string>
+ <string name="revoke" msgid="5404479185228271586">"Revogar"</string>
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 6b0d18a..3877158 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que o aplicativo modifique como o uso da rede é contabilizado em relação aos aplicativos. Não deve ser usado em aplicativos normais."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"acessar notificações"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que o aplicativo recupere, examine e limpe notificações, inclusive as postadas por outros aplicativos."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"sujeitar a um serviço ouvinte de notificações"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite que o proprietário sujeite a interface de nível superior a um serviço ouvinte de notificações. Não deve ser necessário para aplicativos comuns."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Definir regras para senha"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Controle o tamanho e os caracteres permitidos nas senhas de desbloqueio de tela."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitorar tentativas de desbloqueio da tela"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Ações de texto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> em execução"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> está em execução"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> está em execução"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Toque para mais informações ou para parar o aplicativo."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Acessibilidade"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Plano de fundo"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Alterar plano de fundo"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Ouvinte de notificações"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ativada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"A VPN está ativada por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toque para gerenciar a rede."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Navegar na página inicial"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Navegar para cima"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Armazenamento interno"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Cartão SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Armazenamento USB"</string>
@@ -1415,7 +1420,7 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefone"</string>
<string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Fones de ouvido"</string>
- <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Alto-falantes do dock"</string>
+ <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Alto-falantes da dock"</string>
<string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Áudio Bluetooth"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Erro"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"O aplicativo não suporta contas para usuários limitados"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nenhum aplicativo encontrado para executar a ação"</string>
+ <string name="revoke" msgid="5404479185228271586">"Revogar"</string>
</resources>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index 99fd59d..ba863cb 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1040,6 +1040,10 @@
<skip />
<!-- no translation found for permdesc_accessNotifications (458457742683431387) -->
<skip />
+ <!-- no translation found for permlab_bindNotificationListenerService (7057764742211656654) -->
+ <skip />
+ <!-- no translation found for permdesc_bindNotificationListenerService (985697918576902986) -->
+ <skip />
<!-- no translation found for policylab_limitPassword (4497420728857585791) -->
<skip />
<!-- no translation found for policydesc_limitPassword (3252114203919510394) -->
@@ -1634,9 +1638,9 @@
<skip />
<!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
<skip />
- <!-- no translation found for app_running_notification_title (4625479411505090209) -->
+ <!-- no translation found for app_running_notification_title (8718335121060787914) -->
<skip />
- <!-- no translation found for app_running_notification_text (3368349329989620597) -->
+ <!-- no translation found for app_running_notification_text (4653586947747330058) -->
<skip />
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Interrumper"</string>
@@ -1989,6 +1993,8 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Agids d\'access"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fund davos"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Midar il fund davos"</string>
+ <!-- no translation found for notification_listener_binding_label (2014162835481906429) -->
+ <skip />
<!-- no translation found for vpn_title (19615213552042827) -->
<skip />
<!-- no translation found for vpn_title_long (6400714798049252294) -->
@@ -2178,6 +2184,10 @@
<skip />
<!-- no translation found for action_menu_overflow_description (2295659037509008453) -->
<skip />
+ <!-- no translation found for action_bar_home_description_format (7965984360903693903) -->
+ <skip />
+ <!-- no translation found for action_bar_home_subtitle_description_format (6985546530471780727) -->
+ <skip />
<!-- no translation found for storage_internal (4891916833657929263) -->
<skip />
<!-- no translation found for storage_sd_card (3282948861378286745) -->
@@ -2397,4 +2407,6 @@
<skip />
<!-- no translation found for app_not_found (3429141853498927379) -->
<skip />
+ <!-- no translation found for revoke (5404479185228271586) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index b11cc2f..21fcf04 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite aplicaţiei să modifice modul în care este calculată utilizarea reţelei pentru aplicaţii. Nu se utilizează de aplicaţiile obişnuite."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"accesare notificări"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite aplicației să recupereze, să examineze și să șteargă notificări, inclusiv pe cele postate de alte aplicații."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"conectare la un serviciu de citire a notificărilor"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite proprietarului să se conecteze la interfața de nivel superior a unui serviciu de citire a notificărilor. Nu ar trebui să fie niciodată necesară pentru aplicațiile obișnuite."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Setaţi reguli pentru parolă"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Stabiliţi lungimea şi tipul de caractere permise în parolele pentru deblocarea ecranului."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitorizaţi încercările de deblocare a ecranului"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Acţiuni pentru text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spaţiul de stocare aproape ocupat"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Este posibil ca unele funcţii de sistem să nu funcţioneze"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> rulează"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> rulează acum"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> rulează acum"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Atingeți pentru mai multe informații sau pentru a opri aplicația."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Anulaţi"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Accesibilitate"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Imagine de fundal"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Modificaţi imaginea de fundal"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Serviciu de citire a notificărilor"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activat"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN este activată de <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Atingeţi pentru a gestiona reţeaua."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Navigaţi la ecranul de pornire"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Navigaţi în sus"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mai multe opţiuni"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Stocare internă"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Card SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Dsipozitiv de stocare USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Eroare"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Această aplicație nu acceptă conturile pentru utilizatori cu permisiuni limitate"</string>
<string name="app_not_found" msgid="3429141853498927379">"Nicio aplicație pentru gestionarea acestei acțiuni"</string>
+ <string name="revoke" msgid="5404479185228271586">"Revocați"</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 3da7211..592271b 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Приложение сможет изменять порядок расчета использования сетевых ресурсов различными программами. Это разрешение не используется обычными приложениями."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"доступ к уведомлениям"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Приложение сможет получать, проверять и удалять уведомления, включая те, что опубликованы другими приложениями."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"подключить к службе просмотра уведомлений"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Приложение сможет подключаться к базовому интерфейсу службы просмотра уведомлений. Это разрешение не используется обычными приложениями."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Правила выбора паролей"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролировать длину и символы при вводе паролей для снятия блокировки экрана."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Отслеживать попытки снятия блокировки экрана"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Операции с текстом"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Заканчивается свободное место"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некоторые системные функции могут не работать"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> выполняется"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> выполняется в данный момент"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" выполняется"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Нажмите, чтобы получить дополнительные данные или выключить приложение."</string>
<string name="ok" msgid="5970060430562524910">"ОК"</string>
<string name="cancel" msgid="6442560571259935130">"Отмена"</string>
<string name="yes" msgid="5362982303337969312">"ОК"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Спец. возможности"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Фоновый рисунок"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Сменить обои"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Служба просмотра уведомлений"</string>
<string name="vpn_title" msgid="19615213552042827">"Сеть VPN активна"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Сеть VPN активирована приложением <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Нажмите, чтобы открыть настройки."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Перейти на главную"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Перейти вверх"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Ещё"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Внутренняя память"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-карта"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB-накопитель"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Ошибка"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Приложение не поддерживает аккаунты с ограниченным доступом"</string>
<string name="app_not_found" msgid="3429141853498927379">"Невозможно обработать это действие"</string>
+ <string name="revoke" msgid="5404479185228271586">"Отменить"</string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 9ffe30d..228a9e7 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Umožňuje aplikácii upraviť používanie siete jednotlivými aplikáciami. Bežné aplikácie toto nastavenie nepoužívajú."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"prístup k upozorneniam"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Umožňuje aplikácii načítať, zobrazovať a mazať upozornenia vrátane tých, ktoré boli uverejnené inými aplikáciami."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"naviazanie sa na službu na počúvanie upozornení"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Umožňuje držiteľovi naviazať sa na najvyššiu úroveň služby na počúvanie upozornení. Bežné aplikácie by toto nastavenie nemali nikdy požadovať."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Nastaviť pravidlá pre heslo"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Ovládanie dĺžky hesiel na odomknutie obrazovky a v nich používané znaky."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Sledovať pokusy o odomknutie obrazovky"</string>
@@ -644,7 +646,7 @@
<string name="policylab_disableKeyguardFeatures" msgid="266329104542638802">"Zákaz funkcie v zámke kláves."</string>
<string name="policydesc_disableKeyguardFeatures" msgid="3467082272186534614">"Zabrániť používaniu niektorých funkcií v zámke klávesov."</string>
<string-array name="phoneTypes">
- <item msgid="8901098336658710359">"Domovská stránka"</item>
+ <item msgid="8901098336658710359">"Domov"</item>
<item msgid="869923650527136615">"Mobil"</item>
<item msgid="7897544654242874543">"Práca"</item>
<item msgid="1103601433382158155">"Fax do práce"</item>
@@ -654,19 +656,19 @@
<item msgid="9192514806975898961">"Vlastné"</item>
</string-array>
<string-array name="emailAddressTypes">
- <item msgid="8073994352956129127">"Domovská stránka"</item>
+ <item msgid="8073994352956129127">"Domov"</item>
<item msgid="7084237356602625604">"Práca"</item>
<item msgid="1112044410659011023">"Iné"</item>
<item msgid="2374913952870110618">"Vlastné"</item>
</string-array>
<string-array name="postalAddressTypes">
- <item msgid="6880257626740047286">"Domovská stránka"</item>
+ <item msgid="6880257626740047286">"Domov"</item>
<item msgid="5629153956045109251">"Práca"</item>
<item msgid="4966604264500343469">"Iné"</item>
<item msgid="4932682847595299369">"Vlastné"</item>
</string-array>
<string-array name="imAddressTypes">
- <item msgid="1738585194601476694">"Domovská stránka"</item>
+ <item msgid="1738585194601476694">"Domov"</item>
<item msgid="1359644565647383708">"Práca"</item>
<item msgid="7868549401053615677">"Iné"</item>
<item msgid="3145118944639869809">"Vlastné"</item>
@@ -687,7 +689,7 @@
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
<string name="phoneTypeCustom" msgid="1644738059053355820">"Vlastné"</string>
- <string name="phoneTypeHome" msgid="2570923463033985887">"Domovská stránka"</string>
+ <string name="phoneTypeHome" msgid="2570923463033985887">"Domov"</string>
<string name="phoneTypeMobile" msgid="6501463557754751037">"Mobil"</string>
<string name="phoneTypeWork" msgid="8863939667059911633">"Práca"</string>
<string name="phoneTypeFaxWork" msgid="3517792160008890912">"Fax do práce"</string>
@@ -712,16 +714,16 @@
<string name="eventTypeAnniversary" msgid="3876779744518284000">"Výročie"</string>
<string name="eventTypeOther" msgid="7388178939010143077">"Iné"</string>
<string name="emailTypeCustom" msgid="8525960257804213846">"Vlastné"</string>
- <string name="emailTypeHome" msgid="449227236140433919">"Domovská stránka"</string>
+ <string name="emailTypeHome" msgid="449227236140433919">"Domov"</string>
<string name="emailTypeWork" msgid="3548058059601149973">"Práca"</string>
<string name="emailTypeOther" msgid="2923008695272639549">"Iné"</string>
<string name="emailTypeMobile" msgid="119919005321166205">"Mobil"</string>
<string name="postalTypeCustom" msgid="8903206903060479902">"Vlastné"</string>
- <string name="postalTypeHome" msgid="8165756977184483097">"Domovská stránka"</string>
+ <string name="postalTypeHome" msgid="8165756977184483097">"Domov"</string>
<string name="postalTypeWork" msgid="5268172772387694495">"Práca"</string>
<string name="postalTypeOther" msgid="2726111966623584341">"Iné"</string>
<string name="imTypeCustom" msgid="2074028755527826046">"Vlastné"</string>
- <string name="imTypeHome" msgid="6241181032954263892">"Domovská stránka"</string>
+ <string name="imTypeHome" msgid="6241181032954263892">"Domov"</string>
<string name="imTypeWork" msgid="1371489290242433090">"Práca"</string>
<string name="imTypeOther" msgid="5377007495735915478">"Iné"</string>
<string name="imProtocolCustom" msgid="6919453836618749992">"Vlastné"</string>
@@ -753,7 +755,7 @@
<string name="relationTypeSister" msgid="1735983554479076481">"Sestra"</string>
<string name="relationTypeSpouse" msgid="394136939428698117">"Manžel(-ka)"</string>
<string name="sipAddressTypeCustom" msgid="2473580593111590945">"Vlastné"</string>
- <string name="sipAddressTypeHome" msgid="6093598181069359295">"Domovská stránka"</string>
+ <string name="sipAddressTypeHome" msgid="6093598181069359295">"Domov"</string>
<string name="sipAddressTypeWork" msgid="6920725730797099047">"Práca"</string>
<string name="sipAddressTypeOther" msgid="4408436162950119849">"Iné"</string>
<string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Zadajte kód PIN"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Operácie s textom"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nedostatok ukladacieho priestoru"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektoré systémové funkcie nemusia fungovať"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je spustená"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je práve spustená"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je spustená"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Dotknutím sa zobrazíte viac informácií alebo zastavíte aplikáciu."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Zrušiť"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Zjednodušenie"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapeta"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Zmeniť tapetu"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Aplikácia na počúvanie upozornení"</string>
<string name="vpn_title" msgid="19615213552042827">"Sieť VPN je aktivovaná"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikáciu <xliff:g id="APP">%s</xliff:g> aktivovala sieť VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotykom môžete spravovať sieť."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Prejsť na plochu"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Prejsť na"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Viac možností"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Interné úložisko"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Karta SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Ukladací priestor USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Chyba"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Táto aplikácia nepodporuje účty v prípade používateľov s obmedzením"</string>
<string name="app_not_found" msgid="3429141853498927379">"Aplikácia potrebná na spracovanie tejto akcie sa nenašla"</string>
+ <string name="revoke" msgid="5404479185228271586">"Odvolať"</string>
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index ac1b6ad..906c197 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Programu omogoča, da spremeni uporabo omrežja na podlagi programov. Ni za uporabo z navadnimi programi."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"dostop do obvestil"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Dovoli aplikaciji, da prenese, razišče in izbriše obvestila, tudi tista, ki so jih objavile druge aplikacije."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"poveži se s storitvijo poslušalca obvestil"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Lastniku omogoča povezovanje z vmesnikom storitve poslušalca obvestil najvišje ravni. Tega nikoli ni treba uporabiti za navadne aplikacije."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Nastavitev pravil za geslo"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Nadzor nad dolžino in znaki, ki so dovoljeni v geslih za odklepanje zaslona."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"nadzor nad poskusi odklepanja zaslona"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Besedilna dejanja"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Prostor za shranjevanje bo pošel"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nekatere sistemske funkcije morda ne delujejo"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Izvaja se aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Trenutno se izvaja aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> se izvaja"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Dotaknite se, če želite izvedeti več ali ustaviti aplikacijo."</string>
<string name="ok" msgid="5970060430562524910">"V redu"</string>
<string name="cancel" msgid="6442560571259935130">"Prekliči"</string>
<string name="yes" msgid="5362982303337969312">"V redu"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Pripomočki za osebe s posebnimi potrebami"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Ozadje"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Spreminjanje ozadja"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Poslušalec obvestil"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktiviran"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN je aktiviral program <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotaknite se, če želite upravljati omrežje."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Krmarjenje domov"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Krmarjenje navzgor"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Več možnosti"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Notranji pomnilnik"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kartica SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Pomnilnik USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Napaka"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ta aplikacija ne podpira računov za uporabnike z omejitvami"</string>
<string name="app_not_found" msgid="3429141853498927379">"Najdena ni bila nobena aplikacija za izvedbo tega dejanja"</string>
+ <string name="revoke" msgid="5404479185228271586">"Prekliči"</string>
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 7e8cf01..102ced6 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Дозвољава апликацији да измени начин на који апликације користе мрежу. Не користе је уобичајене апликације."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"приступ обавештењима"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Дозвољава апликацији да преузима, испитује и брише обавештења, укључујући она која постављају друге апликације."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"повезивање са услугом монитора обавештења"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Дозвољава власнику да се повеже са интерфејсом услуге монитора обавештења највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Подешавање правила за лозинку"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролишите дужину и знакове дозвољене у лозинкама за откључавање екрана."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Надгледање покушаја откључавања екрана"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Радње у вези са текстом"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Простор за складиштење је на измаку"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Неке системске функције можда не функционишу"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> је покренута"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> је тренутно покренута"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је покренута"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Додирните за више информација или заустављање апликације."</string>
<string name="ok" msgid="5970060430562524910">"Потврди"</string>
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
<string name="yes" msgid="5362982303337969312">"Потврди"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Приступачност"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Позадина"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Промена позадине"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Монитор обавештења"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN је активиран"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Апликација <xliff:g id="APP">%s</xliff:g> је активирала VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Додирните да бисте управљали мрежом."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Кретање до Почетне"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Кретање нагоре"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Још опција"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Интерна меморија"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD картица"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB меморија"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Грешка"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ова апликација не подржава налоге за кориснике са ограничењем"</string>
<string name="app_not_found" msgid="3429141853498927379">"Није пронађена ниједна апликација која би могла да обави ову радњу"</string>
+ <string name="revoke" msgid="5404479185228271586">"Опозови"</string>
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 5a54f71..adeffc3 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Tillåter att appen ändrar hur nätverksanvändning redovisas för appar. Används inte av vanliga appar."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"få åtkomst till meddelanden"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Tillåter att appen hämtar, granskar och raderar meddelanden, även sådana som skickats av andra appar."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"binda till en meddelandelyssnare"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en meddelandelyssnare. Ska inte behövas för vanliga appar."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Ange lösenordsregler"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Bestäm hur många och vilka tecken som är tillåtna i skärmlåsets lösenord."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Övervaka försök att låsa upp skärmen"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Textåtgärder"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lagringsutrymmet börjar ta slut"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Det kan hända att vissa systemfunktioner inte fungerar"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> körs"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> körs"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> körs"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Tryck om du vill veta mer eller stoppa appen."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Tillgänglighet"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Bakgrund"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Ändra bakgrund"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Meddelandelyssnare"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN är aktiverat"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN aktiveras av <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tryck om du vill hantera nätverket."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Visa startsidan"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Navigera uppåt"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Fler alternativ"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Internminne"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kort"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB-lagring"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Fel"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Appen har inte stöd för användarkonton med begränsningar"</string>
<string name="app_not_found" msgid="3429141853498927379">"Ingen app som kan hantera åtgärden hittades"</string>
+ <string name="revoke" msgid="5404479185228271586">"Återkalla"</string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 96b013f..bdc0155 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Huruhusu programu kurekebisha jinsi matumizi ya mtandao yana hesabika dhidi ya programu. Sio ya matumizi na programu za kawaida."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"fikia arifa"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Huruhusu programu kurejesha, kuchunguza, na kuondoa arifa, ikiwa ni pamoja na zile zilizochapishwa na programu nyingine."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"unganisha kwenye huduma ya kisikilizi cha arifa"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Inaruhusu kishikilizi kuunganishwa kwenye kusano cha kiwango cha juu cha huduma ya kisikilizi cha arifa. Haipaswi kuhitajika tena kwa programu za kawaida."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Weka kanuni za nenosiri"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Dhibiti urefu na vibambo vinavyoruhusiwa katika manenosiri ya kufungua skrini."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Chunguza majaribio ya kutofun gua skrini"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Vitendo vya maandishi"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nafasi ya kuhafadhi inakwisha"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaendeshwa"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaendeshwa kwa sasa"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaendesha"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Gusa ili upate maelezo zaidi au usitishe programu."</string>
<string name="ok" msgid="5970060430562524910">"Sawa"</string>
<string name="cancel" msgid="6442560571259935130">"Ghairi"</string>
<string name="yes" msgid="5362982303337969312">"Sawa"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Ufikiaji"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Mandhari"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Badilisha mandhari"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Kisikilizi cha arifa"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN imewezeshwa"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN imeamilishwa na <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Gusa ili kudhibiti mtandao."</string>
@@ -1281,7 +1284,7 @@
<string name="submit" msgid="1602335572089911941">"Wasilisha"</string>
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Mtindo wa gari umewezeshwa"</string>
<string name="car_mode_disable_notification_message" msgid="8035230537563503262">"Gusa ili kutoka katika modi ya gari."</string>
- <string name="tethered_notification_title" msgid="3146694234398202601">"Amilisha uzuiaji au mahali maalum"</string>
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Kushiriki au kusambaza intaneti kumewashwa"</string>
<string name="tethered_notification_message" msgid="6857031760103062982">"Gusa ili kusanidi."</string>
<string name="back_button_label" msgid="2300470004503343439">"Nyuma"</string>
<string name="next_button_label" msgid="1080555104677992408">"Ifuatayo"</string>
@@ -1350,7 +1353,7 @@
<string name="keyboardview_keycode_done" msgid="1992571118466679775">"Imefanyika"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Modi ya mabadiliko"</string>
<string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Songa"</string>
- <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Ingiza"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
<string name="activitychooserview_choose_application" msgid="2125168057199941199">"Chagua programu"</string>
<string name="shareactionprovider_share_with" msgid="806688056141131819">"Shiriki na"</string>
<string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Shiriki na <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Abiri nyumbani"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Ongoza"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Chaguo zaidi"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Hifadhi ya mfumo"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kadi ya SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Hifadhi ya USB"</string>
@@ -1476,10 +1481,8 @@
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Ufikivu umeghairiwa."</string>
<string name="user_switched" msgid="3768006783166984410">"Mtumiaji wa sasa <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Mmiliki"</string>
- <!-- no translation found for error_message_title (4510373083082500195) -->
- <skip />
- <!-- no translation found for app_no_restricted_accounts (5322164210667258876) -->
- <skip />
- <!-- no translation found for app_not_found (3429141853498927379) -->
- <skip />
+ <string name="error_message_title" msgid="4510373083082500195">"Hitilafu"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Programu hii haiwezi kutumiwa na akaunti za watumiaji waliowekewa vizuizi"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"Hakuna programu iliyopatikana ili kushughulikia kitendo hiki"</string>
+ <string name="revoke" msgid="5404479185228271586">"Batilisha"</string>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 43fdeac..a92db72 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -154,9 +154,9 @@
<string name="global_actions" product="default" msgid="2406416831541615258">"ตัวเลือกโทรศัพท์"</string>
<string name="global_action_lock" msgid="2844945191792119712">"ล็อกหน้าจอ"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"ปิดเครื่อง"</string>
- <string name="global_action_bug_report" msgid="7934010578922304799">"รายงานข้อบกพร่อง"</string>
- <string name="bugreport_title" msgid="2667494803742548533">"ใช้รายงานข้อบกพร่อง"</string>
- <string name="bugreport_message" msgid="398447048750350456">"การดำเนินการนี้จะรวบรวมข้อมูลเกี่ยวกับสถานะปัจจุบันของอุปกรณ์ของคุณ โดยจะส่งไปในรูปแบบข้อความอีเมล อาจใช้เวลาสักครู่ตั้งแต่เริ่มการสร้างรายงานข้อบกพร่องจนกระทั่งเสร็จสมบูรณ์ โปรดอดทนรอ"</string>
+ <string name="global_action_bug_report" msgid="7934010578922304799">"รายงานบั๊ก"</string>
+ <string name="bugreport_title" msgid="2667494803742548533">"ใช้รายงานบั๊ก"</string>
+ <string name="bugreport_message" msgid="398447048750350456">"การดำเนินการนี้จะรวบรวมข้อมูลเกี่ยวกับสถานะปัจจุบันของอุปกรณ์ของคุณ โดยจะส่งไปในรูปแบบข้อความอีเมล อาจใช้เวลาสักครู่ตั้งแต่เริ่มการสร้างรายงานบั๊กจนกระทั่งเสร็จสมบูรณ์ โปรดอดทนรอ"</string>
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"โหมดปิดเสียง"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"ปิดเสียงไว้"</string>
<string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"เปิดเสียงแล้ว"</string>
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"อนุญาตให้แอปพลิเคชันแก้ไขวิธีการบันทึกบัญชีการใช้งานเครือข่ายของแอปพลิเคชัน ไม่ใช้สำหรับแอปพลิเคชันทั่วไป"</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"เข้าถึงการแจ้งเตือน"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"ทำให้แอปสามารถเรียกดู ตรวจสอบ และล้างการแจ้งเตือนได้ ซึ่งรวมถึงการแจ้งเตือนที่โพสต์โดยแอปอื่นๆ ด้วย"</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"เชื่อมโยงกับบริการตัวฟังการแจ้งเตือน"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"อนุญาตให้เจ้าของเชื่อมโยงกับอินเตอร์เฟซระดับสูงสุดของบริการตัวฟังการแจ้งเตือน ซึ่งไม่มีความจำเป็นสำหรับแอปธรรมดา"</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"ตั้งค่ากฎรหัสผ่าน"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"ควบคุมความยาวและอักขระที่อนุญาตให้ใช้ในรหัสผ่านการปลดล็อกหน้าจอ"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"ตรวจสอบความพยายามในการปลดล็อกหน้าจอ"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"การทำงานของข้อความ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"พื้นที่จัดเก็บเหลือน้อย"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังทำงาน"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังทำงานในขณะนี้"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังทำงาน"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"แตะเพื่อดูข้อมูลเพิ่มเติมหรือเพื่อหยุดแอป"</string>
<string name="ok" msgid="5970060430562524910">"ตกลง"</string>
<string name="cancel" msgid="6442560571259935130">"ยกเลิก"</string>
<string name="yes" msgid="5362982303337969312">"ตกลง"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"การเข้าถึง"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"วอลเปเปอร์"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"เปลี่ยนวอลเปเปอร์"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"ตัวฟังการแจ้งเตือน"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN เปิดใช้งานแล้ว"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"เปิดใช้งาน VPN โดย <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"แตะเพื่อจัดการเครือข่าย"</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"นำทางไปหน้าแรก"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"นำทางขึ้น"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ตัวเลือกเพิ่มเติม"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"ที่จัดเก็บข้อมูลภายใน"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"การ์ด SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"ที่เก็บข้อมูล USB"</string>
@@ -1476,10 +1481,8 @@
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"ยกเลิกการเข้าถึงแล้ว"</string>
<string name="user_switched" msgid="3768006783166984410">"ผู้ใช้ปัจจุบัน <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="owner_name" msgid="2716755460376028154">"เจ้าของ"</string>
- <!-- no translation found for error_message_title (4510373083082500195) -->
- <skip />
- <!-- no translation found for app_no_restricted_accounts (5322164210667258876) -->
- <skip />
- <!-- no translation found for app_not_found (3429141853498927379) -->
- <skip />
+ <string name="error_message_title" msgid="4510373083082500195">"ข้อผิดพลาด"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"แอปพลิเคชันนี้ไม่สนับสนุนบัญชีผู้ใช้ที่ถูกจำกัด"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"ไม่พบแอปพลิเคชันสำหรับการทำงานนี้"</string>
+ <string name="revoke" msgid="5404479185228271586">"เพิกถอน"</string>
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index beeb6c0..e726333 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Pinapayagan ang app na baguhin kung paano isinasaalang-alang ang paggamit ng network laban sa apps. Hindi para sa paggamit ng normal na apps."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"i-access ang mga notification"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Pinapayagan ang app na kumuha, sumuri, at mag-clear ng mga notification, kabilang ang mga na-post ng iba pang apps."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"mapailalim sa isang serbisyo ng notification listener"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Nagbibigay-daan sa may-ari na mapailalim sa interface sa tuktok na antas ng isang serbisyo ng notification listener. Hindi dapat kailanganin para sa karaniwang apps kahit kailan."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Magtakda ng mga panuntunan sa password"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrolin ang haba at mga character na pinapayagan sa mga password sa pag-unlock ng screen."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Subaybayan ang mga pagsubok sa pag-unlock ng screen"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Pagkilos ng teksto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nauubusan na ang puwang ng storage"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Maaaring hindi gumana nang tama ang ilang paggana ng system"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"Tumatakbo ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"Kasalukuyang tumatakbo ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"Tumatakbo ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Pindutin para sa higit pang impormasyon o upang ihinto ang app."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Kanselahin"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Kakayahang Ma-access"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Baguhin ang wallpaper"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Notification listener"</string>
<string name="vpn_title" msgid="19615213552042827">"Naka-activate ang VPN"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Isinaaktibo ang VPN ng <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Pindutin upang pamahalaan ang network."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Magnabiga sa home"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Magnabiga pataas"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Higit pang mga pagpipilian"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Panloob na storage"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD card"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB storage"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Error"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Hindi sinusuportahan ng application na ito ang mga account para sa mga limitadong user"</string>
<string name="app_not_found" msgid="3429141853498927379">"Walang nakitang application na mangangasiwa sa pagkilos na ito"</string>
+ <string name="revoke" msgid="5404479185228271586">"Bawiin"</string>
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 2fddf7d..9e5e43b 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Uygulamaya, ağın uygulamalara göre nasıl kullanılacağını değiştirme izni verir. Normal uygulamalar tarafından kullanılmak için değildir."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"bildirimlere eriş"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Uygulamanın bildirimler almasına, bildirimleri incelemesine ve temizlemesine izin verir. Buna diğer uygulamalar tarafından yayınlanan bildirimler de dahildir."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bildirim dinleyici hizmetine bağlan"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"İzin sahibine bir bildirim dinleyici hizmetinin en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Şifre kuralları ayarla"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Ekran kilidini açma şifrelerinde izin verilen uzunluğu ve karakterleri denetleme."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Ekran kilidini açma denemelerini izle"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Metin eylemleri"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Depolama alanı bitiyor"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bazı sistem işlevleri çalışmayabilir"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> çalışıyor"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> şu anda çalışıyor"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> çalışıyor"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Daha fazla bilgi edinmek için veya uygulamayı durdurmak için dokunun."</string>
<string name="ok" msgid="5970060430562524910">"Tamam"</string>
<string name="cancel" msgid="6442560571259935130">"İptal"</string>
<string name="yes" msgid="5362982303337969312">"Tamam"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Erişebilirlik"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Duvar Kağıdı"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Duvar kağıdını değiştir"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Bildirim dinleyici"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN etkinleştirildi"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN, <xliff:g id="APP">%s</xliff:g> tarafından etkinleştirildi"</string>
<string name="vpn_text" msgid="3011306607126450322">"Ağı yönetmek için dokunun."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Ana sayfaya git"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Yukarı git"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Diğer seçenekler"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Dahili depolama birimi"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kart"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB bellek"</string>
@@ -1476,10 +1481,8 @@
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Erişilebilirlik iptal edildi."</string>
<string name="user_switched" msgid="3768006783166984410">"Geçerli kullanıcı: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Sahibi"</string>
- <!-- no translation found for error_message_title (4510373083082500195) -->
- <skip />
- <!-- no translation found for app_no_restricted_accounts (5322164210667258876) -->
- <skip />
- <!-- no translation found for app_not_found (3429141853498927379) -->
- <skip />
+ <string name="error_message_title" msgid="4510373083082500195">"Hata"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Bu uygulama, kısıtlı kullanıcı hesaplarını desteklemiyor"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"Bu eylemi gerçekleştirecek bir uygulama bulunamadı"</string>
+ <string name="revoke" msgid="5404479185228271586">"İptal et"</string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 6f505b1..bb223ea 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Дозволяє програмі змінювати метод підрахунку того, як програми використовують мережу. Не для використання звичайними програмами."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"отримувати доступ до сповіщень"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Дозволяє програмі отримувати, перевіряти й очищати сповіщення, зокрема опубліковані іншими програмами."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"прив’язуватися до служби читання сповіщень"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Дозволяє власнику прив’язуватися до інтерфейсу верхнього рівня служби читання сповіщень. Ніколи не застосовується для звичайних програм."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Устан. правила пароля"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролювати довжину паролів для розблокування екрана та дозволені в них символи."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Відстежув. спроби розблок. екрана"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Дії з текстом"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Закінчується пам’ять"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Деякі системні функції можуть не працювати"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> працює"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> зараз працює"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> працює"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Торкніться, щоб дізнатися більше або зупинити програму."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Доступність"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Фоновий мал."</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Змінити фоновий малюнок"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Служба читання сповіщень"</string>
<string name="vpn_title" msgid="19615213552042827">"Мережу VPN активовано"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Мережу VPN активовано програмою <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Торкніться, щоб керувати мережею."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Перейти на головну"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Перейти вгору"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Інші варіанти"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Внутрішня пам’ять"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Карта SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Носій USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Помилка"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ця програма не підтримує облікові записи для обмежених користувачів"</string>
<string name="app_not_found" msgid="3429141853498927379">"Не знайдено програму для обробки цієї дії"</string>
+ <string name="revoke" msgid="5404479185228271586">"Анулювати"</string>
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 4fe8d65..a592144 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Cho phép ứng dụng sửa đổi cách tính mức sử dụng mạng so với ứng dụng. Không dành cho các ứng dụng thông thường."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"truy cập thông báo"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Cho phép ứng dụng truy xuất, kiểm tra và xóa thông báo, bao gồm những thông báo được đăng bởi các ứng dụng khác."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"liên kết với dịch vụ trình xử lý thông báo"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của dịch vụ trình xử lý thông báo. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Đặt quy tắc mật khẩu"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kiểm soát độ dài và ký tự được phép trong mật khẩu mở khóa màn hình."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Giám sát những lần thử mở khóa màn hình"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tác vụ văn bản"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Sắp hết dung lượng lưu trữ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Một số chức năng hệ thống có thể không hoạt động"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang chạy"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> hiện đang chạy"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang chạy"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Chạm để xem thêm thông tin hoặc dừng ứng dụng."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Hủy"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Khả năng truy cập"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Hình nền"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Thay đổi hình nền"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Trình xử lý thông báo"</string>
<string name="vpn_title" msgid="19615213552042827">"Đã kích hoạt VPN"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN được <xliff:g id="APP">%s</xliff:g> kích hoạt"</string>
<string name="vpn_text" msgid="3011306607126450322">"Chạm để quản lý mạng."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Điều hướng về trang chủ"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Điều hướng lên trên"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Tùy chọn khác"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Bộ nhớ trong"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Thẻ SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Bộ lưu trữ USB"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"Lỗi"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ứng dụng này không hỗ trợ tài khoản cho người dùng giới hạn"</string>
<string name="app_not_found" msgid="3429141853498927379">"Không tìm thấy ứng dụng nào để xử lý tác vụ này"</string>
+ <string name="revoke" msgid="5404479185228271586">"Thu hồi"</string>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index af1a2ea..79c79f3 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"允许该应用修改对于各应用的网络使用情况的统计方式。普通应用不应使用此权限。"</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"查看通知"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"允许该应用检索、检查并清除通知,包括其他应用发布的通知。"</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"绑定到通知侦听器服务"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"允许应用绑定到通知侦听器服务的顶级接口(普通应用绝不需要此权限)。"</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"设置密码规则"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"控制屏幕解锁密码所允许的长度和字符。"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"监视屏幕解锁尝试次数"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"文字操作"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"存储空间不足"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"某些系统功能可能无法正常使用"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g>正在运行"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g>目前正在运行"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正在运行"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"触摸即可了解详情或停止应用。"</string>
<string name="ok" msgid="5970060430562524910">"确定"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="yes" msgid="5362982303337969312">"确定"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"辅助功能"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"壁纸"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"更改壁纸"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"通知侦听器"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN 已激活"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"“<xliff:g id="APP">%s</xliff:g>”已激活 VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"触摸可管理网络。"</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"导航首页"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"向上导航"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"更多选项"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s:%2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s - %2$s:%3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"内存设备"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD 卡"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB 存储器"</string>
@@ -1479,4 +1484,5 @@
<string name="error_message_title" msgid="4510373083082500195">"错误"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"此应用不支持受限用户的帐户"</string>
<string name="app_not_found" msgid="3429141853498927379">"找不到可处理此操作的应用"</string>
+ <string name="revoke" msgid="5404479185228271586">"撤消"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index ed2551d..dfd91a4 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"允許應用程式修改應用程式網路使用量的計算方式 (不建議一般應用程式使用)。"</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"存取通知"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"允許應用程式擷取、檢查及清除通知 (包括由其他應用程式發佈的通知)。"</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"繫結至通知接聽器服務"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"允許應用程式繫結至通知接聽器服務的頂層介面 (一般應用程式不需使用)。"</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"設定密碼規則"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"控制螢幕解鎖密碼所允許的長度和字元。"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"監視螢幕解鎖嘗試次數"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"文字動作"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在執行"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」目前正在執行"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」目前正在執行"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"輕觸即可瞭解詳情或停止應用程式。"</string>
<string name="ok" msgid="5970060430562524910">"確定"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="yes" msgid="5362982303337969312">"確定"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"協助工具"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"桌布"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"變更桌布"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"通知接聽器"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN 已啟用"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> 已啟用 VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"輕觸即可管理網路。"</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"瀏覽首頁"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"向上瀏覽"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"更多選項"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s:%2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s - %2$s:%3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"內部儲存空間"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD 卡"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB 儲存裝置"</string>
@@ -1478,5 +1483,6 @@
<string name="owner_name" msgid="2716755460376028154">"擁有者"</string>
<string name="error_message_title" msgid="4510373083082500195">"錯誤"</string>
<string name="app_no_restricted_accounts" msgid="5322164210667258876">"這個應用程式不支援受限的使用者帳戶。"</string>
- <string name="app_not_found" msgid="3429141853498927379">"找不到可以處理這個動作的應用程式"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"找不到支援此操作的應用程式"</string>
+ <string name="revoke" msgid="5404479185228271586">"撤銷"</string>
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index a104729..41ac207 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -621,6 +621,8 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Ivumela insiza ukuthi iguqule ukuthii ukusetshenziswa kwenethiwekhi kumiswa kanjani ezinsizeni. Ayisetshenziswa izinsiza ezijwayelekile."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"finyelela kuzaziso"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Ivumela uhlelo lokusebenza ukuthi lithole, lihlole, liphinde lisuse izaziso, ezifaka lezo ezithunyelwe ezinye izinhlelo zokusebenza."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bophezela kwisevisi yomlaleli wesaziso"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ivumela umbambi ukubophezela kwisixhumi esibonakalayo sezinga eliphezulu lesevisi yomlaleli wesaziso. Akusoze kwadingeka kwizinhlelo zokusebenza ezivamile."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Misa imithetho yephasiwedi"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Lawula ubude nezinhlamvu ezivunyelwe kumaphasiwedi okuvula isikrini"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Gaka imizamo yokuvula isikrini"</string>
@@ -1048,8 +1050,8 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Izenzo zombhalo"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Isikhala sokulondoloza siyaphela"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string>
- <string name="app_running_notification_title" msgid="4625479411505090209">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> iyasebenza"</string>
- <string name="app_running_notification_text" msgid="3368349329989620597">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> isebenza manje"</string>
+ <string name="app_running_notification_title" msgid="8718335121060787914">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> iyasebenza"</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Thinta ukuthola ulwazi oluningi noma ukumisa uhlelo lokusebenza."</string>
<string name="ok" msgid="5970060430562524910">"KULUNGILE"</string>
<string name="cancel" msgid="6442560571259935130">"Khansela"</string>
<string name="yes" msgid="5362982303337969312">"KULUNGILE"</string>
@@ -1267,6 +1269,7 @@
<string name="accessibility_binding_label" msgid="4148120742096474641">"Ukufinyeleleka"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Iphephadonga"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Shintsha iphephadonga"</string>
+ <string name="notification_listener_binding_label" msgid="2014162835481906429">"Umlaleli wesaziso"</string>
<string name="vpn_title" msgid="19615213552042827">"I-VPN isiyasebenza"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"i-VPN ivuswe ngu <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Thinta ukuze wengamele inethiwekhi."</string>
@@ -1370,6 +1373,8 @@
<string name="action_bar_home_description" msgid="5293600496601490216">"Zulazulela ekhaya"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Zulazulela phezulu"</string>
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Izinketho ezingaphezulu"</string>
+ <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
+ <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="4891916833657929263">"Isitoreji sangaphakathi"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Ikhadi le-SD"</string>
<string name="storage_usb" msgid="3017954059538517278">"Isitoreji se-USB"</string>
@@ -1476,10 +1481,8 @@
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Ukufinyelela kukhanseliwe."</string>
<string name="user_switched" msgid="3768006783166984410">"Umsebenzisi wamanje <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="owner_name" msgid="2716755460376028154">"Umnikazi"</string>
- <!-- no translation found for error_message_title (4510373083082500195) -->
- <skip />
- <!-- no translation found for app_no_restricted_accounts (5322164210667258876) -->
- <skip />
- <!-- no translation found for app_not_found (3429141853498927379) -->
- <skip />
+ <string name="error_message_title" msgid="4510373083082500195">"Iphutha"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Lolu hlelo lokusebenza alusekeli ama-akhawunti wabasebenzisi abakhawulelwe"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"Alukho uhlelo lokusebenza olutholakele lokuphatha lesi senzo"</string>
+ <string name="revoke" msgid="5404479185228271586">"Chitha"</string>
</resources>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index f7ff77b..146607e 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -141,6 +141,10 @@
<item>@drawable/menu_submenu_background</item>
<item>@drawable/menu_dropdown_panel_holo_light</item>
<item>@drawable/menu_dropdown_panel_holo_dark</item>
+ <item>@drawable/menu_popup_panel_holo_light</item>
+ <item>@drawable/menu_popup_panel_holo_dark</item>
+ <item>@drawable/menu_panel_holo_light</item>
+ <item>@drawable/menu_panel_holo_dark</item>
<item>@drawable/overscroll_edge</item>
<item>@drawable/overscroll_glow</item>
<item>@drawable/spinner_16_outer_holo</item>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0afe4c1..f2c0aa0 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -570,7 +570,8 @@
this activity. -->
<enum name="behind" value="3" />
<!-- Orientation is determined by a physical orientation sensor:
- the display will rotate based on how the user moves the device. -->
+ the display will rotate based on how the user moves the device.
+ Ignores user's setting to turn off sensor-based rotation. -->
<enum name="sensor" value="4" />
<!-- Always ignore orientation determined by orientation sensor:
the display will not rotate when the user moves the device. -->
@@ -593,6 +594,22 @@
the device will normally do (for example some devices won't
normally use 180 degree rotation). -->
<enum name="fullSensor" value="10" />
+ <!-- Would like to have the screen in landscape orientation, but if
+ the user has enabled sensor-based rotation then we can use the
+ sensor to change which direction the screen is facing. -->
+ <enum name="userLandscape" value="11" />
+ <!-- Would like to have the screen in portrait orientation, but if
+ the user has enabled sensor-based rotation then we can use the
+ sensor to change which direction the screen is facing. -->
+ <enum name="userPortrait" value="12" />
+ <!-- Respect the user's sensor-based rotation preference, but if
+ sensor-based rotation is enabled then allow the screen to rotate
+ in all 4 possible directions regardless of what
+ the device will normally do (for example some devices won't
+ normally use 180 degree rotation). -->
+ <enum name="fullUser" value="13" />
+ <!-- Screen is locked to its current rotation, whatever that is. -->
+ <enum name="locked" value="14" />
</attr>
<!-- Specify one or more configuration changes that the activity will
@@ -892,8 +909,13 @@
<!-- Declare that this application requires access to restricted accounts of a certain
type. The default value is null and restricted accounts won\'t be visible to this
application. The type should correspond to the account authenticator type, such as
- "com.google" -->
+ "com.google". -->
<attr name="restrictedAccountType" format="string"/>
+ <!-- Declare that this application requires an account of a certain
+ type. The default value is null and indicates that the application can work without
+ any accounts. The type should correspond to the account authenticator type, such as
+ "com.google". -->
+ <attr name="requiredAccountType" format="string"/>
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
@@ -996,8 +1018,9 @@
permission, and it must always be granted when it is installed.
If you set this to false, then in some cases the application may
be installed with it being granted the permission, and it will
- need to request the permission later if it needs it. -->
+ need to request the permission later if it needs it.
<attr name="required" format="boolean" />
+ -->
</declare-styleable>
<!-- The <code>uses-configuration</code> tag specifies
@@ -1040,7 +1063,7 @@
don't support it. If you set this to false, then this will
not impose a restriction on where the application can be
installed. -->
- <attr name="required" />
+ <attr name="required" format="boolean" />
</declare-styleable>
<!-- The <code>uses-sdk</code> tag describes the SDK features that the
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 42d692f..074d91f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2042,6 +2042,7 @@
<public type="attr" name="childIndicatorStart" />
<public type="attr" name="childIndicatorEnd" />
<public type="attr" name="restrictedAccountType" />
+ <public type="attr" name="requiredAccountType" />
<public type="style" name="Theme.NoTitleBar.Overscan" />
<public type="style" name="Theme.Light.NoTitleBar.Overscan" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index aea0803..3361ab7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1816,6 +1816,11 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_accessNotifications">Allows the app to retrieve, examine, and clear notifications, including those posted by other apps.</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_bindNotificationListenerService">bind to a notification listener service</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_bindNotificationListenerService">Allows the holder to bind to the top-level interface of a notification listener service. Should never be needed for normal apps.</string>
+
<!-- Policy administration -->
<!-- Title of policy access to limiting the user's password choices -->
@@ -2997,11 +3002,11 @@
<!-- [CHAR LIMIT=NONE] Stub notification title for an app running a service that has provided
a bad bad notification for itself. -->
<string name="app_running_notification_title"><xliff:g id="app_name">%1$s</xliff:g>
- running</string>
+ is running</string>
<!-- [CHAR LIMIT=NONE] Stub notification text for an app running a service that has provided
a bad bad notification for itself. -->
- <string name="app_running_notification_text"><xliff:g id="app_name">%1$s</xliff:g>
- is currently running</string>
+ <string name="app_running_notification_text">Touch for more information
+ or to stop the app.</string>
<!-- Preference framework strings. -->
<string name="ok">OK</string>
@@ -3513,6 +3518,9 @@
<string name="wallpaper_binding_label">Wallpaper</string>
<!-- Dialog title for user to select a different wallpaper from service list -->
<string name="chooser_wallpaper">Change wallpaper</string>
+ <!-- Label to show for a service that is running because it is observing
+ the user's notifications. -->
+ <string name="notification_listener_binding_label">Notification listener</string>
<!-- Do Not Translate: Alternate eri.xml -->
<string name="alternate_eri_file">/data/eri.xml</string>
@@ -3768,6 +3776,17 @@
<string name="action_bar_up_description">Navigate up</string>
<!-- Content description for the action menu overflow button. [CHAR LIMIT=NONE] -->
<string name="action_menu_overflow_description">More options</string>
+ <!-- Formatting string for describing the action bar's title/home/up affordance.
+ This is a single tappable "button" that includes the app icon, the Up indicator
+ (usually a "<" chevron) and the window title text.
+ %1$s is the title. %2$s is the description of what tapping/clicking the whole
+ thing is going to do. -->
+ <string name="action_bar_home_description_format">%1$s, %2$s</string>
+ <!-- Just like action_bar_home_description_format, but this one will be used
+ if the window is also providing subtitle text.
+ %1$s is the title. %2$s is the subtitle. %3$s is the description of what
+ tapping/clicking the whole thing is going to do. -->
+ <string name="action_bar_home_subtitle_description_format">%1$s, %2$s, %3$s</string>
<!-- Storage description for internal storage. [CHAR LIMIT=NONE] -->
<string name="storage_internal">Internal storage</string>
@@ -4074,4 +4093,5 @@
<string name="app_no_restricted_accounts">This application does not support accounts for limited users</string>
<!-- Message informing user that the requested activity could not be found [CHAR LIMIT=none] -->
<string name="app_not_found">No application found to handle this action</string>
+ <string name="revoke">Revoke</string>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 56c2235..f494d8c 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1869,7 +1869,7 @@ please see styles_device_defaults.xml.
<style name="Widget.Holo.ListPopupWindow" parent="Widget.ListPopupWindow">
<item name="android:dropDownSelector">@android:drawable/list_selector_holo_dark</item>
- <item name="android:popupBackground">@android:drawable/menu_dropdown_panel_holo_dark</item>
+ <item name="android:popupBackground">@android:drawable/menu_panel_holo_dark</item>
<item name="android:dropDownVerticalOffset">0dip</item>
<item name="android:dropDownHorizontalOffset">0dip</item>
<item name="android:dropDownWidth">wrap_content</item>
@@ -2242,7 +2242,7 @@ please see styles_device_defaults.xml.
<style name="Widget.Holo.Light.ListPopupWindow" parent="Widget.ListPopupWindow">
<item name="android:dropDownSelector">@android:drawable/list_selector_holo_light</item>
- <item name="android:popupBackground">@android:drawable/menu_dropdown_panel_holo_light</item>
+ <item name="android:popupBackground">@android:drawable/menu_panel_holo_light</item>
<item name="android:dropDownVerticalOffset">0dip</item>
<item name="android:dropDownHorizontalOffset">0dip</item>
<item name="android:dropDownWidth">wrap_content</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 563d28c..45ea182 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -713,6 +713,7 @@
<java-symbol type="string" name="relationTypeSpouse" />
<java-symbol type="string" name="relative_time" />
<java-symbol type="string" name="reset" />
+ <java-symbol type="string" name="revoke" />
<java-symbol type="string" name="ringtone_default" />
<java-symbol type="string" name="ringtone_default_with_actual" />
<java-symbol type="string" name="ringtone_picker_title" />
@@ -873,6 +874,8 @@
<java-symbol type="string" name="config_chooseTypeAndAccountActivity" />
<java-symbol type="string" name="config_appsAuthorizedForSharedAccounts" />
<java-symbol type="string" name="error_message_title" />
+ <java-symbol type="string" name="action_bar_home_description_format" />
+ <java-symbol type="string" name="action_bar_home_subtitle_description_format" />
<java-symbol type="plurals" name="abbrev_in_num_days" />
<java-symbol type="plurals" name="abbrev_in_num_hours" />
@@ -1641,6 +1644,7 @@
<java-symbol type="string" name="launch_warning_title" />
<java-symbol type="string" name="low_internal_storage_view_text" />
<java-symbol type="string" name="low_internal_storage_view_title" />
+ <java-symbol type="string" name="notification_listener_binding_label" />
<java-symbol type="string" name="report" />
<java-symbol type="string" name="select_input_method" />
<java-symbol type="string" name="select_keyboard_layout_notification_title" />
diff --git a/core/res/res/xml/storage_list.xml b/core/res/res/xml/storage_list.xml
index 944bb3a..ceebdcc 100644
--- a/core/res/res/xml/storage_list.xml
+++ b/core/res/res/xml/storage_list.xml
@@ -17,24 +17,12 @@
*/
-->
-<!-- The <device> element should contain one or more <storage> elements.
- Exactly one of these should have the attribute primary="true".
- This storage will be the primary external storage and should have mountPoint="/mnt/sdcard".
- Each storage should have both a mountPoint and storageDescription attribute.
- The following attributes are optional:
-
- primary: (boolean) this storage is the primary external storage
- removable: (boolean) this is removable storage (for example, a real SD card)
- emulated: (boolean) the storage is emulated via the FUSE sdcard daemon
- mtpReserve: (integer) number of megabytes of storage MTP should reserve for free storage
- (used for emulated storage that is shared with system's data partition)
-
- A storage should not have both emulated and removable set to true
--->
+<!-- See storage config details at http://source.android.com/tech/storage/ -->
<StorageList xmlns:android="http://schemas.android.com/apk/res/android">
<!-- removable is not set in nosdcard product -->
- <storage android:mountPoint="/mnt/sdcard"
- android:storageDescription="@string/storage_usb"
- android:primary="true" />
+ <storage
+ android:mountPoint="/storage/sdcard"
+ android:storageDescription="@string/storage_usb"
+ android:primary="true" />
</StorageList>
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java
index 9c44d61..0518e64 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestApp.java
@@ -265,8 +265,8 @@ public class DownloadManagerTestApp extends DownloadManagerBaseTest {
* @throws Exception if unsuccessful
*/
public void runDownloadMultipleSwitching() throws Exception {
- String filename = DOWNLOAD_500K_FILENAME;
- long filesize = DOWNLOAD_500K_FILESIZE;
+ String filename = DOWNLOAD_5MB_FILENAME;
+ long filesize = DOWNLOAD_5MB_FILESIZE;
doCommonDownloadSetup();
String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
@@ -340,8 +340,8 @@ public class DownloadManagerTestApp extends DownloadManagerBaseTest {
* @throws Exception if unsuccessful
*/
public void runDownloadMultipleWiFiEnableDisable() throws Exception {
- String filename = DOWNLOAD_500K_FILENAME;
- long filesize = DOWNLOAD_500K_FILESIZE;
+ String filename = DOWNLOAD_5MB_FILENAME;
+ long filesize = DOWNLOAD_5MB_FILESIZE;
doCommonDownloadSetup();
String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
@@ -409,8 +409,8 @@ public class DownloadManagerTestApp extends DownloadManagerBaseTest {
* @throws Exception if unsuccessful
*/
public void runDownloadMultipleAirplaneModeEnableDisable() throws Exception {
- String filename = DOWNLOAD_500K_FILENAME;
- long filesize = DOWNLOAD_500K_FILESIZE;
+ String filename = DOWNLOAD_5MB_FILENAME;
+ long filesize = DOWNLOAD_5MB_FILESIZE;
// make sure WiFi is enabled, and airplane mode is not on
doCommonDownloadSetup();
diff --git a/data/fonts/DroidSerif-Bold.ttf b/data/fonts/DroidSerif-Bold.ttf
index 838d255..16a914e 100644
--- a/data/fonts/DroidSerif-Bold.ttf
+++ b/data/fonts/DroidSerif-Bold.ttf
Binary files differ
diff --git a/data/fonts/DroidSerif-BoldItalic.ttf b/data/fonts/DroidSerif-BoldItalic.ttf
index 0b1601f..50324fc 100644
--- a/data/fonts/DroidSerif-BoldItalic.ttf
+++ b/data/fonts/DroidSerif-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/DroidSerif-Italic.ttf b/data/fonts/DroidSerif-Italic.ttf
index 2972809..bb2757c 100644
--- a/data/fonts/DroidSerif-Italic.ttf
+++ b/data/fonts/DroidSerif-Italic.ttf
Binary files differ
diff --git a/data/fonts/DroidSerif-Regular.ttf b/data/fonts/DroidSerif-Regular.ttf
index 5b4fe81..da0a2cc 100644
--- a/data/fonts/DroidSerif-Regular.ttf
+++ b/data/fonts/DroidSerif-Regular.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Bold.ttf b/data/fonts/Roboto-Bold.ttf
index 40ecd14..072b842 100644
--- a/data/fonts/Roboto-Bold.ttf
+++ b/data/fonts/Roboto-Bold.ttf
Binary files differ
diff --git a/data/fonts/Roboto-BoldItalic.ttf b/data/fonts/Roboto-BoldItalic.ttf
index d9067c5..74919ff 100644
--- a/data/fonts/Roboto-BoldItalic.ttf
+++ b/data/fonts/Roboto-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Italic.ttf b/data/fonts/Roboto-Italic.ttf
index 88e4a5b..4642d6f 100644
--- a/data/fonts/Roboto-Italic.ttf
+++ b/data/fonts/Roboto-Italic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Light.ttf b/data/fonts/Roboto-Light.ttf
index 2ae4dec..13bf13a 100644
--- a/data/fonts/Roboto-Light.ttf
+++ b/data/fonts/Roboto-Light.ttf
Binary files differ
diff --git a/data/fonts/Roboto-LightItalic.ttf b/data/fonts/Roboto-LightItalic.ttf
index 44177ef..130672a 100644
--- a/data/fonts/Roboto-LightItalic.ttf
+++ b/data/fonts/Roboto-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Regular.ttf b/data/fonts/Roboto-Regular.ttf
index f592adf..0ba95c9 100644
--- a/data/fonts/Roboto-Regular.ttf
+++ b/data/fonts/Roboto-Regular.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Thin.ttf b/data/fonts/Roboto-Thin.ttf
index 5ae4d7f..309c22d 100644
--- a/data/fonts/Roboto-Thin.ttf
+++ b/data/fonts/Roboto-Thin.ttf
Binary files differ
diff --git a/data/fonts/Roboto-ThinItalic.ttf b/data/fonts/Roboto-ThinItalic.ttf
index 9cd3927..0b53ba4 100644
--- a/data/fonts/Roboto-ThinItalic.ttf
+++ b/data/fonts/Roboto-ThinItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Bold.ttf b/data/fonts/RobotoCondensed-Bold.ttf
index 21c10f5..f0fd409 100644
--- a/data/fonts/RobotoCondensed-Bold.ttf
+++ b/data/fonts/RobotoCondensed-Bold.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-BoldItalic.ttf b/data/fonts/RobotoCondensed-BoldItalic.ttf
index d8edd2d..e67b02b 100644
--- a/data/fonts/RobotoCondensed-BoldItalic.ttf
+++ b/data/fonts/RobotoCondensed-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Italic.ttf b/data/fonts/RobotoCondensed-Italic.ttf
index 4dec2cf..a08414b 100644
--- a/data/fonts/RobotoCondensed-Italic.ttf
+++ b/data/fonts/RobotoCondensed-Italic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Regular.ttf b/data/fonts/RobotoCondensed-Regular.ttf
index 875ea1a..713fd30 100644
--- a/data/fonts/RobotoCondensed-Regular.ttf
+++ b/data/fonts/RobotoCondensed-Regular.ttf
Binary files differ
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index 2ca77dd..3e3b12d 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -24,6 +24,9 @@ redirects:
- from: /sdk/installing/next.html
to: /training/basics/firstapp/index.html
+- from: /sdk/ndk/overview.html
+ to: /tools/sdk/ndk/index.html
+
- from: /sdk/ndk/...
to: /tools/sdk/ndk/...
@@ -113,13 +116,14 @@ redirects:
- from: /guide/appendix/install-location.html
to: /guide/topics/data/install-location.html
-- from: /guide/basics/what-is-android.html
+- from: /guide/basics/...
to: /about/index.html
- from: /guide/topics/security/security.html
to: /training/articles/security-tips.html
-# type: permanent
-# comment: Move content and then adjust this
+
+- from: /guide/topics/security/index.html
+ to: /training/articles/security-tips.html
- from: /guide/appendix/market-filters.html
to: /google/play/filters.html
diff --git a/docs/html/about/versions/android-4.0.jd b/docs/html/about/versions/android-4.0.jd
index f2fd0c4..868227a 100644
--- a/docs/html/about/versions/android-4.0.jd
+++ b/docs/html/about/versions/android-4.0.jd
@@ -122,7 +122,7 @@ to invoke an action that indicates the user wants to add a contact to a social n
receiving the app uses it to invite the specified contact to that
social network. Most apps will be on the receiving-end of this operation. For example, the
built-in People app invokes the invite intent when the user selects "Add connection" for a specific
-social app that's listed in a person's contact details.</p>
+social app that's listed in a person's contact details.</p>
<p>To make your app visible as in the "Add connection" list, your app must provide a sync adapter to
sync contact information from your social network. You must then indicate to the system that your
@@ -327,7 +327,7 @@ image (usually done by calling the {@link android.opengl.GLES20#glTexImage2D glT
function). You may provide multiple mipmap levels. If the output texture has not been bound to a
texture image, it will be automatically bound by the effect as a {@link
android.opengl.GLES20#GL_TEXTURE_2D} and with one mipmap level (0), which will have the same
-size as the input.</p>
+size as the input.</p>
<p>All effects listed in {@link android.media.effect.EffectFactory} are guaranteed to be supported.
However, some additional effects available from external libraries are not supported by all devices,
@@ -452,7 +452,7 @@ android.hardware.Camera.Parameters#getMaxNumDetectedFaces()} and ensure the retu
value is greater than zero. Also, some devices may not support identification of eyes and mouth,
in which case, those fields in the {@link android.hardware.Camera.Face} object will be null.</p>
-
+
<h4>Focus and metering areas</h4>
<p>Camera apps can now control the areas that the camera uses for focus and for metering white
@@ -495,7 +495,7 @@ added in API level 9.</p>
<h4>Other camera features</h4>
-<ul>
+<ul>
<li>While recording video, you can now call {@link android.hardware.Camera#takePicture
takePicture()} to save a photo without interrupting the video session. Before doing so, you should
call {@link android.hardware.Camera.Parameters#isVideoSnapshotSupported} to be sure the hardware
@@ -775,7 +775,7 @@ methods that allow the view and its parents to add more contextual information t
<li>When invoked, the {@link
android.view.View#sendAccessibilityEvent sendAccessibilityEvent()} and {@link
android.view.View#sendAccessibilityEventUnchecked sendAccessibilityEventUnchecked()} methods defer
-to {@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()}.
+to {@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()}.
<p>Custom implementations of {@link android.view.View} might want to implement {@link
android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()} to
attach additional accessibility information to the {@link
@@ -1022,46 +1022,6 @@ roaming or connected to Wi-Fi.</p>
-<h3 id="RenderScript">RenderScript</h3>
-
-<p>Three major features have been added to RenderScript:</p>
-
-<ul>
- <li>Off-screen rendering to a framebuffer object</li>
- <li>Rendering inside a view</li>
- <li>RS for each from the framework APIs</li>
-</ul>
-
-<p>The {@link android.renderscript.Allocation} class now supports a {@link
-android.renderscript.Allocation#USAGE_GRAPHICS_RENDER_TARGET} memory space, which allows you to
-render things directly into the {@link android.renderscript.Allocation} and use it as a framebuffer
-object.</p>
-
-<p>{@link android.renderscript.RSTextureView} provides a means to display RenderScript graphics
-inside of a {@link android.view.View}, unlike {@link android.renderscript.RSSurfaceView}, which
-creates a separate window. This key difference allows you to do things such as move, transform, or
-animate an {@link android.renderscript.RSTextureView} as well as draw RenderScript graphics inside
-a view that lies within an activity layout.</p>
-
-<p>The {@link android.renderscript.Script#forEach Script.forEach()} method allows you to call
-RenderScript compute scripts from the VM level and have them automatically delegated to available
-cores on the device. You do not use this method directly, but any compute RenderScript that you
-write will have a {@link android.renderscript.Script#forEach forEach()} method that you can call in
-the reflected RenderScript class. You can call the reflected {@link
-android.renderscript.Script#forEach forEach()} method by passing in an input {@link
-android.renderscript.Allocation} to process, an output {@link android.renderscript.Allocation} to
-write the result to, and a {@link android.renderscript.FieldPacker} data structure in case the
-RenderScript needs more information. Only one of the {@link android.renderscript.Allocation}s is
-necessary and the data structure is optional.</p>
-
-
-
-
-
-
-
-
-
<h3 id="Enterprise">Enterprise</h3>
<p>Android 4.0 expands the capabilities for enterprise application with the following features.</p>
@@ -1758,7 +1718,7 @@ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targe
notes for more information.</li>
</ul>
</dd>
-
+
<dt><a href="android-3.1.html">Android 3.1</a></dt>
<dd>
<ul>
@@ -1781,7 +1741,7 @@ android.net.rtp} documentation.</li>
notes for many more new APIs.</li>
</ul>
</dd>
-
+
<dt><a href="android-3.2.html">Android 3.2</a></dt>
<dd>
<ul>
diff --git a/docs/html/design/building-blocks/buttons.jd b/docs/html/design/building-blocks/buttons.jd
index 9e82ed4..600ec6c 100644
--- a/docs/html/design/building-blocks/buttons.jd
+++ b/docs/html/design/building-blocks/buttons.jd
@@ -1,5 +1,5 @@
page.title=Buttons
-page.tags="button"
+page.tags="button","input"
@jd:body
<p>A button consists of text and/or an image that clearly communicates what action will occur when the
diff --git a/docs/html/design/building-blocks/grid-lists.jd b/docs/html/design/building-blocks/grid-lists.jd
index 69a43b1..8c82ba9 100644
--- a/docs/html/design/building-blocks/grid-lists.jd
+++ b/docs/html/design/building-blocks/grid-lists.jd
@@ -1,5 +1,5 @@
page.title=Grid Lists
-page.tags="gridview","layout"
+page.tags="gridview","layout","listview"
@jd:body
<img src="{@docRoot}design/media/gridview_overview.png">
diff --git a/docs/html/design/building-blocks/progress.jd b/docs/html/design/building-blocks/progress.jd
index 96cc1af..60ad2ca 100644
--- a/docs/html/design/building-blocks/progress.jd
+++ b/docs/html/design/building-blocks/progress.jd
@@ -1,5 +1,5 @@
page.title=Progress &amp; Activity
-page.tags="progressbar"
+page.tags="progressbar","download","network"
@jd:body
<p>Progress bars and activity indicators signal to users that something is happening that will take a moment.</p>
diff --git a/docs/html/design/building-blocks/spinners.jd b/docs/html/design/building-blocks/spinners.jd
index 8c80b91..3550b0c 100644
--- a/docs/html/design/building-blocks/spinners.jd
+++ b/docs/html/design/building-blocks/spinners.jd
@@ -1,5 +1,5 @@
page.title=Spinners
-page.tags="spinner","drop down"
+page.tags="spinner","dropdown"
@jd:body
<p>Spinners provide a quick way to select one value from a set. In the default state, a spinner shows
diff --git a/docs/html/design/building-blocks/switches.jd b/docs/html/design/building-blocks/switches.jd
index 6386bdf..0b195b9 100644
--- a/docs/html/design/building-blocks/switches.jd
+++ b/docs/html/design/building-blocks/switches.jd
@@ -1,5 +1,5 @@
page.title=Switches
-page.tags="switch","checkbox","radiobutton"
+page.tags="switch","checkbox","radiobutton","button"
@jd:body
<p>Switches allow the user to select options. There are three kinds of switches: checkboxes, radio
diff --git a/docs/html/design/building-blocks/tabs.jd b/docs/html/design/building-blocks/tabs.jd
index 1fe2c62..79cc9c7 100644
--- a/docs/html/design/building-blocks/tabs.jd
+++ b/docs/html/design/building-blocks/tabs.jd
@@ -1,5 +1,5 @@
page.title=Tabs
-page.tags="tabs","action bar","navigation"
+page.tags="tabs","actionbar","navigation","viewpager"
@jd:body
<img src="{@docRoot}design/media/tabs_overview.png">
diff --git a/docs/html/design/building-blocks/text-fields.jd b/docs/html/design/building-blocks/text-fields.jd
index c1bed78..82321f0 100644
--- a/docs/html/design/building-blocks/text-fields.jd
+++ b/docs/html/design/building-blocks/text-fields.jd
@@ -1,5 +1,5 @@
page.title=Text Fields
-page.tags="text","edittext","input",
+page.tags="text","edittext","input"
@jd:body
<p>Text fields allow the user to type text into your app. They can be either single line or multi-line.
diff --git a/docs/html/design/patterns/accessibility.jd b/docs/html/design/patterns/accessibility.jd
index edf3843..5f46082 100644
--- a/docs/html/design/patterns/accessibility.jd
+++ b/docs/html/design/patterns/accessibility.jd
@@ -1,5 +1,5 @@
page.title=Accessibility
-page.tags="accessibility","navigation"
+page.tags="accessibility","navigation","input"
@jd:body
<p>One of Android's missions is to organize the world's information and make it universally accessible and useful. Accessibility is the measure of how successfully a product can be used by people with varying abilities. Our mission applies to all users-including people with disabilities such as visual impairment, color deficiency, hearing loss, and limited dexterity.</p>
diff --git a/docs/html/design/patterns/app-structure.jd b/docs/html/design/patterns/app-structure.jd
index e1bb819..1809ecd 100644
--- a/docs/html/design/patterns/app-structure.jd
+++ b/docs/html/design/patterns/app-structure.jd
@@ -1,5 +1,5 @@
page.title=Application Structure
-page.tags="navigation","layout"
+page.tags="navigation","layout","tablet"
@jd:body
<p>Apps come in many varieties that address very different needs. For example:</p>
diff --git a/docs/html/design/patterns/compatibility.jd b/docs/html/design/patterns/compatibility.jd
index 84ae337..3a56f52 100644
--- a/docs/html/design/patterns/compatibility.jd
+++ b/docs/html/design/patterns/compatibility.jd
@@ -1,4 +1,5 @@
page.title=Backwards Compatibility
+page.tags="support"
@jd:body
<p>Significant changes in Android 3.0 included:</p>
diff --git a/docs/html/design/patterns/confirming-acknowledging.jd b/docs/html/design/patterns/confirming-acknowledging.jd
index f2e88ec..e347231 100644
--- a/docs/html/design/patterns/confirming-acknowledging.jd
+++ b/docs/html/design/patterns/confirming-acknowledging.jd
@@ -1,5 +1,5 @@
page.title=Confirming &amp; Acknowledging
-page.tags="dialog","toast"
+page.tags="dialog","toast","notification"
@jd:body
<p>In some situations, when a user invokes an action in your app, it's a good idea to <em>confirm</em> or <em>acknowledge</em> that action through text.</p>
diff --git a/docs/html/design/patterns/gestures.jd b/docs/html/design/patterns/gestures.jd
index 3ef133d..127a1c8 100644
--- a/docs/html/design/patterns/gestures.jd
+++ b/docs/html/design/patterns/gestures.jd
@@ -1,5 +1,5 @@
page.title=Gestures
-page.tags="gesture","input"
+page.tags="gesture","input","touch"
@jd:body
<p>Gestures allow users to interact with your app by manipulating the screen objects you provide. The
diff --git a/docs/html/design/patterns/help.jd b/docs/html/design/patterns/help.jd
index cdac54d..a32fb25 100644
--- a/docs/html/design/patterns/help.jd
+++ b/docs/html/design/patterns/help.jd
@@ -1,4 +1,5 @@
page.title=Help
+page.tags="settings","preferences"
@jd:body
<p>We wish we could guarantee that if you follow every piece of advice on this website, everyone will be able to learn and use your app without a hitch. Sadly, that's not the case.</p>
diff --git a/docs/html/design/patterns/navigation.jd b/docs/html/design/patterns/navigation.jd
index 36debbe..b717884 100644
--- a/docs/html/design/patterns/navigation.jd
+++ b/docs/html/design/patterns/navigation.jd
@@ -1,5 +1,5 @@
page.title=Navigation with Back and Up
-page.tags="navigation","activity"
+page.tags="navigation","activity","task"
@jd:body
<p>Consistent navigation is an essential component of the overall user experience. Few things frustrate
diff --git a/docs/html/design/patterns/selection.jd b/docs/html/design/patterns/selection.jd
index 682ce56..d16e86c 100644
--- a/docs/html/design/patterns/selection.jd
+++ b/docs/html/design/patterns/selection.jd
@@ -1,5 +1,5 @@
page.title=Selection
-page.tags="actionmode","navigation"
+page.tags="actionmode","navigation","contextual"
@jd:body
<p>Android 3.0 changed the <em>long press</em> gesture&mdash;that is, a touch that's held in the same position for a moment&mdash;to be the global gesture to select data.. This affects the way you should
diff --git a/docs/html/design/patterns/settings.jd b/docs/html/design/patterns/settings.jd
index f86cd39..4748e48 100644
--- a/docs/html/design/patterns/settings.jd
+++ b/docs/html/design/patterns/settings.jd
@@ -1,5 +1,5 @@
page.title=Settings
-page.tags="settings","preferences"
+page.tags="preferences","sharedpreferences"
@jd:body
<p>Settings is a place in your app where users indicate their preferences for how your app should
diff --git a/docs/html/design/patterns/swipe-views.jd b/docs/html/design/patterns/swipe-views.jd
index b86d990..f18fc63 100644
--- a/docs/html/design/patterns/swipe-views.jd
+++ b/docs/html/design/patterns/swipe-views.jd
@@ -1,5 +1,5 @@
page.title=Swipe Views
-page.tags="viewpager","navigation"
+page.tags="viewpager","navigation","tabs"
@jd:body
<p>Efficient navigation is one of the cornerstones of a well-designed app. While apps are generally
diff --git a/docs/html/design/patterns/widgets.jd b/docs/html/design/patterns/widgets.jd
index f2b0f4a..3152e91 100644
--- a/docs/html/design/patterns/widgets.jd
+++ b/docs/html/design/patterns/widgets.jd
@@ -1,5 +1,5 @@
page.title=Widgets
-page.tags="appwidget"
+page.tags="appwidget","home"
@jd:body
<p>Widgets are an essential aspect of home screen customization. You can imagine them as "at-a-glance" views of an app's most important data and functionality that is accessible right from the user's home screen. Users can move widgets across their home screen panels, and, if supported, resize them to tailor the amount of information within a widget to their preference.</p>
diff --git a/docs/html/design/style/iconography.jd b/docs/html/design/style/iconography.jd
index ce11cf7..1475e5c 100644
--- a/docs/html/design/style/iconography.jd
+++ b/docs/html/design/style/iconography.jd
@@ -1,4 +1,5 @@
page.title=Iconography
+page.tags="icons"
@jd:body
<img src="{@docRoot}design/media/iconography_overview.png">
diff --git a/docs/html/design/style/metrics-grids.jd b/docs/html/design/style/metrics-grids.jd
index e2b9ab5..3116ff6 100644
--- a/docs/html/design/style/metrics-grids.jd
+++ b/docs/html/design/style/metrics-grids.jd
@@ -1,4 +1,5 @@
page.title=Metrics and Grids
+page.tags="layout","screens"
@jd:body
<p>Devices vary not only in physical size, but also in screen density (<acronym title="Dots per
diff --git a/docs/html/design/style/touch-feedback.jd b/docs/html/design/style/touch-feedback.jd
index 5fe72a7..340a3a4 100644
--- a/docs/html/design/style/touch-feedback.jd
+++ b/docs/html/design/style/touch-feedback.jd
@@ -1,4 +1,5 @@
page.title=Touch Feedback
+page.tags="input","button"
@jd:body
<div class="layout-content-row" style="margin-bottom: -100px">
diff --git a/docs/html/design/style/typography.jd b/docs/html/design/style/typography.jd
index 427b8c6..114d13b 100644
--- a/docs/html/design/style/typography.jd
+++ b/docs/html/design/style/typography.jd
@@ -1,4 +1,5 @@
page.title=Typography
+page.tags="textview","font"
@jd:body
<div class="layout-content-row">
diff --git a/docs/html/design/style/writing.jd b/docs/html/design/style/writing.jd
index 919ea7a..5358847 100644
--- a/docs/html/design/style/writing.jd
+++ b/docs/html/design/style/writing.jd
@@ -1,4 +1,5 @@
page.title=Writing Style
+page.tags="dialog","toast","notification"
@jd:body
<p>When choosing words for your app:</p>
diff --git a/docs/html/distribute/distribute_toc.cs b/docs/html/distribute/distribute_toc.cs
index ad3121c..3ea11bf 100644
--- a/docs/html/distribute/distribute_toc.cs
+++ b/docs/html/distribute/distribute_toc.cs
@@ -1,106 +1,71 @@
<ul id="nav">
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/index.html">
- <span class="en">Google Play</span></a>
- </div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/index.html">Google Play</a></div>
<ul>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/about/visibility.html">
- <span class="en">Visibility</a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/about/monetizing.html">
- <span class="en">Monetizing</a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/about/distribution.html">
- <span class="en">Distribution</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/about/visibility.html">Visibility</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/about/monetizing.html">Monetizing</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/about/distribution.html">Distribution</a></li>
</ul>
</li>
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/publish/index.html">
- <span class="en">Publishing</span></a>
- </div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/publish/index.html">Publishing</a></div>
<ul>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/publish/register.html">
- <span class="en">Get Started</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/publish/console.html">
- <span class="en">Developer Console</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/publish/preparing.html">
- <span class="en">Publishing Checklist</span>
- </a></li>
-
- </ul>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/publish/register.html">Get Started</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/publish/console.html">Developer Console</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/publish/preparing.html">Publishing Checklist</a></li>
+ </ul>
</li>
<!-- <li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/developer-console.html">
- <span class="en">The Developer Console</span>
- </a>
+ <div class="nav-section-header">
+ <a href="<?cs var:toroot ?>distribute/googleplay/developer-console.html">The Developer Console</a>
</div>
<ul>
- <li class="nav-section"><a href="<?cs var:toroot ?>distribute/googleplay/register.html">
- <span class="en">Get Started</span></a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/distribution-controls.html">
- <span class="en">Managing Distribution</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/pricing-billing.html">
- <span class="en">Pricing and Billing</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/app-data.html">
- <span class="en">Reviewing App Data</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/advanced-options.html">
- <span class="en">Advanced Options</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/publishing.html">
- <span class="en">Publishing and Updating</span>
- </a></li>
+ <li class="nav-section"><a href="<?cs var:toroot ?>distribute/googleplay/register.html">Get Started</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/distribution-controls.html">Managing Distribution</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/pricing-billing.html">Pricing and Billing</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/app-data.html">Reviewing App Data</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/advanced-options.html">Advanced Options</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/publishing.html">Publishing and Updating</a></li>
</ul>
</li> end of Developer Console -->
-
+
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/promote/index.html">
- <span class="en">Promoting</span></a>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/promote/index.html">Promoting</a>
</div>
<ul>
-<!-- <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/product-pages.html">
- <span class="en">Your Product Pages</a></li>
--->
- <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/linking.html">
- <span class="en">Linking to Your Products</a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/badges.html">
- <span class="en">Google Play Badges</a></li>
- <li><a href="<?cs var:toroot ?>distribute/promote/device-art.html">
- <span class="en">Device Art Generator</a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/brand.html">
- <span class="en">Brand Guidelines</a></li>
+<!-- <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/product-pages.html">Your Product Pages</a></li> -->
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/linking.html">Linking to Your Products</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/badges.html">Google Play Badges</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/promote/device-art.html">Device Art Generator</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/brand.html">Brand Guidelines</a></li>
</ul>
</li>
-
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/quality/index.html">
- <span class="en">App Quality</span></a>
- </div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/quality/index.html">App Quality</a></div>
<ul>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/quality/core.html">
- <span class="en">Core App Quality</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/quality/tablet.html">
- <span class="en">Tablet App Quality</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/strategies/app-quality.html">
- <span class="en">Improving App Quality</span>
- </a></li>
-
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/quality/core.html">Core App Quality</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/quality/tablet.html">Tablet App Quality</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/strategies/app-quality.html">Improving App Quality</a></li>
</ul>
</li>
+ <li class="nav-section">
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/policies/index.html">Policies</a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/policies/spam.html">Spam</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/policies/ip.html">Intellectual<br />Property</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/policies/ads.html">Ads</a></li>
+ </ul>
+ </li>
<!--
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/after.html">
- <span class="en">After Launch</span></a>
+ After Launch</a>
</div>
<ul>
<li><a href="<?cs var:toroot ?>distribute/googleplay/errors.html.html">Reviewing Errors</a></li>
@@ -111,22 +76,14 @@
-->
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/spotlight/index.html">
- <span class="en">Spotlight</span></a>
- </div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/spotlight/index.html">Spotlight</a></div>
<ul>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/spotlight/tablets.html">
- <span class="en">Tablet Stories</span>
- </a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/spotlight/tablets.html">Tablet Stories</a></li>
</ul>
</li>
<li class="nav-section">
- <div class="nav-section-header empty">
- <a href="<?cs var:toroot ?>distribute/open.html">
- <span class="en">Open Distribution</span>
- </a>
- </div>
+ <div class="nav-section-header empty"><a href="<?cs var:toroot ?>distribute/open.html">Open Distribution</a></div>
</li>
</ul>
\ No newline at end of file
diff --git a/docs/html/distribute/googleplay/policies/ads.jd b/docs/html/distribute/googleplay/policies/ads.jd
new file mode 100644
index 0000000..8920499
--- /dev/null
+++ b/docs/html/distribute/googleplay/policies/ads.jd
@@ -0,0 +1,352 @@
+page.title=Ads
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In This Document</h2>
+ <ol>
+ <li><a href="#content-maturity">Content and Maturity</a></li>
+ <li><a href="#context">Context and Behavior</a></li>
+ <li><a href="#disclosure" style="clear:right">Disclosure</a></li>
+ <li><a href="#impersonation">Impersonation of System UI</a></li>
+ <li><a href="#adwalls">Adwalls</a></li>
+ <li><a href="#interfering" style="clear:right;">Interference with Ads and Websites</a></li>
+ </ol>
+
+ <h2>More Resources</h2>
+ <ol>
+ <li><a href="http://play.google.com/about/developer-content-policy.html" target="_policies">Developer Program Policies</a></li>
+ <li><a href="http://www.android.com/us/developer-distribution-agreement.html#showlanguages" target="_policies">Developer Distribution Agreement</a></li>
+ <li><a href="http://support.google.com/googleplay/android-developer/answer/188189" target="_policies">Maturity Ratings</a></p>
+ </ol>
+</div>
+</div>
+
+<p>
+ Google Play policies guide how you can use ads in your apps, to help ensure
+ the best experience for users visiting and downloading apps from the store.
+</p>
+
+<p>
+ In general, for the purposes of policy, the content of ads displayed by your
+ app is considered part of your app. As an app developer, it is your
+ responsibility to ensure that the content, context, and behavior of ads in
+ your apps conforms to Google Play policies.
+</p>
+
+<p>
+ Before you publish, make sure you understand Google Play ad policies and how
+ to display ads in conformance with those policies. The sections below
+ highlight best practices and common examples to help you avoid the most
+ common types of policy violations.
+</p>
+
+<p>
+ For more information about Google Play policies that apply to your apps and
+ content, please see the <a href=
+ "http://play.google.com/about/developer-content-policy.html" target=
+ "_policies">Developer Program Policies</a> and <a href=
+ "http://play.google.com/about/developer-distribution-agreement.html" target=
+ "_policies">Developer Distribution Agreement</a>.
+</p>
+
+
+<h2 id="content-maturity">Content and Maturity</h2>
+
+<div class="example-block bad">
+ <div class="heading">Ad maturity exceeds app</div>
+ <img src="{@docRoot}images/gp-policy-ads-maturity-violation.png">
+</div>
+
+<p>
+ From a policy perspective, ads shown in your app are part of your content
+ and your app is responsible for any violations. If an ad shown in your app
+ violates Google Play policies, your app may be suspended or your developer
+ account terminated.
+</p>
+
+<p>
+ For this reason, it's important for you to be be aware of what ads will be
+ displayed in your app and to manage the ads content according to Google Play
+ policies. Here are some guidelines:
+</p>
+
+<ul>
+ <li>
+ <strong>Ads must not violate Content Policy</strong>&mdash;Ads in
+ your app must not violate the terms of Google Play’s Content Policy,
+ including those concerning illegal activities, violence, sexually
+ explicit content, or privacy violations.
+ </li>
+ <li>
+ <strong>Ads maturity must be consistent with your app's
+ maturity</strong>&mdash;Content shown in your ads must be consistent
+ with the app’s maturity rating in Google Play. Especially, ads content
+ should never exceed your app's maturity rating, even if the ads content
+ by itself complies with general policies.
+ </li>
+</ul>
+
+<p>
+ In the example at right, the app's maturity rating is set to
+ "Everyone", which is the lowest maturity level on Google Play. By choosing
+ the "Everyone" maturity level, the developer is declaring that all of the
+ content in the app, <em>including ads</em>, is suitable for all users
+ regardless of age.
+</p>
+
+<p>
+ The example app violates Google Play policies by displaying ad content with a
+ higher maturity level&mdash;ad content showing gambling, profanity, user
+ location, suggestive content, or content from another app with higher
+ maturity exceeds the "Everyone" maturity rating. Because the ad's
+ maturity is higher than the app's maturity level, the app itself is in
+ violation of policy. To correct the problem, the developer must either
+ restrict ads content to "Everyone" level or raise the app's maturity rating.
+</p>
+
+<p>
+ For detailed information about how to choose the appropriate maturity level
+ for your app, or to assess the maturity requirement of ads in your app, see
+ <a href=
+ "http://support.google.com/googleplay/android-developer/answer/188189"
+ target="_policies">Rating your application content for Google Play</a>.
+</p>
+
+
+<h2 id="context">Context and Behavior</h2>
+
+<p>
+ If your app displays ads, it should do so in ways that do not interrupt users,
+ mislead them into clicking on ads, or make changes outside the app without
+ the user's knowledge or consent. Here are some guidelines:
+</p>
+
+<ul>
+ <li>
+ <strong>Display your ads within your UI</strong>&mdash;If possible,
+ display ads only within your app's UI. This leads to a better user
+ experience and helps avoid policy violations
+ </li>
+
+ <li>
+ <strong>Make sure app origin is clear</strong>&mdash;When you display an
+ ad, it must be clear to the user that the ad has originated from your app.
+ If you show the ad in your app's UI while your app has focus, the user
+ understands the ad origin without explicit attribution. However, if you
+ display the ad outside of your app, such as in a notification, you must
+ explicitly indicate the origin.
+ </li>
+
+ <li>
+ <strong>Don't make changes outside of the app without consent</strong>
+ &mdash;Ads must not make changes outside of the app without the user's
+ full knowledge and consent. For example, ads should not install shortcuts,
+ bookmarks, or icons, or change default settings without user consent.
+ </li>
+
+ <li>
+ <strong>Changes outside the app must be reversible</strong>&mdash;If an
+ ad makes changes outside the app as described above, the changes (and
+ origin app) must be evident and easily reversible. For example, the user
+ must be able to locate and reverse the changes by adjusting settings,
+ changing ad preferences in the app, or uninstalling the app altogether.
+ </li>
+
+ <li>
+ <strong>Notification ads require user opt-in</strong>&mdash;Your app
+ should not create <a href=
+ "{@docRoot}design/patterns/notifications.html">notifications</a>
+ containing ads unless the user has specifically opted-in to this behavior
+ and is able to easily opt-out.
+ </li>
+
+ <li>
+ <strong>Use low priority for notification ads</strong>&mdash;Always
+ assign your notification ads <a href="
+ {@docRoot}reference/android/app/Notification.html#PRIORITY_LOW">low
+ priority</a> (for API level 16 and above).
+ </li>
+</ul>
+
+<div class="example-block bad" style="width:400px;margin:.5em 0 0 2em;">
+ <div class="heading">Does not fully indicate origin app</div>
+ <img src="{@docRoot}images/gp-policy-ads-notif-attr-violation.png">
+</div>
+<div class="example-block good" style="width:400px;margin:.5em 0 0 2em;">
+ <div class="heading">Indicates origin app by name and icon</div>
+ <img src="{@docRoot}images/gp-policy-ads-notif-attr.png">
+</div>
+
+<p>
+ In particular, note that notification ads must clearly identify your app as
+ the ad origin. If your app sends notification ads that do not sufficiently
+ identify your app as the origin, the app will be in violation of policy.
+</p>
+
+<p>
+ To identify your app as the origin, you should display the <strong>app's full
+ name and and icon</strong> in the notification to provide the clearest
+ identification and best policy compliance. Displaying a partial app name can
+ also be sufficient, provided the name unambiguously identifies your app.
+</p>
+
+<p>
+ Above right is an example notification ad that violates ad policy by not
+ providing attribution of the origin app. Below right, the notification ads
+ comply with policy by providing both the app icon and full app name (in this
+ case, "Turtle Test").
+</p>
+
+
+<h2 id="disclosure" style="clear:right">Disclosure of Ads to Users</h2>
+
+<p>
+ It's important to sufficiently disclose to users how your app will use ads.
+ You must make it easy for users to understand what ads will be shown in your
+ app, where they will be shown, and what the associated behaviors are, if any.
+ Further, you should ask for user consent and provide options for managing ads
+ or opt-out. Here are some guidelines:
+</p>
+
+<ul>
+ <li>
+ <strong>Tell users about your ads</strong>&mdash;Create a simple,
+ complete disclosure that tells users how your app uses ads, where the ads
+ are shown, and how they can manage ad options. Take common-sense steps to
+ make the disclosure as clear as possible.
+ </li>
+
+ <li>
+ <strong>Make sure users know</strong>&mdash;Present your ads disclosure
+ is an easy-to-see location, rather than hiding it where users are not
+ likely to find it.
+ </li>
+
+ <li>
+ <strong>Ask for consent (opt-in) at launch</strong>&mdash;Where possible,
+ include your ads disclosure in the app description as well as in an Ads
+ Terms, End User License Agreement (EULA), or similar document. Display the
+ terms at first launch and ask for the user's consent before continuing to
+ the app.
+ </li>
+</ul>
+
+<p>
+ A recommended approach is to provide an ads disclosure in an End-User License
+ Agreement (EULA). The disclosure should be clear and succinct and displayed
+ in a modal dialog that asks the user to agree to the terms before using the
+ app.
+</p>
+
+<p>
+ If your app adds homescreen icons and/or browser bookmarks, an acceptable
+ practice for revealing that behavior is to provide a disclosure in both the
+ app description and an opt-in EULA on app launch. This ensures that the
+ behaviors are clearly explained to the user up-front and requires the user’s
+ consent in a pop-up EULA to continue using the app.
+</p>
+
+<div class="example-block good" style="width:213px;margin-right:2em;">
+ <div class="heading">Disclosure in Terms</div>
+ <img src="{@docRoot}images/gp-policy-ads-terms.png">
+</div>
+
+<div class="example-block good" style="width:213px;">
+ <div class="heading">Disclosure in EULA</div>
+ <img src="{@docRoot}images/gp-policy-ads-eula.png">
+</div>
+
+<div class="example-block bad" style="width:213px;margin-left:0em;">
+ <div class="heading">Disclosure is hidden</div>
+ <img src="{@docRoot}images/gp-policy-ads-eula-violation.png">
+</div>
+
+<p style="clear:right">
+ Above left is an example of ads disclosure that is hidden in a long EULA. The
+ disclosure information itself is not clearly indicated in the document text
+ and it's not visible unless the user happens to scroll down far enough in the
+ EULA. Above middle and right show two alternative approaches that
+ present the disclosure in an obvious and clear manner at the top of a
+ EULA and in a dedicated Terms agreement.
+</p>
+
+
+<h2 id="impersonation">Impersonation of System UI</h2>
+
+<div class="example-block bad">
+ <div class="heading">Ad impersonates system dialog</div>
+ <img src="{@docRoot}images/gp-policy-ads-impersonate-violation.png">
+</div>
+
+<p>
+ Your app must not display any ad that attempts to impersonate or represent a
+ system function or UI component. If such an ad is displayed in your app, your
+ app will be in violation of policy and subject to suspension. Here are some
+ guidelines:
+</p>
+
+<ul>
+ <li>
+ <strong>No fake system dialogs or warnings</strong>&mdash;Any ad that
+ presents itself as a system dialog or warning and asks for user input is in
+ violation of Google Play policies.
+ </li>
+
+ <li>
+ <strong>No fake app updates</strong>&mdash;Ads should not impersonate
+ system UI for app updates.
+ </li>
+</ul>
+
+<p>
+ At right is an example of a pop-up ad impersonating a system dialog, warning
+ the user about viruses. This is a violation of policy.
+</p>
+
+
+<h2 id="adwalls">Adwalls</h2>
+
+<div class="example-block good" style="width:213px;">
+ <div class="heading">Adwall lets user cancel</div>
+ <img src="{@docRoot}images/gp-policy-ads-paywall.png">
+</div>
+
+<div class="example-block bad" style="width:213px;">
+ <div class="heading">Adwall forces user action</div>
+ <img src="{@docRoot}images/gp-policy-ads-paywall-violation.png">
+</div>
+
+<p>
+ If your app uses adwalls to drive affiliate traffic, those adwalls must not
+ force the user to click on ads or submit personal information for advertising
+ purposes before using the app.
+</p>
+
+<p>
+ Forcing a user action in an adwall is not only a poor user experience, it is
+ a violation of Google Play policies.
+</p>
+
+<p>
+ For this reason, <strong>all adwalls must give the user the option to
+ cancel</strong> or otherwise dismiss the ad without penalty.
+</p>
+
+<p>
+ At right is an example of an app that requires the user to click through the
+ ad to fully use the app. This is a violation of policy.
+</p>
+
+<p>
+ The adjacent example demonstrates an adequate option to let the user dismiss
+ the ad wall easily by cancelling.
+</p>
+
+
+<h2 id="interfering" style="clear:right;">Interference with Third-party Ads and Websites</h2>
+
+<p>
+ Ads associated with your app <strong>must not interfere</strong> with any
+ other ads originating in other applications.
+</p> \ No newline at end of file
diff --git a/docs/html/distribute/googleplay/policies/index.jd b/docs/html/distribute/googleplay/policies/index.jd
new file mode 100644
index 0000000..fb46055
--- /dev/null
+++ b/docs/html/distribute/googleplay/policies/index.jd
@@ -0,0 +1,59 @@
+page.title=Google Play Policies and Guidelines
+page.metaDescription=Guidelines and tips for creating apps that comply with Google Play content and distribution policies.
+@jd:body
+
+<p>
+ Before publishing your apps on Google Play, take a few minutes to read and
+ understand the content and distribution policies that apply to all apps
+ in the store. These policies help to keep Android and Google Play an enjoyable
+ and trusted platform for content consumers and developers alike.
+</p>
+
+<p>
+ The documents below highlight important policy areas and provide tips to help
+ you create policy-compliant apps. You'll also find examples and guidance on common
+ policy questions that can help your app stay clear of practices that can result in
+ low ratings or even suspensions from the store.
+</p>
+
+<p>
+ For complete information about Google Play policies, please see the full
+ <a href="http://play.google.com/about/developer-content-policy.html" target=
+ "_policies">Developer Program Policies</a> and <a href=
+ "http://play.google.com/about/developer-distribution-agreement.html" target=
+ "_policies">Developer Distribution Agreement</a> documents.
+</p>
+
+<div class="vspace size-1">
+ &nbsp;
+</div>
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+ <h4>
+ Spam
+ </h4>
+ <p>
+ Make sure that your app does not present content that is unwanted,
+ deceptive, repetitive, or unrelated to the core function of the app.
+ </p><a href="{@docRoot}distribute/googleplay/policies/spam.html">Learn more &raquo;</a>
+ </div>
+ <div class="layout-content-col span-4">
+ <h4>
+ Intellectual Property
+ </h4>
+ <p>
+ Tips and examples of how to use intelletual property (IP) properly,
+ including when to ask permission to use someone else's copyright or
+ trademark.
+ </p><a href="{@docRoot}distribute/googleplay/policies/ip.html">Learn more &raquo;</a>
+ </div>
+ <div class="layout-content-col span-4">
+ <h4>
+ Ads
+ </h4>
+ <p>
+ Make sure that the ads displayed in your app follow the Google Play Content
+ Policy and meet the maturity rating that you have selected for your app.
+ </p><a href="{@docRoot}distribute/googleplay/policies/ads.html">Learn more &raquo;</a>
+ </div>
+</div> \ No newline at end of file
diff --git a/docs/html/distribute/googleplay/policies/ip.jd b/docs/html/distribute/googleplay/policies/ip.jd
new file mode 100644
index 0000000..0d1f68d
--- /dev/null
+++ b/docs/html/distribute/googleplay/policies/ip.jd
@@ -0,0 +1,345 @@
+page.title=Intellectual Property
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In This Document</h2>
+ <ol>
+ <li><a href="#copyright">Copyright Infringement</a></li>
+ <li><a href="#impersonation">Impersonation</a></li>
+ <li><a href="#trademarks">Trademark Infringement</a></li>
+ <li><a href="#other">DDA 4.4 Prohibited Actions</a></li>
+ </ol>
+
+ <h2>More Resources</h2>
+ <ol>
+ <li><a href="http://play.google.com/about/developer-content-policy.html"
+ target="_policies">Developer Program Policies</a></li>
+ <li><a href="http://www.android.com/us/developer-distribution-agreement.html#showlanguages"
+ target="_policies">Developer Distribution Agreement</a></li>
+ </ol>
+</div>
+</div>
+
+<p>
+ Google Play policies protect your intellectual property (IP) as well as that
+ of other app developers and content creators in the store. The policies and
+ their enforcements help ensure proper use of copyright, trademarks, and
+ developer identity in Google Play.
+</p>
+
+<p>
+ As an app developer, these IP policies benefit you. At the same time, it's
+ your responsibility to ensure that your app does not violate the IP of other
+ developers or content creators. Violations of IP-related policy may result in
+ suspension of your apps from the store and termination of your developer
+ account.
+</p>
+
+<p>
+ This document introduces several key areas of IP-related policy that you
+ should understand before publishing on Google Play. In each area you'll find
+ best practices and examples to help you avoid common types of mistakes and
+ violations.
+</p>
+
+<p>
+ For more information about Google Play policies that apply to your apps and
+ content, please see the <a href=
+ "http://play.google.com/about/developer-content-policy.html" target=
+ "_policies">Developer Program Policies</a> and <a href=
+ "http://play.google.com/about/developer-distribution-agreement.html" target=
+ "_policies">Developer Distribution Agreement</a>.
+</p>
+
+
+
+<h2 id="copyright">Copyright Infringement</h2>
+
+<p>
+ Copyright is the legal right granted to an author or creator for a literary,
+ dramatic or artistic piece of work. As soon as you create an original piece
+ of work and fix it in a tangible medium, the work is automatically protected
+ by copyright law and you are the owner of the copyright. Likewise, when other
+ people create content, they may own the copyrights for those works.
+</p>
+
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>How to report infringements</h2>
+<p>If you feel your copyright is being infringed, you may file a Digital Millenium
+ Copyright Act (DMCA) request. Please see <a
+ href="http://support.google.com/bin/request.py?&product=androidmarket&contact_type=lr_dmca"
+ target="_policies">copyright procedures</a> for more information.</p>
+</div>
+</div>
+
+<p>
+ Copyright infringement is an improper or unauthorized use of a copyrighted
+ work. If you publish an app in Google Play that uses another party's copyrighted
+ works improperly or without permission, your apps can be suspended and your
+ developer account terminated.
+</p>
+
+<p>
+ As you design your app and prepare for publishing, make sure to review Google
+ Play policies and analyze all of your content. If your app uses or links to
+ another party's original work, make sure that your app is not infringing on
+ copyright. Not all uses of another party’s work are infringements on
+ copyright, and the rules vary by country and can be complex.
+</p>
+
+<p>
+ If you are unsure whether your use of another party's work infringes on a
+ copyright, consider getting legal advice before publishing, or simply request
+ permission to use the work from the copyright owner.
+</p>
+
+<p>
+ Here are some guidelines to help you avoid copyright infringement policy
+ violations:
+</p>
+
+<ul>
+ <li>
+ <strong>Respect copyright laws</strong>&mdash;Do not let your app infringe
+ on the copyrights of others. That includes linking to other apps or web
+ sites that contain obviously infringing material (please refer to the <a href="
+ {@docRoot}distribute/googleplay/policies/spam.html#webview-spam">Spam in WebViews</a> guidelines), and using icons or images that are obvious infringements.
+ </li>
+
+ <li>
+ <strong>Know your app's content</strong>&mdash;Before you publish, look
+ for content that may be protected by trademark or copyright in your app
+ and get legal advice if necessary. Protected work could typically include
+ product names, brands, images, music, and similar works.
+ </li>
+
+ <li>
+ <strong>Create original work</strong>&mdash;If you’re not sure whether
+ something will violate another party's copyright, the safest approach is to
+ create something that's completely original, such as images or audio
+ that you’ve created yourself. When you create your own original content,
+ you rarely have to worry about infringing on existing copyright.
+ </li>
+
+ <li>
+ <strong>Ask permission to use copyrighted work</strong>&mdash;If you want
+ to use another party's copyrighted work in your app, you should ask for
+ permission from the work's creator or copyright owner and include
+ appropriate copyright attribution.
+ </li>
+</ul>
+
+<p>
+ A common misunderstanding is believing that your app may use copyrighted
+ content without permission, provided that you clearly indicate that your app
+ is not the "official" app that readers may be familiar with. That is not the
+ case. Even if you let users know that your app is "unofficial", it still
+ violates Google Play policies if it uses or links to copyrighted content
+ without permission. Also, this type of "unofficial" app may violate <a
+ href="#impersonation">impersonation policies</a>.
+</p>
+
+<p>
+ The example app below shows an app that uses screenshots/images of known
+ artists without their authorization and lists popular songs. The combination
+ of these may induce users to download music ringtones that infringe on
+ copyright. This is a violation of Google Play policy.
+</p>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">Images and downloads that violate copyright</div>
+ <img src="{@docRoot}images/gp-policy-ip-copyright-violation.png">
+</div>
+
+
+<h2 id="impersonation">Impersonation</h2>
+
+<p>
+ Impersonation is when an app attempts to imply a relationship to another app
+ or developer, where no relationship actually exists.
+</p>
+
+<p>
+ For example, if your app displays the brand, icon, or title from another app
+ in order to get to users to download your app, you are leading users to
+ believe that your app is developed by the same entity as the other app and
+ offers similar content or experience. This is an impersonation of the other
+ app and developer, and it is a violation of Google Play policy. If you
+ publish apps that violate impersonation policies, your apps can be suspended
+ and your developer account terminated.
+</p>
+
+<p>
+ No matter what type of app you offer or what your motivation, don’t try to
+ imply an endorsement or relationship to another company or product where none
+ exists. Don’t try to establish your app as the "official" version of another
+ party's work by prominently featuring their brand names or trademarks in your
+ app title or description.
+</p>
+
+<p>
+ Even if your app description states that your app is an "unofficial" version,
+ the use of the other app's branding, trademarks, and other content still can
+ violate policy by presenting content that isn’t yours.
+</p>
+
+<p>
+ Here are some guidelines:
+</p>
+
+<ul>
+ <li>
+ <strong>Don't pretend to be someone else</strong>&mdash; Don't represent
+ that your content is produced by another company or organization if that is
+ not the case.
+ </li>
+
+ <li>
+ <strong>Don't support infringing sites or apps</strong>&mdash; Don't divert
+ users or provide links to any other site that mimics Google Play or
+ represents itself as another application or service.
+ </li>
+
+ <li>
+ <strong>Don't use another app's branding</strong>&mdash; Don’t try to pass
+ off your app as the official version of someone else’s property by using a
+ person or entity (or brand) name in your app title or description.
+ </li>
+</ul>
+
+<p>
+ Below is an example of an "unofficial" app that violates Google Play policy
+ by impersonating another company and an existing product. Specifically:
+</p>
+
+<ul>
+ <li>The example app has a name and icon that appear to be impersonating an
+ existing product.
+ </li>
+
+ <li>The example developer name implies an endorsement or relationship to
+ another company and their products where none exists.
+ </li>
+</ul>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">App name, icon, and developer name that impersonate another</div>
+ <img src="{@docRoot}images/gp-policy-ip-impersonation-violation.png">
+</div>
+
+
+<h2 id="trademarks">Trademark Infringement</h2>
+
+<p>
+ A trademark is a brand that uniquely identifies a product and distinguishes
+ it from other products. It can be a word, name, symbol, or combination of
+ those that is intended to identify the source of the product. A trademark is
+ specifically acquired by a company or other entity through a legal process
+ and once acquired gives the owner exclusive rights to the trademark usage.
+</p>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>How to report infringements</h2>
+<p>If you feel your trademark is being infringed, you can request a content review.
+See <a href="http://support.google.com/bin/static.py?&ts=1114905&page=ts.cs"
+target="_policies">Removing content from Google</a> for more information.</p>
+</div>
+</div>
+
+<p>
+ Trademark infringement is improper or unauthorized use of a trademark. Google
+ Play policies prohibit apps that infringe trademarks. If you publish apps in
+ Google Play that use another party's trademarks, your apps can be suspended
+ and your developer account terminated.
+</p>
+
+<p>
+ As you design your app and prepare for publishing, make sure to review Google
+ Play policies and analyze all of your content. If your app uses a trademark
+ not owned by you, or if you are not sure whether a brand is a trademark, you
+ should get legal advice before publishing. As with copyright, the rules vary
+ by country and can be complex.
+</p>
+
+<p>
+ Here are some guidelines for avoiding trademark infringement policy
+ violations:
+</p>
+
+<ul>
+ <li>
+ <strong>Understand and follow trademark laws</strong>&mdash;Don't let your
+ app infringe on the trademarks of others.
+ </li>
+
+ <li>
+ <strong>Know your app's content</strong>&mdash;Before you publish, look for
+ brands and potential trademarks used in your app and store listing and get
+ legal advice if necessary.
+ </li>
+
+ <li>
+ <strong>Use a distinct name</strong>&mdash;Don't give your app a name that
+ is confusingly similar to another company's trademark.
+ </li>
+
+ <li>
+ <strong>Don't use trademarks to imply a relationship</strong>&mdash;Don't
+ describe your app using another company's trademarks in a way that implies
+ an endorsement by or affiliation with the other company.
+ </li>
+
+ <li>
+ <strong>Use a distinct app icon and logo</strong>&mdash;Don't use a
+ modified version of another company’s trademarked logo.
+ </li>
+</ul>
+
+<p>
+ A common misunderstanding is believing that your app may use a brand or
+ trademark without permission, provided you clearly indicate that the app is
+ not the "official" or original app. That is not the case. Even if you let
+ users know that your app is "unofficial", it still violates Google Play
+ policies if it uses another party's trademarks. Also, this type of
+ "unofficial" app may violate <a href="#impersonation">impersonation
+ policies</a>.
+</p>
+
+<p>
+ Below is an example app that violates Google Play policies by infringing on
+ another party's trademarks. Specifically:
+</p>
+
+<ul>
+ <li>The example app name is confusingly similar to another party's trademark.</li>
+ <li>The example app icon is a modified version of a another party's logo.</li>
+</ul>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">App name and icon that infringe trademarks</div>
+ <img src="{@docRoot}images/gp-policy-ip-trademark-violation.png">
+</div>
+
+
+<h2 id="other">DDA 4.4 Prohibited Actions</h2>
+
+<p>
+ When you publish an app on Google Play, you agree to the terms of the
+ Developer Distribution Agreement (DDA). Section 4.4 of the DDA prohibits certain
+ types of actions on your part. For reference, you agree that you will not
+ engage in any activity with the Market, including the development or
+ distribution of Products, that interferes with, disrupts, damages, or
+ accesses in an unauthorized manner the devices, servers, networks, or other
+ properties or services of any third party including, but not limited to,
+ Android users, Google or any mobile network operator.
+</p>
+
+<p>
+ For details, please refer to the complete <a href=
+ "http://play.google.com/about/developer-distribution-agreement.html" target=
+ "_policies">Developer Distribution Agreement</a>.
+</p> \ No newline at end of file
diff --git a/docs/html/distribute/googleplay/policies/spam.jd b/docs/html/distribute/googleplay/policies/spam.jd
new file mode 100644
index 0000000..602c89a
--- /dev/null
+++ b/docs/html/distribute/googleplay/policies/spam.jd
@@ -0,0 +1,421 @@
+page.title=Spam
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In This Document</h2>
+ <ol>
+ <li><a href="#keyword-spam">Spam in App Title and Description</a></li>
+ <li><a href="#ratings">Spam in Ratings and Reviews</a></li>
+ <li><a href="#webview-spam">Spam in WebViews</a></li>
+ <li><a href="#wizard-spam">Spam from Wizards</a></li>
+ <li><a href="#message-spam">Spam in Messaging</a></li>
+ </ol>
+
+ <h2>More Resources</h2>
+ <ol>
+ <li><a href="http://play.google.com/about/developer-content-policy.html" target="_policies">Developer Program Policies</a></li>
+ <li><a href="http://play.google.com/about/developer-distribution-agreement.html" target="_policies">Developer Distribution Agreement</a></li>
+ </ol>
+</div>
+</div>
+
+<p>
+ Google Play policies prohibit spam, to help ensure the best experience for
+ Android users. Please do not publish deceptive, repetitive, or irrelevant
+ content on Google Play. Not only will it lower your app's rating and cause
+ negative reviews, it can result in your app being suspended or your developer
+ account terminated.
+</p>
+
+<p>
+ As an app developer, it is your responsibility to ensure that your apps are
+ free from spam and conform to the Google Play policies highlighted in this
+ document. Before you publish, make sure that you understand what is
+ considered spam on Google Play and check your apps for violations, even those
+ that might be inadvertent. The sections below highlight best practices and
+ common spam examples to help you avoid the most common types of policy
+ violations.
+</p>
+
+<p>
+ For more information about Google Play policies that apply to your apps and
+ content, please see the <a href=
+ "http://play.google.com/about/developer-content-policy.html" target=
+ "_policies">Developer Program Policies</a> and <a href=
+ "http://play.google.com/about/developer-distribution-agreement.html" target=
+ "_policies">Developer Distribution Agreement</a>.
+</p>
+
+
+<h2 id="keyword-spam">Spam in App Title and Description</h2>
+
+<p>
+ When you publish an app on Google Play, you should pay special attention to
+ the app's title and description in its store listing. Those fields are
+ important because they make your app recognizable to users, and they help to
+ drive downloads by highlighting what's great about your app. A memorable
+ title and compelling description are essential to effective marketing, but
+ you should realize that these must follow Google Play policies, just as your
+ app content must do.
+</p>
+
+<p>
+ Many developers unknowingly violate spam policy in their app titles and
+ descriptions in ways that are easy to avoid. In general, you can
+ avoid spam violations in your app title and description by following these
+ best practices:
+</p>
+
+<ul>
+ <li>
+ <strong>Highlight what's great about your app</strong>&mdash;Share
+ interesting and exciting facts about your app with users. Help users
+ understand what makes your app special.
+ </li>
+
+ <li>
+ <strong>Describe your app accurately</strong>&mdash;Make sure the title
+ and description describe the app function and user experience accurately.
+ </li>
+
+ <li>
+ <strong>Don't use repetitive keywords</strong>&mdash;Avoid keywords that
+ are repetitive or excessive.
+ </li>
+
+ <li>
+ <strong>Don't include unrelated keywords or references</strong> &mdash;
+ Your description should not be loaded with irrelevant keywords in an
+ attempt to manipulate ranking or relevancy.
+ </li>
+
+ <li>
+ <strong>Keep it brief</strong>&mdash;Keep the description succinct and
+ straightforward. Shorter descriptions tend to give a better user experience
+ on devices with smaller displays. Excessive length, detail, or repetition
+ can violate spam policy.
+ </li>
+</ul>
+
+<p>
+ Here's an example app title and description that follows best practices and
+ does not violate Google Play spam policies.
+</p>
+
+<div class="example-block good" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">Best practice: App description</div>
+ <table>
+ <tr>
+ <td>App Title:</td>
+ <td>Kids puzzle: Identify Turtles</td>
+ </tr>
+ <tr>
+ <td style="white-space:nowrap;">App Description:</td>
+ <td>
+ <p>This is the perfect app to have a good time with your children. It
+ is designed to help kids learn different species of turtles through
+ cute pictures and amusing puzzle games.</p>
+ <p>The rules of Kids puzzle: Identify Turtles are quite simple. Have
+ your child drag images around the screen to fit them into the shaded
+ region. Phonics is also utilized, as a child can also tap the word
+ below the image and hear the name pronounced.</p>
+ </td>
+ </tr>
+ </table>
+</div>
+
+<p>
+ The sections below highlight common types of policy violations in an app
+ title and description, illustrated with variations on the best practice
+ example.
+</p>
+
+<h3 id="repetitive-keywords">Repetitive keywords</h3>
+
+<p>
+ Your app description should not include keywords that are repetitive or excessive.
+</p>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">Description includes repetitive keywords</div>
+ <table>
+ <tr>
+ <td>App Title:</td>
+ <td>Kids puzzle: Identify Turtles</td>
+ </tr>
+ <tr>
+ <td style="white-space:nowrap;">App Description:</td>
+ <td>
+ <p>This is the perfect app to have a good time with your children. It is
+ designed to help kids learn different species of turtles through cute
+ pictures and amusing puzzle games.</p>
+ <p>The rules of Kids puzzle: Identify Turtles are quite simple. Have your
+ child drag images around the screen to fit them into the shaded region.
+ Phonics is also utilized, as a child can also tap the word below the image
+ and hear the name pronounced.</p>
+ <p style="border:2px solid red;">KEYWORDS: game, games, fun, funny, child,
+ children, kid, kids, puzzle, puzzle games, sound, turtle, turtles, sea turtles,
+ turtles, turtle, turtles, tortoise, tortoises, tortoise, tortoise, turtles,
+ turtles, turtles, turtles, tortoises, tortoise</p>
+ </td>
+ </tr>
+ </table>
+</div>
+
+<h3 id="unrelated-keywords">Unrelated keywords or references</h3>
+
+<p>
+ The description should not be loaded with irrelevant keywords in an attempt
+ to manipulate ranking or relevancy in Google Play search results.
+</p>
+
+<p>
+ For example, if your app has nothing to do with Lady Gaga, then she shouldn’t
+ be included in your description. Also, do not add highly searched, irrelevant
+ keywords that are unrelated to the function of the app. This is in breach of
+ policy.
+</p>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">Description includes unrelated keywords or references</div>
+ <table>
+ <tr>
+ <td>App Title:</td>
+ <td>Kids puzzle: Identify Turtles</td>
+ </tr>
+ <tr>
+ <td style="white-space:nowrap;">App Description:</td>
+ <td>
+ <p>This is the perfect app to have a good time with your children. It is designed to
+ help kids learn different species of turtles through cute pictures and amusing puzzle
+ games.</p>
+ <p>The rules of Kids puzzle: Identify Turtles are quite simple. Have your child drag
+ images around the screen to fit them into the shaded region. Phonics is also utilized,
+ as a child can also tap the word below the image and hear the name pronounced.</p>
+ <p style="border:2px solid red;">This game is as addictive as Angry Birds, more social
+ than Facebook and Twitter, and has a soundtrack reminiscent of Katy Perry and Lady
+ Gaga.</p>
+ <p style="border:2px solid red;">KEYWORDS: Angry Birds, Facebook, Twitter, Katy Perry,
+ Lady Gaga</p>
+ </td>
+ </tr>
+ </table>
+</div>
+
+<h3 id="excessive-detail">Excessive detail, references to your other apps</h3>
+
+<p>
+ Your app description should avoid excessive detail and references to your
+ other apps or products. For example, you should not list all of the details
+ of content included in the app or its various components, as shown in the
+ example below. Also, the description should not include any references to
+ other apps you’ve published.
+</p>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">Description includes excessive detail, references to your other apps</div>
+ <table>
+ <tr>
+ <td>App Title:</td>
+ <td>Kids puzzle: Identify Turtles</td>
+ </tr>
+ <tr>
+ <td style="white-space:nowrap;">App Description:</td>
+ <td>
+ <p>This is the perfect app to have a good time with your children. It is designed
+ to help kids learn different species of turtles through cute pictures and amusing
+ puzzle games.</p>
+ <p>The rules of Kids puzzle: Identify Turtles are quite simple. Have your child
+ drag images around the screen to fit them into the shaded region. Phonics is also
+ utilized, as a child can also tap the word below the image and hear the name
+ pronounced.</p>
+ <p style="border:2px solid red;">Turtles included in the app: Alligator
+ Snapping Turtle, Asian Box Turtle, Bog Turtle, Common Musk Turtle, Common Snapping
+ Turtle, Diamondback Terrapin, Eastern Box Turtle, Eastern Mud Turtle, Eastern Painted
+ Turtle, False Map Turtle, Florida Pond Cooter, Florida Softshell Turtle, Green Sea
+ Turtle, Map Turtle, Matamata Ornate Box Turtle, Red-bellied Side-necked Turtle,
+ Red-eared Slider, Smooth Softshell Turtle, Spiny Softshell Turtle, Spotted Turtle,
+ Western Painted Turtle, Wood Turtle, Yellow-bellied Slider</p>
+ <p style="border:2px solid red;">If you like this app try our other free apps:<br />
+ ★ Fun Zoo<br />
+ ★ CD Guns<br />
+ ★ Dessert House<br />
+ ★ Playground<br />
+ ★ 578 Weapons</p>
+ </td>
+ </tr>
+ </table>
+</div>
+
+
+<h2 id="ratings">Spam in Ratings and Reviews</h2>
+
+<p>
+ Ratings and reviews are benchmarks of app quality and users depend on them to
+ be authentic and relevant. As an app developer, you should not attempt to
+ artificially influence your app's ratings and reviews or those of your
+ competitor, such as by posting fake ratings or reviews or including spam
+ content in app reviews. The sections below provide guidelines for rating and
+ reviewing apps.
+</p>
+
+<p>
+ So that you can stay in touch with any issues that users are having with your
+ app, you should read through your ratings and reviews on a regular basis. If
+ you choose to reply to reviews, make sure to keep your reply focused on the
+ actual issues raised in the user's comments and do not ask for a higher
+ rating.
+</p>
+
+<p>
+ If you see an app or developer reply that doesn’t follow these guidelines,
+ you can report it. See <a href=
+ "http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=113417&topic=2364761&ctx=topic"
+ target="_policies">Inappropriate content in comments and applications</a> for
+ more information.
+</p>
+
+<div class="example-block bad" style="width:440px;">
+ <div class="heading">Inappropriate content in a review</div>
+ <img src="{@docRoot}images/gp-policy-spam-negreview.png">
+</div>
+
+<div class="example-block bad" style="margin-top:3em;">
+ <div class="heading">Soliciting ratings</div>
+ <img src="{@docRoot}images/gp-policy-spam-reqrating.png">
+</div>
+
+<h3 id="fake-ratings">Fake or inappropriate ratings and reviews</h3>
+
+<p>
+ To help ensure the quality of ratings and reviews, Google Play policies limit
+ the ways that individuals can use ratings and reviews. In particular, note
+ that it is a violation of policy to use ratings and reviews to influence the
+ placement of any app in Google Play.
+</p>
+
+<p>
+ As an app developer, make sure that you follow these guidelines:
+</p>
+
+<ul>
+ <li>
+ <strong>Don't try to manipulate ratings</strong>&mdash;Do not engage in
+ attempts to manipulate the ratings, reviews, or ranking of your apps,
+ either directly or indirectly, or by manipulating the ratings of your
+ competitors. Do not attempt to artificially boost reviews, ratings, or
+ installs through any means.
+ </li>
+
+ <li>
+ <strong>Don't solicit ratings through incentives</strong>&mdash;Do not
+ offer users any incentives to rate your app, such as offering rewards of
+ any kind or tying app functionality to rating.
+ </li>
+
+ <li>
+ <strong>Don't rate apps multiple times</strong>&mdash;Do not review or
+ rate any app multiple times in an attempt to influence its placement in
+ Google Play.
+ </li>
+
+ <li>
+ <strong>Don't add improper content to reviews</strong>&mdash;Do not
+ include affiliate, coupon, game codes, email addresses, or links to
+ websites or other apps in your reviews. If you are responding to a user
+ review, feel free to include references to helpful resources such as a
+ support address or FAQ page.
+ </li>
+</ul>
+
+<h3 id="solicited-ratings">Soliciting ratings from users</h3>
+
+<p>
+ In general, <strong>do not offer incentives for ratings</strong>. You should
+ not offer users incentives of any kind for rating your app (or any other app)
+ on Google Play, and you should not tie your app's functionality or content to
+ rating in any way.
+</p>
+
+<p>
+ It's acceptable to ask users to rate your app without incentives, for
+ example: "If you like this game, rate us in Google Play!" On the other hand,
+ it's a policy violation to ask users to rate your app based on incentives,
+ for example: "Rate this app and get 500 coins" or "Rate this app 5 stars and
+ get you 500 coins!"
+</p>
+
+
+<h2 id="webview-spam" style="clear:right">Spam in WebViews</h2>
+
+<p>
+ Apps published on Google Play should provide their own content. Do not
+ publish an app whose primary function is to reproduce or frame someone else’s
+ website (unless you have permission).
+</p>
+
+<p>
+ Similarly, do not publish an app whose primary function is to drive affiliate
+ traffic to a website. Although affiliate deals can exist where an app's
+ primary purpose is delivering its own content or functionality, it's a
+ violation of Google Play policies to publish an app whose primary (or
+ only) purpose is to direct affiliate traffic to another website.
+</p>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">WebView spam</div>
+ <table>
+ <tr>
+ <td>App Title:</td>
+ <td>Kids puzzle: Desktop Browser for Turtoogle Game</td>
+ </tr>
+ <tr>
+ <td>Developer:</td>
+ <td>AAZZZ <span style="border:2px solid red;">(not affiliated with Turtoogle
+ Inc.)</span></td>
+ </tr>
+ <tr>
+ <td style="white-space:nowrap;">App Description:</td>
+ <td>
+ <p>Have you ever wanted to use the full, desktop web version of Turtoogle
+ Game from your phone or tablet instead of the Turtoogle Game mobile app
+ or Turtoogle Game mobile web site?</p>
+ <p style="border:2px solid red;">This app lets you access Turtoogle Game
+ on your Android device in the same way as you access the game on your
+ desktop computer, and with all the same Turtoogle Game features.</p>
+ </td>
+ </tr>
+ </table>
+</div>
+
+
+<h2 id="wizard-spam">Spam from Wizards</h2>
+
+<p>
+ Apps that are created by an automated tool or wizard service must not be
+ submitted to Google Play by the operator of that service on behalf of other
+ persons. Such tools often produce too many duplicative or low-quality
+ apps which crowd the higher-quality apps in the Play Store.
+</p>
+
+<p>
+ Please be advised that apps created by an automated tool are only permissible
+ if the app end-product complies with Google Play policies and is published in
+ the Play Store through a developer account that is registered and owned by
+ you.
+</p>
+
+
+<h2 id="message-spam">Spam in Messaging</h2>
+
+<p>
+ Your app may not send SMS, email, or other messages on behalf of the user
+ without providing the user with the ability to confirm the content and intended
+ recipient.
+</p>
+
+<p>
+ Google Play will aggressively remove applications that are found to send or
+ modify SMS messages without user knowledge or consent.
+</p> \ No newline at end of file
diff --git a/docs/html/google/play/billing/v2/api.jd b/docs/html/google/play/billing/v2/api.jd
index 9d3a045..7e386a2 100644
--- a/docs/html/google/play/billing/v2/api.jd
+++ b/docs/html/google/play/billing/v2/api.jd
@@ -1,4 +1,5 @@
page.title=In-app Billing Version 2
+excludeFromSuggestions=true
@jd:body
<div style="background-color:#fffdeb;width:100%;margin-bottom:1em;padding:.5em;">In-app Billing Version 2 is superseded. Please <a href="{@docRoot}google/play/billing/billing_overview.html#migration">migrate to Version 3</a> at your earliest convenience.</div>
diff --git a/docs/html/google/play/billing/v2/billing_integrate.jd b/docs/html/google/play/billing/v2/billing_integrate.jd
index defe265..1581315 100644
--- a/docs/html/google/play/billing/v2/billing_integrate.jd
+++ b/docs/html/google/play/billing/v2/billing_integrate.jd
@@ -1,4 +1,5 @@
page.title=Implementing In-app Billing <span style="font-size:16px;">(IAB Version 2)</span>
+excludeFromSuggestions=true
@jd:body
<div style="background-color:#fffdeb;width:100%;margin-bottom:1em;padding:.5em;">In-app Billing Version 2 is superseded. Please <a href="{@docRoot}google/play/billing/billing_overview.html#migration">migrate to Version 3</a> at your earliest convenience.</div>
diff --git a/docs/html/google/play/billing/v2/billing_reference.jd b/docs/html/google/play/billing/v2/billing_reference.jd
index 84576bc..a946295 100644
--- a/docs/html/google/play/billing/v2/billing_reference.jd
+++ b/docs/html/google/play/billing/v2/billing_reference.jd
@@ -1,4 +1,5 @@
page.title=In-app Billing Reference <span style="font-size:16px;">(IAB Version 2)</span>
+excludeFromSuggestions=true
@jd:body
<div style="background-color:#fffdeb;width:100%;margin-bottom:1em;padding:.5em;">In-app Billing Version 2 is superseded. Please <a href="{@docRoot}google/play/billing/billing_overview.html#migration">migrate to Version 3</a> at your earliest convenience.</div>
diff --git a/docs/html/google/play/billing/v2/billing_subscriptions.jd b/docs/html/google/play/billing/v2/billing_subscriptions.jd
index 5e3bd28..3bcf212 100644
--- a/docs/html/google/play/billing/v2/billing_subscriptions.jd
+++ b/docs/html/google/play/billing/v2/billing_subscriptions.jd
@@ -1,4 +1,5 @@
page.title=Implementing Subscriptions <span style="font-size:16px;">(IAB Version 2)</span>
+excludeFromSuggestions=true
@jd:body
<div style="background-color:#fffdeb;width:100%;margin-bottom:1em;padding:.5em;">In-app Billing Version 2 is superseded. Please <a href="{@docRoot}google/play/billing/billing_overview.html#migration">migrate to Version 3</a> at your earliest convenience.</div>
diff --git a/docs/html/guide/basics/appmodel.jd b/docs/html/guide/basics/appmodel.jd
deleted file mode 100644
index 323fc9b..0000000
--- a/docs/html/guide/basics/appmodel.jd
+++ /dev/null
@@ -1,261 +0,0 @@
-page.title=Application Model
-@jd:body
-<h1>Android Application Model: Applications, Tasks, Processes, and Threads</h1>
-
-<p>In most operating systems, there is a strong 1-to-1 correlation between
-the executable image (such as the .exe on Windows) that an application lives in,
-the process it runs in, and the icon and application the user interacts with.
-In Android these associations are much more fluid, and it is important to
-understand how the various pieces can be put together.</p>
-
-<p>Because of the flexible nature of Android applications, there is some
-basic terminology that needs to be understood when implementing the
-various pieces of an application:</p>
-
-<ul>
-<li><p>An <strong>android package</strong> (or <strong>.apk</strong> for short)
-is the file containing an application's code and its resources. This is the
-file that an application is distributed in and downloaded by the user when
-installing that application on their device.</p></li>
-
-<li><p>A <strong>task</strong> is generally what the user perceives as
-an "application" that can be launched: usually a task has an icon in the
-home screen through which it is accessed, and it is available as a top-level
-item that can be brought to the foreground in front of other
-tasks.</p></li>
-
-<li><p>A <strong>process</strong> is a low-level kernel process in which
-an application's code is running. Normally all of the code in a
-.apk is run in one, dedicated process for that .apk; however, the
-{@link android.R.styleable#AndroidManifestApplication_process process} tag
-can be used to modify where that code is run, either for
-{@link android.R.styleable#AndroidManifestApplication the entire .apk}
-or for individual
-{@link android.R.styleable#AndroidManifestActivity activity},
-{@link android.R.styleable#AndroidManifestReceiver receiver},
-{@link android.R.styleable#AndroidManifestService service}, or
-{@link android.R.styleable#AndroidManifestProvider provider}, components.</p></li>
-</ul>
-
-<h2 id="Tasks">Tasks</h2>
-
-<p>A key point here is: <em>when the user sees as an "application," what
-they are actually dealing with is a task</em>. If you just create a .apk
-with a number of activities, one of which is a top-level entry point (via
-an {@link android.R.styleable#AndroidManifestIntentFilter intent-filter} for
-the action <code>android.intent.action.MAIN</code> and
-category <code>android.intent.category.LAUNCHER</code>), then there will indeed
-be one task created for your .apk, and any activities you start from there
-will also run as part of that task.</p>
-
-<p>A task, then, from the user's perspective your application; and from the
-application developer's perspective it is one or more activities the user
-has traversed through in that task and not yet closed, or an activity stack.
-A new task is created by
-starting an activity Intent with the {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK
-Intent.FLAG_ACTIVITY_NEW_TASK} flag; this Intent will be used as the root Intent of
-the task, defining what task it is. Any activity started without this flag
-will run in the same task as the activity that is starting it (unless that
-activity has requested a special launch mode, as discussed later). Tasks can
-be re-ordered: if you use FLAG_ACTIVITY_NEW_TASK but there is already a task
-running for that Intent, the current task's activity stack will be brought
-to the foreground instead of starting a new task.</p>
-
-<p>FLAG_ACTIVITY_NEW_TASK must only be used with care: using it says that,
-from the user's perspective, a new application starts at this point. If this
-is not the behavior you desire, you should not be creating a new task. In
-addition, you should only use the new task flag if it is possible for the user
-to navigate from home back to where they are and launch the same Intent as a
-new task. Otherwise, if the user presses HOME instead of BACK from the task
-you have launched, your task and its activities will be ordered behind the
-home screen without a way to return to them.</p>
-
-<h3>Task Affinities</h3>
-
-<p>In some cases Android needs to know which task an activity belongs to even when
-it is not being launched in to a specific task. This is accomplished through
-task affinities, which provide a unique static name for the task that one or more
-activities are intended to run in. The default task affinity for an activity
-is the name of the .apk package name the activity is implemented in. This
-provides the normally expected behavior, where all of the activities in a
-particular .apk are part of a single application to the user.</p>
-
-<p>When starting a new activity without the
-{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK
-Intent.FLAG_ACTIVITY_NEW_TASK} flag, task affinities have no impact on the
-task the new activity will run in: it will always run in the task of the
-activity that is starting it. However, if the NEW_TASK flag is being used,
-then the affinity will be used to determine if a task already exists with
-the same affinity. If so, that task will be brought to the front and the
-new activity launched at the top of that task.</p>
-
-<p>This behavior is most useful for situations where you must use the
-NEW_TASK flag, in particular launching activities from status bar notifications
-or home screen shortcuts. The result is that, when the user launches your
-application this way, its current task state will be brought to the foreground,
-and the activity they now want to look at placed on top of it.</p>
-
-<p>You can assign your own task affinities in your manifest's
-{@link android.R.styleable#AndroidManifestApplication application} tag for
-all activities in the .apk, or the
-{@link android.R.styleable#AndroidManifestActivity activity} tag of
-individual activities. Some examples of how this can be used are:</p>
-
-<ul>
-<li>If your .apk contains multiple top-level applications that the user can
-launch, then you will probably want to assign different affinities to each
-of the activities that the users sees for your .apk. A good convention for
-coming up with distinct names is to append your .apk's package name with
-a colon separated string. For example, the "com.android.contacts" .apk
-may have the affinities "com.android.contacts:Dialer" and
-"com.android.contacts:ContactsList".</ul>
-<li>If you are replacing a notification, shortcut, or other such "inner"
-activity of an application that can be launched from outside of it, you may
-need to explicitly set the taskAffinity of your replacement activity to be
-the same as the application you are replacing. For example, if you are
-replacing the contacts details view (which the user can make and invoke
-shortcuts to), you would want to set the taskAffinity to
-"com.android.contacts".</li>
-</ul>
-
-<h3>Launch Modes and Launch Flags</h3>
-
-<p>The main way you control how activities interact with tasks is through
-the activity's
-{@link android.R.styleable#AndroidManifestActivity_launchMode launchMode}
-attribute and the {@link android.content.Intent#setFlags flags} associated
-with an Intent. These two parameters can work together in various ways
-to control the outcome of the activity launch, as described in their
-associated documentation. Here we will look at some common use cases and
-combinations of these parameters.</p>
-
-<p>The most common launch mode you will use (besides the default
-<code>standard</code> mode) is <code>singleTop</code>. This does not have
-an impact on tasks; it just avoids starting the same activity multiple times
-on the top of a stack.
-
-<p>The <code>singleTask</code> launch mode has a major
-impact on tasks: it causes the activity to always be started in
-a new task (or its existing task to be brought to the foreground). Using
-this mode requires a lot of care in how you interact with the rest of the
-system, as it impacts every path in to the activity. It should only be used
-with activities that are front doors to the application (that is, which
-support the MAIN action and LAUNCHER category).</p>
-
-<p>The <code>singleInstance</code> launch mode is even more specialized, and
-should only be used in applications that are implemented entirely as one
-activity.</p>
-
-<p>A situation you will often run in to is when another entity (such as the
-{@link android.app.SearchManager} or {@link android.app.NotificationManager})
-starts one of your activities. In this case, the
-{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK
-Intent.FLAG_ACTIVITY_NEW_TASK} flag must be used, because the activity is
-being started outside of a task (and the application/task may not even
-exist). As described previously, the standard behavior in this situation
-is to bring to the foreground the current task matching the new activity's
-affinity and start the new activity at the top of it. There are, however,
-other types of behavior that you can implement.</p>
-
-<p>One common approach is to also use the
-{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP
-Intent.FLAG_ACTIVITY_CLEAR_TOP} flag in conjunction with NEW_TASK. By doing so,
-if your task is already running, then it will be brought to the foreground,
-all of the activities on its stack cleared except the root activity, and the
-root activity's {@link android.app.Activity#onNewIntent} called with the
-Intent being started. Note that the activity often also use the <code>singleTop</code>
-or <code>singleTask</code> launch mode when using this approach, so that
-the current instance is given the new intent instead of requiring that it
-be destroyed and a new instance started.</p>
-
-<p>Another approach you can take is to set the notification activity's
-<code>android:taskAffinity</code> to the empty string "" (indicating no affinity)
-and setting the
-<code>{@link android.R.styleable#AndroidManifestActivity_noHistory
-android:noHistory}</code> and
-<code>{@link android.R.styleable#AndroidManifestActivity_excludeFromRecents
-android:excludeFromRecents}</code> attributes.
-This approach is useful if you would like the notification
-to take the user to a separate activity describing it, rather than return
-to the application's task. By specifying these attributes, the activity will
-be finished whether the user leaves it with BACK or HOME and it will not
-show up in the recent tasks list; if the <code>noHistory</code> attribute
-isn't specified, pressing HOME will result in the activity and its task
-remaining in the system, possibly with no way to return to it.</p>
-
-<p>Be sure to read the documentation on the
-{@link android.R.styleable#AndroidManifestActivity_launchMode launchMode attribute}
-and the {@link android.content.Intent#setFlags Intent flags} for the details
-on these options.</p>
-
-<h2 id="Processes">Processes</h2>
-
-<p>In Android, processes are entirely an implementation detail of applications
-and not something the user is normally aware of. Their main uses are simply:</p>
-
-<ul>
-<li> Improving stability or security by putting untrusted or unstable code
-into another process.
-<li> Reducing overhead by running the code of multiple .apks in the same
-process.
-<li> Helping the system manage resources by putting heavy-weight code in
-a separate process that can be killed independently of other parts of the
-application.
-</ul>
-
-<p>As described previously, the
-{@link android.R.styleable#AndroidManifestApplication_process process} attribute
-is used to control the process that particular application components run in.
-Note that this attribute can not be used to violate security of the system: if
-two .apks that are not sharing the same user ID try to run in the same process,
-this will not be allowed and different distinct processes will be created for
-each of them.</p>
-
-<p>See the <a href="{@docRoot}devel/security.html">security</a> document for
-more information on these security restrictions.</p>
-
-<h2 id="Threads">Threads</h2>
-
-<p>Every process has one or more threads running in it. In most situations, Android
-avoids creating additional threads in a process, keeping an application
-single-threaded unless it creates its own threads. An important repercussion
-of this is that all calls to {@link android.app.Activity},
-{@link android.content.BroadcastReceiver}, and {@link android.app.Service}
-instances are made only from the main thread of the process they are running in.</p>
-
-<p>Note that a new thread is <strong>not</strong> created for each
-Activity, BroadcastReceiver, Service, or ContentProvider instance:
-these application components are instantiated in the desired process (all in the
-same process unless otherwise specified), in the main thread of that process.
-This means that none of these components (including services) should perform
-long or blocking operations (such as networking calls or computation loops)
-when called by the system, since this will block
-all other components in the process. You can use the standard library
-{@link java.lang.Thread} class or Android's {@link android.os.HandlerThread}
-convenience class to perform long operations on another thread.</p>
-
-<p>There are a few important exceptions to this threading rule:</p>
-
-<ul>
-<li><p>Calls on to an {@link android.os.IBinder} or interface implemented on
-an IBinder are dispatched from the thread calling them or a thread pool in the
-local process if coming from another process, <em>not</em>
-from the main thread of their process. In particular, calls on to the IBinder
-of a {@link android.app.Service} will be called this way. (Though
-calls to methods on Service itself are done from the main thread.)
-This means that <em>implementations of IBinder interfaces must always be
-written in a thread-safe way, since they can be called from any number of
-arbitrary threads at the same time</em>.</p></li>
-
-<li><p>Calls to the main methods of {@link android.content.ContentProvider}
-are dispatched from the calling thread or main thread as with IBinder. The
-specific methods are documented in the ContentProvider class.
-This means that <em>implementations of these methods must always be
-written in a thread-safe way, since they can be called from any number of
-arbitrary threads at the same time</em>.</p></li>
-
-<li><p>Calls on {@link android.view.View} and its subclasses are made from the
-thread that the view's window is running in. Normally this will be the main
-thread of the process, however if you create a thread and show a window from
-there then the window's view hierarchy will be called from that thread.</p></li>
-</ul>
diff --git a/docs/html/guide/basics/building-blocks.jd b/docs/html/guide/basics/building-blocks.jd
deleted file mode 100644
index b8a609e..0000000
--- a/docs/html/guide/basics/building-blocks.jd
+++ /dev/null
@@ -1,76 +0,0 @@
-page.title=Building Blocks
-@jd:body
-<h1>Android Building Blocks</h1>
-
-<p>You can think of an Android application as a collection of components, of
-various kinds. These components are for the most part quite loosely coupled,
-to the degree where you can accurately describe them as a federation of
-components rather than a single cohesive application.</p>
-
-<p>Generally, these components all run in the same system process. It's
-possible (and quite common) to create multiple threads within that process,
-and it's also possible to create completely separate child processes if you
-need to. Such cases are pretty uncommon though, because Android tries very
-hard to make processes transparent to your code.</p>
-
-<p>These are the most important parts of the Android APIs:</p>
-
-<dl>
- <dt><a href="{@docRoot}devel/bblocks-manifest.html">AndroidManifest.xml</a></dt>
- <dd>The AndroidManifest.xml file is the control file that tells the system
- what to do with all the top-level components (specifically activities,
- services, intent receivers, and content providers described below)
- you've created. For instance, this is the
- "glue" that actually specifies which Intents your Activities receive.</dd>
-
- <dt>{@link android.app.Activity Activities}</dt>
- <dd>An Activity is, fundamentally, an object that has a life cycle. An
- Activity is a chunk of code that does some work; if necessary, that work
- can include displaying a UI to the user. It doesn't have to, though - some
- Activities never display UIs. Typically, you'll designate one of your
- application's Activities as the entry point to your application. </dd>
-
-
- <dt>{@link android.view.View Views}</dt>
- <dd>A View is an object that knows how to draw itself to the screen.
- Android user interfaces are comprised of trees of Views. If you want to
- perform some custom graphical technique (as you might if you're writing a
- game, or building some unusual new user interface widget) then you'd
- create a View.</dd>
-
-
- <dt>{@link android.content.Intent Intents}</dt>
- <dd>An Intent is a simple message object that represents an "intention" to
- do something. For example, if your application wants to display a web
- page, it expresses its "Intent" to view the URI by creating an Intent
- instance and handing it off to the system. The system locates some other
- piece of code (in this case, the Browser) that knows how to handle that
- Intent, and runs it. Intents can also be used to broadcast interesting
- events (such as a notification) system-wide.</dd>
-
-
- <dt>{@link android.app.Service Services}</dt>
- <dd>A Service is a body of code that runs in the background. It can run in
- its own process, or in the context of another application's process,
- depending on its needs. Other components "bind" to a Service and invoke
- methods on it via remote procedure calls. An example of a Service is a
- media player; even when the user quits the media-selection UI, she
- probably still intends for her music to keep playing. A Service keeps the
- music going even when the UI has completed.</dd>
-
-
- <dt>{@link android.app.NotificationManager Notifications}</dt>
- <dd>A Notification is a small icon that appears in the status bar. Users
- can interact with this icon to receive information. The most well-known
- notifications are SMS messages, call history, and voicemail, but
- applications can create their own. Notifications are the
- strongly-preferred mechanism for alerting the user of something that needs
- their attention.</dd>
-
- <dt>{@link android.content.ContentProvider ContentProviders}</dt>
- <dd>A ContentProvider is a data storehouse that provides access to data on
- the device; the classic example is the ContentProvider that's used to
- access the user's list of contacts. Your application can access data that
- other applications have exposed via a ContentProvider, and you can also
- define your own ContentProviders to expose data of your own.</dd>
-</dl>
diff --git a/docs/html/guide/basics/fixme-gs-core-packages.jd b/docs/html/guide/basics/fixme-gs-core-packages.jd
deleted file mode 100644
index 5281990..0000000
--- a/docs/html/guide/basics/fixme-gs-core-packages.jd
+++ /dev/null
@@ -1,92 +0,0 @@
-page.title=Getting Started
-@jd:body
-<h1>Getting Started with Android</h1>
-
-<p>To get started with Android, please read the following sections first:</p>
-<dl>
- <dt><a href="{@docRoot}intro/installing.html">Installing the SDK and
- Plugin</a></dt>
- <dd>How to install the Android SDK and Eclipse plugin.</dd>
- <dt><a href="{@docRoot}intro/develop-and-debug.html">Developing and Debugging</a></dt>
- <dd>An introduction to developing and debugging Android applications in Eclipse,
- plus information on using other IDEs.</dd>
- <dt><a href="{@docRoot}intro/hello-android.html">Hello Android</a></dt>
- <dd>Writing your first Android Application, the ever popular Hello World,
- Android style.</dd>
- <dt><a href="{@docRoot}intro/anatomy.html">Anatomy of an App</a></dt>
- <dd>A guide to the structure and architecture of an Android
- Application. This guide will help you understand the pieces that make up
- an Android app.</dd>
- <dt><a href="{@docRoot}intro/tutorial.html">Notepad Tutorial</a></dt>
- <dd>This tutorial document will lead you through
- constructing a real Android Application: A notepad which can create, edit
- and delete notes, and covers many of the basic concepts with practical
- examples.</dd>
- <dt><a href="{@docRoot}intro/tools.html">Development Tools</a></dt>
- <dd>The
- command line tools included with the SDK, what they do, and how to use
- them.</dd>
- <dt><a href="{@docRoot}intro/appmodel.html">Application Model</a></dt>
- <dd>A guide to Applications, Tasks, Processes, and Threads.
- These are the elements that define the way your application is run by the
- system and presented to the user.</dd>
- <dt><a href="{@docRoot}intro/lifecycle.html">Application Life Cycle</a></dt>
- <dd>The important life-cycle details for
- Applications and the Activities running inside of them.</dd>
-
-</dl>
-
-<h2>Other Introductory Material</h2>
-<p>After reading the sections above, the following Getting Started information is also very useful:</p>
-
-
-<h3>Core Packages</h3>
-<p> These are the basic packages that make up the Android SDK for writing
-applications. The packages are organized as layers, listed here from
-lowest-level to highest.</p>
-
-<dl>
- <dt>{@link android.util}</dt>
- <dd>contains various low-level utility classes, such
- as specialized container classes, XML utilities, etc.</dd>
- <dt>{@link android.os}</dt>
- <dd> provides basic operating system services, message
- passing, and inter-process communication.</dd>
- <dt>{@link android.graphics}</dt><dd>is the core rendering package.</dd>
- <dt>{@link android.text}, {@link android.text.method}, {@link
- android.text.style}, and {@link android.text.util} </dt>
- <dd>supply a rich set of
- text processing tools, supporting rich text, input methods, etc.</dd>
- <dt>{@link android.database}</dt>
- <dd>contains low-level APIs for working with
- databases.</dd>
- <dt>{@link android.content}</dt>
- <dd>provides various services for accessing data
- on the device: applications installed on the device and their associated
- resources, and content providers for persistent dynamic data.</dd>
- <dt>{@link android.view}</dt>
- <dd>is the core user-interface framework.</dd>
- <dt>{@link android.widget}</dt>
- <dd>supplies standard user interface elements
- (lists, buttons, layout managers, etc) built from the view package.</dd>
- <dt>{@link android.app}</dt>
- <dd>provides the high-level application model,
- implemented using Activities.</dd>
-</dl>
-
-<h3>Other Notable Packages</h3>
-
-<p> These packages provide additional domain-specific features of the Android
-platform. They are not necessary for basic application development.</p>
-
-<dl>
- <dt>{@link android.provider}</dt>
- <dd>contains definitions for various standard
- content providers included with the platform.</dd>
- <dt>{@link android.telephony}</dt>
- <dd>provides APIs for interacting with the
- device's phone stack.</dd>
- <dt>{@link android.webkit}</dt>
- <dd>includes various APIs for working with
- web-based content.</dd>
-</dl>
diff --git a/docs/html/guide/basics/index.html b/docs/html/guide/basics/index.html
deleted file mode 100644
index 4881acf..0000000
--- a/docs/html/guide/basics/index.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<html>
-<head>
-<meta http-equiv="refresh" content="0;url=../index.html">
-</head>
-<body>
-<a href="../index.html">click here</a> if you are not redirected.
-</body>
-</html> \ No newline at end of file
diff --git a/docs/html/guide/components/processes-and-threads.jd b/docs/html/guide/components/processes-and-threads.jd
index 07a2667..1fed712 100644
--- a/docs/html/guide/components/processes-and-threads.jd
+++ b/docs/html/guide/components/processes-and-threads.jd
@@ -1,4 +1,6 @@
page.title=Processes and Threads
+page.tags="lifecycle","background"
+
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/admin/device-admin.jd b/docs/html/guide/topics/admin/device-admin.jd
index f917576..a474498 100644
--- a/docs/html/guide/topics/admin/device-admin.jd
+++ b/docs/html/guide/topics/admin/device-admin.jd
@@ -1,4 +1,5 @@
page.title=Device Administration
+page.tags="devicepolicymanager","policy","security"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/admin/keychain.jd b/docs/html/guide/topics/admin/keychain.jd
deleted file mode 100644
index 2ea2408..0000000
--- a/docs/html/guide/topics/admin/keychain.jd
+++ /dev/null
@@ -1,4 +0,0 @@
-page.title=Text and Input
-@jd:body
-
-<p>Add contnet here</p>
diff --git a/docs/html/guide/topics/appwidgets/index.jd b/docs/html/guide/topics/appwidgets/index.jd
index 93d6c6f..cdbf827 100644
--- a/docs/html/guide/topics/appwidgets/index.jd
+++ b/docs/html/guide/topics/appwidgets/index.jd
@@ -1,4 +1,5 @@
page.title=App Widgets
+page.tags="home"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/connectivity/bluetooth.jd b/docs/html/guide/topics/connectivity/bluetooth.jd
index 832b850..1c55d8b 100644
--- a/docs/html/guide/topics/connectivity/bluetooth.jd
+++ b/docs/html/guide/topics/connectivity/bluetooth.jd
@@ -1,4 +1,5 @@
page.title=Bluetooth
+page.tags="wireless","bluetoothadapter","bluetoothdevice"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/connectivity/sip.jd b/docs/html/guide/topics/connectivity/sip.jd
index a5f0e2e..526eb83 100644
--- a/docs/html/guide/topics/connectivity/sip.jd
+++ b/docs/html/guide/topics/connectivity/sip.jd
@@ -1,4 +1,5 @@
page.title=Session Initiation Protocol
+page.tags="sipmanager","sipprofile","sipaudiocall","telephony"
@jd:body
<div id="qv-wrapper">
<div id="qv">
diff --git a/docs/html/guide/topics/connectivity/wifip2p.jd b/docs/html/guide/topics/connectivity/wifip2p.jd
index efb3ac7..2167a0f 100644
--- a/docs/html/guide/topics/connectivity/wifip2p.jd
+++ b/docs/html/guide/topics/connectivity/wifip2p.jd
@@ -1,4 +1,5 @@
page.title=Wi-Fi Direct
+page.tags="wireless","WifiP2pManager"
@jd:body
diff --git a/docs/html/guide/topics/data/data-storage.jd b/docs/html/guide/topics/data/data-storage.jd
index 2603a06..385c116 100644
--- a/docs/html/guide/topics/data/data-storage.jd
+++ b/docs/html/guide/topics/data/data-storage.jd
@@ -1,4 +1,5 @@
page.title=Storage Options
+page.tags="database","sharedpreferences","sdcard"
@jd:body
diff --git a/docs/html/guide/topics/data/install-location.jd b/docs/html/guide/topics/data/install-location.jd
index 757cd19..2ec0d5a 100644
--- a/docs/html/guide/topics/data/install-location.jd
+++ b/docs/html/guide/topics/data/install-location.jd
@@ -1,4 +1,5 @@
page.title=App Install Location
+page.tags="sdcard","external"
@jd:body
diff --git a/docs/html/guide/topics/graphics/opengl.jd b/docs/html/guide/topics/graphics/opengl.jd
index 5630e63..469eae2 100644
--- a/docs/html/guide/topics/graphics/opengl.jd
+++ b/docs/html/guide/topics/graphics/opengl.jd
@@ -1,6 +1,5 @@
page.title=OpenGL
-parent.title=Graphics
-parent.link=index.html
+page.tags="games"
@jd:body
<div id="qv-wrapper">
@@ -138,7 +137,7 @@ calling OpenGL APIs using the following classes:</p>
<li>{@link android.opengl.GLES10}</li>
<li>{@link android.opengl.GLES10Ext}</li>
<li>{@link android.opengl.GLES11}</li>
- <li>{@link android.opengl.GLES10Ext}</li>
+ <li>{@link android.opengl.GLES11Ext}</li>
</ul>
</li>
<li>{@link javax.microedition.khronos.opengles} - This package provides the standard
@@ -289,13 +288,14 @@ matrices to the coordinates of objects that use this shader.
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
- // the coordinates of objects that use this vertex shader
+ // the coordinates of objects that use this vertex shader.
"uniform mat4 uMVPMatrix; \n" +
"attribute vec4 vPosition; \n" +
"void main(){ \n" +
-
- // the matrix must be included as part of gl_Position
+ // The matrix must be included as part of gl_Position
+ // Note that the uMVPMatrix factor *must be first* in order
+ // for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition; \n" +
"} \n";
diff --git a/docs/html/guide/topics/graphics/prop-animation.jd b/docs/html/guide/topics/graphics/prop-animation.jd
index 49d7bb8..22bf769 100644
--- a/docs/html/guide/topics/graphics/prop-animation.jd
+++ b/docs/html/guide/topics/graphics/prop-animation.jd
@@ -1,6 +1,5 @@
page.title=Property Animation
-parent.title=Animation
-parent.link=animation.html
+page.tags="valueanimator","objectanimator","layouttransition","ViewPropertyAnimator"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/graphics/renderscript/graphics.jd b/docs/html/guide/topics/graphics/renderscript/graphics.jd
deleted file mode 100644
index 58676ea..0000000
--- a/docs/html/guide/topics/graphics/renderscript/graphics.jd
+++ /dev/null
@@ -1,994 +0,0 @@
-page.title=Graphics
-parent.title=Renderscript
-parent.link=index.html
-
-@jd:body
-
- <div id="qv-wrapper">
- <div id="qv">
- <h2>In this document</h2>
-
- <ol>
- <li>
- <a href="#creating-graphics-rs">Creating a Graphics Renderscript</a>
- <ol>
- <li><a href="#creating-native">Creating the Renderscript file</a></li>
- <li><a href="#creating-entry">Creating the Renderscript entry point class</a></li>
- <li><a href="#creating-view">Creating the view class</a></li>
- <li><a href="#creating-activity">Creating the activity class</a></li>
- </ol>
- </li>
- <li>
- <a href="#drawing">Drawing</a>
- <ol>
- <li><a href="#drawing-rsg">Simple drawing</a></li>
- <li><a href="#drawing-mesh">Drawing with a mesh</a></li>
- </ol>
- </li>
- <li>
- <a href="#shaders">Shaders</a>
- <ol>
- <li><a href="#shader-bindings">Shader bindings</a></li>
- <li><a href="#shader-sampler">Defining a sampler</a></li>
- </ol>
- </li>
- <li>
- <a href="#fbo">Rendering to a Framebuffer Object</a>
- </li>
- </ol>
-
- <h2>Related Samples</h2>
-
- <ol>
- <li><a href="{@docRoot}resources/samples/RenderScript/Balls/index.html">Balls</a></li>
-
- <li><a href="{@docRoot}resources/samples/RenderScript/Fountain/index.html">Fountain</a></li>
-
- <li><a href="{@docRoot}resources/samples/RenderScript/FountainFbo/index.html">FountainFbo</a></li>
-
- <li><a href="{@docRoot}resources/samples/RenderScript/HelloWorld/index.html">Hello
-World</a></li>
-
- <li><a
-href="{@docRoot}resources/samples/RenderScript/MiscSamples/index.html">Misc Samples</a></li>
- </ol>
- </div>
- </div>
-
- <p>Renderscript provides a number of graphics APIs for rendering, both at the Android
- framework level as well as at the Renderscript runtime level. For instance, the Android framework APIs let you
- create meshes and define shaders to customize the graphical rendering pipeline. The native
- Renderscript graphics APIs let you draw the actual meshes to render your scene. You need to
- be familiar with both APIs to appropriately render graphics on an Android-powered device.</p>
-
- <h2 id="creating-graphics-rs">Creating a Graphics Renderscript</h2>
-
- <p>Renderscript applications require various layers of code, so it is useful to create the following
- files to help keep your application organized:</p>
-
- <dl>
- <dt>The Renderscript <code>.rs</code> file</dt>
-
- <dd>This file contains the logic to do the graphics rendering.</dd>
-
- <dt>The Renderscript entry point <code>.java</code> class</dt>
-
- <dd>This class allows the view class to interact with the code defined in the <code>.rs</code>
- file. This class contains a Renderscript object (instance of
- <code>ScriptC_<em>renderscript_file</em></code>), which allows your Android framework code to
- call the Renderscript code. In general, this class does much of the setup for Renderscript
- such as shader and mesh building and memory allocation and binding. The SDK samples follow the
- convention of naming this file ActivityRS.java,
- where Activity is the name of your main activity class.</dd>
-
- <dt>The view <code>.java</code> class</dt>
-
- <dd>This class extends {@link android.renderscript.RSSurfaceView} or {@link
- android.renderscript.RSTextureView} to provide a surface to render on. A {@link
- android.renderscript.RSSurfaceView} consumes a whole window, but a {@link
- android.renderscript.RSTextureView} allows you to draw Renderscript graphics inside of a
- view and add it to a {@link android.view.ViewGroup} alongside
- other views. In this class, you create a {@link android.renderscript.RenderScriptGL} context object
- with a call to {@link android.renderscript.RSSurfaceView#createRenderScriptGL
- RSSurfaceView.createRenderscriptGL()} or {@link android.renderscript.RSTextureView#createRenderScriptGL
- RSTextureView.createRenderscriptGL()}. The {@link android.renderscript.RenderScriptGL} context object
- contains information about the current rendering state of Renderscript such as the vertex and
- fragment shaders. You pass this context object to the Renderscript entry point class, so that
- class can modify the rendering context if needed and bind the Renderscript code to the context. Once bound,
- the view class can use the Renderscript code to display graphics.
- The view class should also implement callbacks for events inherited from {@link
- android.view.View}, such as {@link android.view.View#onTouchEvent onTouchEvent()} and {@link
- android.view.View#onKeyDown onKeyDown()} if you want to detect these types of user interactions.
- The SDK samples follow the convention of naming this file ActivityView.java,
- where Activity is the name of your main activity class</dd>
-
- <dt>The activity <code>.java</code> class</dt>
-
- <dd>This class is the main activity class and sets your {@link android.renderscript.RSSurfaceView} as the main content
- view for this activity or uses the {@link android.renderscript.RSTextureView} alongside other views.</dd>
- </dl>
- <p>Figure 1 describes how these classes interact with one another in a graphics Renderscript:</p>
-
- <img src="{@docRoot}images/rs_graphics.png">
- <p class="img-caption"><strong>Figure 1.</strong> Graphics Renderscript overview</p>
-
-
- <p>The following sections describe how to create an application that uses a graphics Renderscript by using
- the <a href="{@docRoot}resources/samples/RenderScript/Fountain/index.html">Renderscript Fountain
- sample</a> that is provided in the SDK as a guide (some code has been modified from its original
- form for simplicity).</p>
-
- <h3 id="creating-native">Creating the Renderscript file</h3>
-
- <p>Your Renderscript code resides in <code>.rs</code> and <code>.rsh</code> (headers) files in the
- <code>&lt;project_root&gt;/src/</code> directory. This code contains the logic to render your
- graphics and declares all other necessary items such as variables, structs,
- and pointers. Every graphics <code>.rs</code> file generally contains the following items:</p>
-
- <ul>
- <li>A pragma declaration (<code>#pragma rs java_package_name(<em>package.name</em>)</code>) that declares
- the package name of the <code>.java</code> reflection of this Renderscript.</li>
-
- <li>A pragma declaration (<code>#pragma version(1)</code>) that declares the version of Renderscript that
- you are using (1 is the only value for now).</li>
-
- <li>A <code>#include "rs_graphics.rsh"</code> declaration.</li>
-
- <li>A <code>root()</code> function. This is the main worker function for your Renderscript and
- calls Renderscript graphics functions to render scenes. This function is called every time a
- frame refresh occurs, which is specified as its return value. A <code>0</code> (zero) specified for
- the return value says to only render the frame when a property of the scene that you are
- rendering changes. A non-zero positive integer specifies the refresh rate of the frame in
- milliseconds.
-
- <p class="note"><strong>Note:</strong> The Renderscript runtime makes its best effort to
- refresh the frame at the specified rate. For example, if you are creating a live wallpaper
- and set the return value to 20, the Renderscript runtime renders the wallpaper at 50fps if it has just
- enough or more resources to do so. It renders as fast as it can if not enough resources
- are available.</p>
-
- <p>For more information on using the Renderscript graphics functions, see the <a href=
- "#drawing">Drawing</a> section.</p>
- </li>
-
- <li>An <code>init()</code> function. This allows you to do initialization of your
- Renderscript before the <code>root()</code> function runs, such as assigning values to variables. This
- function runs once and is called automatically when the Renderscript starts, before anything
- else in your Renderscript. Creating this function is optional.</li>
-
- <li>Any variables, pointers, and structures that you wish to use in your Renderscript code (can
- be declared in <code>.rsh</code> files if desired)</li>
- </ul>
-
- <p>The following code shows how the <code>fountain.rs</code> file is implemented:</p>
- <pre>
-#pragma version(1)
-
-// Tell which java package name the reflected files should belong to
-#pragma rs java_package_name(com.example.android.rs.fountain)
-
-//declare shader binding
-#pragma stateFragment(parent)
-
-// header with graphics APIs, must include explicitly
-#include "rs_graphics.rsh"
-
-static int newPart = 0;
-
-// the mesh to render
-rs_mesh partMesh;
-
-// the point representing where a particle is rendered
-typedef struct __attribute__((packed, aligned(4))) Point {
- float2 delta;
- float2 position;
- uchar4 color;
-} Point_t;
-Point_t *point;
-
-// main worker function that renders particles onto the screen
-int root() {
- float dt = min(rsGetDt(), 0.1f);
- rsgClearColor(0.f, 0.f, 0.f, 1.f);
- const float height = rsgGetHeight();
- const int size = rsAllocationGetDimX(rsGetAllocation(point));
- float dy2 = dt * (10.f);
- Point_t * p = point;
- for (int ct=0; ct &lt; size; ct++) {
- p-&gt;delta.y += dy2;
- p-&gt;position += p-&gt;delta;
- if ((p-&gt;position.y &gt; height) &amp;&amp; (p-&gt;delta.y &gt; 0)) {
- p-&gt;delta.y *= -0.3f;
- }
- p++;
- }
-
- rsgDrawMesh(partMesh);
- return 1;
-}
-
-// adds particles to the screen to render
-static float4 partColor[10];
-void addParticles(int rate, float x, float y, int index, bool newColor)
-{
- if (newColor) {
- partColor[index].x = rsRand(0.5f, 1.0f);
- partColor[index].y = rsRand(1.0f);
- partColor[index].z = rsRand(1.0f);
- }
- float rMax = ((float)rate) * 0.02f;
- int size = rsAllocationGetDimX(rsGetAllocation(point));
- uchar4 c = rsPackColorTo8888(partColor[index]);
-
- Point_t * np = &amp;point[newPart];
- float2 p = {x, y};
- while (rate--) {
- float angle = rsRand(3.14f * 2.f);
- float len = rsRand(rMax);
- np-&gt;delta.x = len * sin(angle);
- np-&gt;delta.y = len * cos(angle);
- np-&gt;position = p;
- np-&gt;color = c;
- newPart++;
- np++;
- if (newPart &gt;= size) {
- newPart = 0;
- np = &amp;point[newPart];
- }
- }
-}
-</pre>
-
- <h3 id="creating-entry">Creating the Renderscript entry point class</h3>
-
- <p>When you create a Renderscript (<code>.rs</code>) file, it is helpful to create a
- corresponding Android framework class that is an entry point into the <code>.rs</code> file.
- The most important thing this class does is receive a {@link android.renderscript.RenderScriptGL} rendering context
- object from the <a href="#creating-view">view class</a> and binds the actual Renderscript
- code to the rendering context. This notifies your view class of the code that it needs
- to render graphics.
- </p>
-
- <p>In addition, this class should contain all of the things needed to set up Renderscript.
- Some important things that you need to do in this class are:</p>
-
- <ul>
- <li>Create a Renderscript object
- <code>ScriptC_<em>rs_filename</em></code>. The Renderscript object is attached to the Renderscript bytecode, which is platform-independent and
- gets compiled on the device when the Renderscript application runs. The bytecode is referenced
- as a raw resource and is passed into the constructor for the Renderscript object.
- For example, this is how the <a href="{@docRoot}resources/samples/RenderScript/Fountain/index.html">Fountain</a>
- sample creates the Renderscript object:
- <pre>
- RenderScriptGL rs; //obtained from the view class
- Resources res; //obtained from the view class
- ...
- ScriptC_fountain mScript = new ScriptC_fountain(mRS, mRes, R.raw.fountain);
- </pre>
- </li>
- <li>Allocate any necessary memory and bind it to your Renderscript code via the Renderscript object.</li>
- <li>Build any necessary meshes and bind them to the Renderscript code via the Renderscript object.</li>
- <li>Create any necessary programs and bind them to the Renderscript code via the Renderscript object.</li>
- </ul>
-
- <p>The following code shows how the <a href=
- "{@docRoot}resources/samples/RenderScript/Fountain/src/com/example/android/rs/fountain/FountainRS.html">
- FountainRS</a> class is implemented:</p>
- <pre>
-package com.example.android.rs.fountain;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.util.Log;
-
-public class FountainRS {
- public static final int PART_COUNT = 50000;
-
- public FountainRS() {
- }
-
- /**
- * This provides us with the Renderscript context and resources
- * that allow us to create the Renderscript object
- */
- private Resources mRes;
- private RenderScriptGL mRS;
-
- // Renderscript object
- private ScriptC_fountain mScript;
-
- // Called by the view class to initialize the Renderscript context and renderer
- public void init(RenderScriptGL rs, Resources res) {
- mRS = rs;
- mRes = res;
-
- /**
- * Create a shader and bind to the Renderscript context
- */
- ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs);
- pfb.setVaryingColor(true);
- rs.bindProgramFragment(pfb.create());
-
- /**
- * Allocate memory for the particles to render and create the mesh to draw
- */
- ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT);
- Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
- smb.addVertexAllocation(points.getAllocation());
- smb.addIndexSetType(Mesh.Primitive.POINT);
- Mesh sm = smb.create();
-
- /**
- * Create and bind the Renderscript object to the Renderscript context
- */
- mScript = new ScriptC_fountain(mRS, mRes, R.raw.fountain);
- mScript.set_partMesh(sm);
- mScript.bind_point(points);
- mRS.bindRootScript(mScript);
- }
-
- boolean holdingColor[] = new boolean[10];
-
- /**
- * Calls Renderscript functions (invoke_addParticles)
- * via the Renderscript object to add particles to render
- * based on where a user touches the screen.
- */
- public void newTouchPosition(float x, float y, float pressure, int id) {
- if (id &gt;= holdingColor.length) {
- return;
- }
- int rate = (int)(pressure * pressure * 500.f);
- if (rate &gt; 500) {
- rate = 500;
- }
- if (rate &gt; 0) {
- mScript.invoke_addParticles(rate, x, y, id, !holdingColor[id]);
- holdingColor[id] = true;
- } else {
- holdingColor[id] = false;
- }
-
- }
-}
-</pre>
-
-
- <h3 id="creating-view">Creating the view class</h3>
-
-
- <p>To display graphics, you need a view to render on. Create a class that extends {@link
- android.renderscript.RSSurfaceView} or {@link android.renderscript.RSTextureView}. This class
- allows you to create a {@link android.renderscript.RenderScriptGL} context object by calling and
- pass it to the Rendscript entry point class to bind the two. Once bound, the content is aware
- of the code that it needs to use to render graphics with. If your Renderscript code
- depends on any type of information that the view is aware of, such as touches from the user,
- you can also use this class to relay that information to the Renderscript entry point class.
- The following code shows how the <code>FountainView</code> class is implemented:</p>
- <pre>
-package com.example.android.rs.fountain;
-
-import android.renderscript.RSTextureView;
-import android.renderscript.RenderScriptGL;
-import android.content.Context;
-import android.view.MotionEvent;
-
-public class FountainView extends RSTextureView {
-
- public FountainView(Context context) {
- super(context);
- }
- // Renderscript context
- private RenderScriptGL mRS;
- // Renderscript entry point object that calls Renderscript code
- private FountainRS mRender;
-
- /**
- * Create Renderscript context and initialize Renderscript entry point
- */
- &#064;Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- android.util.Log.e("rs", "onAttachedToWindow");
- if (mRS == null) {
- RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
- mRS = createRenderScriptGL(sc);
- mRender = new FountainRS();
- mRender.init(mRS, getResources());
- }
- }
-
- &#064;Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- android.util.Log.e("rs", "onDetachedFromWindow");
- if (mRS != null) {
- mRS = null;
- destroyRenderScriptGL();
- }
- }
-
-
- /**
- * Use callbacks to relay data to Renderscript entry point class
- */
- &#064;Override
- public boolean onTouchEvent(MotionEvent ev)
- {
- int act = ev.getActionMasked();
- if (act == ev.ACTION_UP) {
- mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
- return false;
- } else if (act == MotionEvent.ACTION_POINTER_UP) {
- // only one pointer going up, we can get the index like this
- int pointerIndex = ev.getActionIndex();
- int pointerId = ev.getPointerId(pointerIndex);
- mRender.newTouchPosition(0, 0, 0, pointerId);
- }
- int count = ev.getHistorySize();
- int pcount = ev.getPointerCount();
-
- for (int p=0; p &lt; pcount; p++) {
- int id = ev.getPointerId(p);
- mRender.newTouchPosition(ev.getX(p),
- ev.getY(p),
- ev.getPressure(p),
- id);
-
- for (int i=0; i &lt; count; i++) {
- mRender.newTouchPosition(ev.getHistoricalX(p, i),
- ev.getHistoricalY(p, i),
- ev.getHistoricalPressure(p, i),
- id);
- }
- }
- return true;
- }
-}
-</pre>
-
- <h3 id="creating-activity">Creating the activity class</h3>
-
- <p>Applications that use Renderscript still behave like normal Android applications, so you
- need an activity class that handles activity lifecycle callback events appropriately. The activity class
- also sets your {@link android.renderscript.RSSurfaceView} view class to be the main content view of the
- activity or uses your {@link android.renderscript.RSTextureView}
- in a {@link android.view.ViewGroup} alongside other views.</p>
-
- <p>The following code shows how the <a href="{@docRoot}resources/samples/RenderScript/Fountain/index.html">Fountain</a>
- sample declares its activity class:</p>
- <pre>
-package com.example.android.rs.fountain;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-public class Fountain extends Activity {
-
- private static final String LOG_TAG = "libRS_jni";
- private static final boolean DEBUG = false;
- private static final boolean LOG_ENABLED = false;
-
- private FountainView mView;
-
- &#064;Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- // Create our Preview view and set it as
- // the content of our activity
- mView = new FountainView(this);
- setContentView(mView);
- }
-
- &#064;Override
- protected void onResume() {
- Log.e("rs", "onResume");
-
- // Ideally a game should implement onResume() and onPause()
- // to take appropriate action when the activity looses focus
- super.onResume();
- mView.resume();
- }
-
- &#064;Override
- protected void onPause() {
- Log.e("rs", "onPause");
-
- // Ideally a game should implement onResume() and onPause()
- // to take appropriate action when the activity looses focus
- super.onPause();
- mView.pause();
-
- }
-
- static void log(String message) {
- if (LOG_ENABLED) {
- Log.v(LOG_TAG, message);
- }
- }
-}
-</pre>
-
-<p>Now that you have an idea of what is involved in a Renderscript graphics application, you can
-start building your own. It might be easiest to begin with one of the
-<a href="{@docRoot}resources/samples/RenderScript/index.html">Renderscript samples</a> as a starting
-point if this is your first time using Renderscript.</p>
-
- <h2 id="drawing">Drawing</h2>
- <p>The following sections describe how to use the graphics functions to draw with Renderscript.</p>
-
- <h3 id="drawing-rsg">Simple drawing</h3>
-
- <p>The native Renderscript APIs provide a few convenient functions to easily draw a polygon or text to
- the screen. You call these in your <code>root()</code> function to have them render to the {@link
- android.renderscript.RSSurfaceView} or {@link android.renderscript.RSTextureView}. These functions are
- available for simple drawing and should not be used for complex graphics rendering:</p>
-
- <ul>
- <li><code>rsgDrawRect()</code>: Sets up a mesh and draws a rectangle to the screen. It uses the
- top left vertex and bottom right vertex of the rectangle to draw.</li>
-
- <li><code>rsgDrawQuad()</code>: Sets up a mesh and draws a quadrilateral to the screen.</li>
-
- <li><code>rsgDrawQuadTexCoords()</code>: Sets up a mesh and draws a quadrilateral to the screen
- using the provided coordinates of a texture.</li>
-
- <li><code>rsgDrawText()</code>: Draws specified text to the screen. Use <code>rsgFontColor()</code>
- to set the color of the text.</li>
- </ul>
-
- <h3 id="drawing-mesh">Drawing with a mesh</h3>
-
- <p>When you want to render complex scenes to the screen, instantiate a {@link
- android.renderscript.Mesh} and draw it with <code>rsgDrawMesh()</code>. A {@link
- android.renderscript.Mesh} is a collection of allocations that represent vertex data (positions,
- normals, texture coordinates) and index data that provides information on how to draw triangles
- and lines with the provided vertex data. You can build a Mesh in three different ways:</p>
-
- <ul>
- <li>Build the mesh with the {@link android.renderscript.Mesh.TriangleMeshBuilder} class, which
- allows you to specify a set of vertices and indices for each triangle that you want to draw.</li>
-
- <li>Build the mesh using an {@link android.renderscript.Allocation} or a set of {@link
- android.renderscript.Allocation}s with the {@link android.renderscript.Mesh.AllocationBuilder}
- class. This approach allows you to build a mesh with vertices already stored in memory, which allows you
- to specify the vertices in Renderscript or Android framework code.</li>
-
- <li>Build the mesh with the {@link android.renderscript.Mesh.Builder} class. You should use
- this convenience method when you know the data types you want to use to build your mesh, but
- don't want to make separate memory allocations like with {@link
- android.renderscript.Mesh.AllocationBuilder}. You can specify the types that you want and this
- mesh builder automatically creates the memory allocations for you.</li>
- </ul>
-
- <p>To create a mesh using the {@link android.renderscript.Mesh.TriangleMeshBuilder}, you need to
- supply it with a set of vertices and the indices for the vertices that comprise the triangle. For
- example, the following code specifies three vertices, which are added to an internal array,
- indexed in the order they were added. The call to {@link
- android.renderscript.Mesh.TriangleMeshBuilder#addTriangle addTriangle()} draws the triangle with
- vertex 0, 1, and 2 (the vertices are drawn counter-clockwise).</p>
- <pre>
-int float2VtxSize = 2;
-Mesh.TriangleMeshBuilder triangles = new Mesh.TriangleMeshBuilder(renderscriptGL,
-float2VtxSize, Mesh.TriangleMeshBuilder.COLOR);
-triangles.addVertex(300.f, 300.f);
-triangles.addVertex(150.f, 450.f);
-triangles.addVertex(450.f, 450.f);
-triangles.addTriangle(0 , 1, 2);
-Mesh smP = triangle.create(true);
-script.set_mesh(smP);
-</pre>
-
- <p>To draw a mesh using the {@link android.renderscript.Mesh.AllocationBuilder}, you need to
- supply it with one or more allocations that contain the vertex data:</p>
- <pre>
-Allocation vertices;
-
-...
-Mesh.AllocationBuilder triangle = new Mesh.AllocationBuilder(mRS);
-smb.addVertexAllocation(vertices.getAllocation());
-smb.addIndexSetType(Mesh.Primitive.TRIANGLE);
-Mesh smP = smb.create();
-script.set_mesh(smP);
-</pre>
-
- <p>In your Renderscript code, draw the built mesh to the screen:</p>
- <pre>
-rs_mesh mesh;
-...
-
-int root(){
-...
-rsgDrawMesh(mesh);
-...
-return 0; //specify a non zero, positive integer to specify the frame refresh.
- //0 refreshes the frame only when the mesh changes.
-}
-</pre>
-
- <h2 id="shader">Programs</h2>
-
- <p>You can attach four program objects to the {@link android.renderscript.RenderScriptGL} context
- to customize the rendering pipeline. For example, you can create vertex and fragment shaders in
- GLSL or build a raster program object that controls culling. The four programs mirror a
- traditional graphical rendering pipeline:</p>
-
- <table>
- <tr>
- <th>Android Object Type</th>
-
- <th>Renderscript Native Type</th>
-
- <th>Description</th>
- </tr>
-
- <tr>
- <td>{@link android.renderscript.ProgramVertex}</td>
-
- <td>rs_program_vertex</td>
-
- <td>
- <p>The Renderscript vertex program, also known as a vertex shader, describes the stage in
- the graphics pipeline responsible for manipulating geometric data in a user-defined way.
- The object is constructed by providing Renderscript with the following data:</p>
-
- <ul>
- <li>An {@link android.renderscript.Element} describing its varying inputs or attributes</li>
-
- <li>GLSL shader string that defines the body of the program</li>
-
- <li>a {@link android.renderscript.Type} that describes the layout of an
- Allocation containing constant or uniform inputs</li>
- </ul>
-
- <p>Once the program is created, bind it to the {@link android.renderscript.RenderScriptGL}
- graphics context by calling {@link android.renderscript.RenderScriptGL#bindProgramVertex
- bindProgramVertex()}. It is then used for all subsequent draw calls until you bind a new
- program. If the program has constant inputs, the user needs to bind an allocation
- containing those inputs. The allocation's type must match the one provided during creation.
- </p>
-
- <p>The Renderscript runtime then does all the necessary plumbing to send those constants to
- the graphics hardware. Varying inputs to the shader, such as position, normal, and texture
- coordinates are matched by name between the input {@link android.renderscript.Element}
- and the mesh object that is being drawn. The signatures don't have to be exact or in any
- strict order. As long as the input name in the shader matches a channel name and size
- available on the mesh, the Renderscript runtime handles connecting the two. Unlike OpenGL
- there is no need to link the vertex and fragment programs.</p>
-
- <p>To bind shader constants to the program, declare a <code>struct</code> that contains the necessary
- shader constants in your Renderscript code. This <code>struct</code> is generated into a
- reflected class that you can use as a constant input element during the program's creation.
- It is an easy way to create an instance of this <code>struct</code> as an allocation. You would then
- bind this {@link android.renderscript.Allocation} to the program and the
- Renderscript runtime sends the data that is contained in the <code>struct</code> to the hardware
- when necessary. To update shader constants, you change the values in the
- {@link android.renderscript.Allocation} and notify the Renderscript
- code of the change.</p>
-
- <p>The {@link android.renderscript.ProgramVertexFixedFunction.Builder} class also
- lets you build a simple vertex shader without writing GLSL code.
- </p>
- </td>
- </tr>
-
- <tr>
- <td>{@link android.renderscript.ProgramFragment}</td>
-
- <td>rs_program_fragment</td>
-
- <td>
- <p>The Renderscript fragment program, also known as a fragment shader, is responsible for
- manipulating pixel data in a user-defined way. It's constructed from a GLSL shader string
- containing the program body, texture inputs, and a {@link android.renderscript.Type}
- object that describes the constants
- used by the program. Like the vertex programs, when an {@link android.renderscript.Allocation}
- with constant input
- values is bound to the shader, its values are sent to the graphics program automatically.
- Note that the values inside the {@link android.renderscript.Allocation} are not explicitly tracked.
- If they change between two draw calls using the same program object, notify the runtime of that change by
- calling <code>rsgAllocationSyncAll()</code>, so it can send the new values to hardware. Communication
- between the vertex and fragment programs is handled internally in the GLSL code. For
- example, if the fragment program is expecting a varying input called <code>varTex0</code>, the GLSL code
- inside the program vertex must provide it.</p>
-
- <p>To bind shader constructs to the program, declare a <code>struct</code> that contains the necessary
- shader constants in your Renderscript code. This <code>struct</code> is generated into a
- reflected class that you can use as a constant input element during the program's creation.
- It is an easy way to create an instance of this <code>struct</code> as an allocation. You would then
- bind this {@link android.renderscript.Allocation} to the program and the
- Renderscript runtime sends the data that is contained in the <code>struct</code> to the hardware
- when necessary. To update shader constants, you change the values in the
- {@link android.renderscript.Allocation} and notify the Renderscript
- code of the change.</p>
-
- <p>The {@link android.renderscript.ProgramFragmentFixedFunction.Builder} class also
- lets you build a simple fragment shader without writing GLSL code.
- </p>
- </td>
- </tr>
-
- <tr>
- <td>{@link android.renderscript.ProgramStore}</td>
-
- <td>rs_program_store</td>
-
- <td>The Renderscript store program contains a set of parameters that control how the graphics
- hardware writes to the framebuffer. It could be used to enable and disable depth writes and
- testing, setup various blending modes for effects like transparency and define write masks
- for color components.</td>
- </tr>
-
- <tr>
- <td>{@link android.renderscript.ProgramRaster}</td>
-
- <td>rs_program_raster</td>
-
- <td>The Renderscript raster program is primarily used to specify whether point sprites are enabled and to
- control the culling mode. By default back faces are culled.</td>
- </tr>
- </table>
-
- <p>The following example defines a vertex shader in GLSL and binds it to a Renderscript context object:</p>
- <pre>
- private RenderScriptGL glRenderer; //rendering context
- private ScriptField_Point mPoints; //vertices
- private ScriptField_VpConsts mVpConsts; //shader constants
-
- ...
-
- ProgramVertex.Builder sb = new ProgramVertex.Builder(glRenderer);
- String t = "varying vec4 varColor;\n" +
- "void main() {\n" +
- " vec4 pos = vec4(0.0, 0.0, 0.0, 1.0);\n" +
- " pos.xy = ATTRIB_position;\n" +
- " gl_Position = UNI_MVP * pos;\n" +
- " varColor = vec4(1.0, 1.0, 1.0, 1.0);\n" +
- " gl_PointSize = ATTRIB_size;\n" +
- "}\n";
- sb.setShader(t);
- sb.addConstant(mVpConsts.getType());
- sb.addInput(mPoints.getElement());
- ProgramVertex pvs = sb.create();
- pvs.bindConstants(mVpConsts.getAllocation(), 0);
- glRenderer.bindProgramVertex(pvs);
-</pre>
-
-
- <p>The <a href=
- "{@docRoot}resources/samples/RenderScript/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.html">
- RsRenderStatesRS</a> sample has many examples on how to create a shader without writing GLSL.</p>
-
- <h3 id="shader-bindings">Program bindings</h3>
-
- <p>You can also declare four pragmas that control default program bindings to the {@link
- android.renderscript.RenderScriptGL} context when the script is executing:</p>
-
- <ul>
- <li><code>stateVertex</code></li>
-
- <li><code>stateFragment</code></li>
-
- <li><code>stateRaster</code></li>
-
- <li><code>stateStore</code></li>
- </ul>
-
- <p>The possible values for each pragma are <code>parent</code> or <code>default</code>. Using
- <code>default</code> binds the shaders to the graphical context with the system defaults.</p>
-
- <p>Using <code>parent</code> binds the shaders in the same manner as it is bound in the calling
- script. If this is the root script, the parent state is taken from the bind points that are set
- by the {@link android.renderscript.RenderScriptGL} bind methods.</p>
-
- <p>For example, you can define this at the top of your graphics Renderscript code to have
- the vertex and store programs inherent the bind properties from their parent scripts:</p>
- <pre>
-#pragma stateVertex(parent)
-#pragma stateStore(parent)
-</pre>
-
- <h3 id="shader-sampler">Defining a sampler</h3>
-
- <p>A {@link android.renderscript.Sampler} object defines how data is extracted from textures.
- Samplers are bound to a {@link android.renderscript.ProgramFragment} alongside the texture
- whose sampling they control. These
- objects are used to specify such things as edge clamping behavior, whether mip-maps are used, and
- the amount of anisotropy required. There might be situations where hardware does not support the
- desired behavior of the sampler. In these cases, the Renderscript runtime attempts to provide the
- closest possible approximation. For example, the user requested 16x anisotropy, but only 8x was
- set because it's the best available on the hardware.</p>
-
- <p>The <a href=
- "{@docRoot}resources/samples/RenderScript/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.html">
- RsRenderStatesRS</a> sample has many examples on how to create a sampler and bind it to a
- Fragment program.</p>
-
-
-
-<h2 id="fbo">Rendering to a Framebuffer Object</h2>
-
-<p>Framebuffer objects allow you to render offscreen instead of in the default onscreen
-framebuffer. This approach might be useful for situations where you need to post-process a texture before
-rendering it to the screen, or when you want to composite two scenes in one such as rendering a rear-view
-mirror of a car. There are two buffers associated with a framebuffer object: a color buffer
-and a depth buffer. The color buffer (required) contains the actual pixel data of the scene
-that you are rendering, and the depth buffer (optional) contains the values necessary to figure
-out what vertices are drawn depending on their z-values.</p>
-
-<p>In general, you need to do the following to render to a framebuffer object:</p>
-
-<ul>
- <li>Create {@link android.renderscript.Allocation} objects for the color buffer and
- depth buffer (if needed). Specify the {@link
- android.renderscript.Allocation#USAGE_GRAPHICS_RENDER_TARGET} usage attribute for these
- allocations to notify the Renderscript runtime to use these allocations for the framebuffer
- object. For the color buffer allocation, you most likely need to declare the {@link
- android.renderscript.Allocation#USAGE_GRAPHICS_TEXTURE} usage attribute
- to use the color buffer as a texture, which is the most common use of the framebuffer object.</li>
-
- <li>Tell the Renderscript runtime to render to the framebuffer object instead of the default
- framebuffer by calling <code>rsgBindColorTarget()</code> and passing it the color buffer
- allocation. If applicable, call <code>rsgBindDepthTarget()</code> passing in the depth buffer
- allocation as well.</li>
-
- <li>Render your scene normally with the <code>rsgDraw</code> functions. The scene will be
- rendered into the color buffer instead of the default onscreen framebuffer.</li>
-
- <li>When done, tell the Renderscript runtime stop rendering to the color buffer and back
- to the default framebuffer by calling <code>rsgClearAllRenderTargets()</code>.</li>
-
- <li>Create a fragment shader and bind a the color buffer to it as a texture.</li>
-
- <li>Render your scene to the default framebuffer. The texture will be used according
- to the way you setup your fragment shader.</li>
-</ul>
-
-<p>The following example shows you how to render to a framebuffer object by modifying the
-<a href="{@docRoot}guide/resources/renderscript/Fountain/">Fountain</a> Renderscript sample. The end
-result is the <a href="{@docRoot}guide/resources/renderscript/FountainFBO/">FountainFBO</a> sample.
-The modifications render the exact same scene into a framebuffer object as it does the default
-framebuffer. The framebuffer object is then rendered into the default framebuffer in a small
-area at the top left corner of the screen.</p>
-
-<ol>
- <li>Modify <code>fountain.rs</code> and add the following global variables. This creates setter
- methods when this file is reflected into a <code>.java</code> file, allowing you to allocate
- memory in your Android framework code and binding it to the Renderscript runtime.
-<pre>
-//allocation for color buffer
-rs_allocation gColorBuffer;
-//fragment shader for rendering without a texture (used for rendering to framebuffer object)
-rs_program_fragment gProgramFragment;
-//fragment shader for rendering with a texture (used for rendering to default framebuffer)
-rs_program_fragment gTextureProgramFragment;
-</pre>
- </li>
-
- <li>Modify the root function of <code>fountain.rs</code> to look like the following code. The
- modifications are commented:
-<pre>
-int root() {
- float dt = min(rsGetDt(), 0.1f);
- rsgClearColor(0.f, 0.f, 0.f, 1.f);
- const float height = rsgGetHeight();
- const int size = rsAllocationGetDimX(rsGetAllocation(point));
- float dy2 = dt * (10.f);
- Point_t * p = point;
- for (int ct=0; ct < size; ct++) {
- p->delta.y += dy2;
- p->position += p->delta;
- if ((p->position.y > height) && (p->delta.y > 0)) {
- p->delta.y *= -0.3f;
- }
- p++;
- }
- //Tell Renderscript runtime to render to the frame buffer object
- rsgBindColorTarget(gColorBuffer, 0);
- //Begin rendering on a white background
- rsgClearColor(1.f, 1.f, 1.f, 1.f);
- rsgDrawMesh(partMesh);
-
- //When done, tell Renderscript runtime to stop rendering to framebuffer object
- rsgClearAllRenderTargets();
-
- //Bind a new fragment shader that declares the framebuffer object to be used as a texture
- rsgBindProgramFragment(gTextureProgramFragment);
-
- //Bind the framebuffer object to the fragment shader at slot 0 as a texture
- rsgBindTexture(gTextureProgramFragment, 0, gColorBuffer);
- //Draw a quad using the framebuffer object as the texture
- float startX = 10, startY = 10;
- float s = 256;
- rsgDrawQuadTexCoords(startX, startY, 0, 0, 1,
- startX, startY + s, 0, 0, 0,
- startX + s, startY + s, 0, 1, 0,
- startX + s, startY, 0, 1, 1);
-
- //Rebind the original fragment shader to render as normal
- rsgBindProgramFragment(gProgramFragment);
-
- //Render the main scene
- rsgDrawMesh(partMesh);
-
- return 1;
-}
-</pre>
- </li>
-
- <li>In the <code>FountainRS.java</code> file, modify the <code>init()</code> method to look
- like the following code. The modifications are commented:
-
-<pre>
-/* Add necessary members */
-private ScriptC_fountainfbo mScript;
-private Allocation mColorBuffer;
-private ProgramFragment mProgramFragment;
-private ProgramFragment mTextureProgramFragment;
-
-public void init(RenderScriptGL rs, Resources res) {
- mRS = rs;
- mRes = res;
-
- ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT);
-
- Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
- smb.addVertexAllocation(points.getAllocation());
- smb.addIndexSetType(Mesh.Primitive.POINT);
- Mesh sm = smb.create();
-
- mScript = new ScriptC_fountainfbo(mRS, mRes, R.raw.fountainfbo);
- mScript.set_partMesh(sm);
- mScript.bind_point(points);
-
- ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs);
- pfb.setVaryingColor(true);
- mProgramFragment = pfb.create();
- mScript.set_gProgramFragment(mProgramFragment);
-
- /* Second fragment shader to use a texture (framebuffer object) to draw with */
- pfb.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
- ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-
- /* Set the fragment shader in the Renderscript runtime */
- mTextureProgramFragment = pfb.create();
- mScript.set_gTextureProgramFragment(mTextureProgramFragment);
-
- /* Create the allocation for the color buffer */
- Type.Builder colorBuilder = new Type.Builder(mRS, Element.RGBA_8888(mRS));
- colorBuilder.setX(256).setY(256);
- mColorBuffer = Allocation.createTyped(mRS, colorBuilder.create(),
- Allocation.USAGE_GRAPHICS_TEXTURE |
- Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-
- /* Set the allocation in the Renderscript runtime */
- mScript.set_gColorBuffer(mColorBuffer);
-
- mRS.bindRootScript(mScript);
-}
-</pre>
-
-<p class="note"><strong>Note:</strong> This sample doesn't use a depth buffer, but the following code
-shows you how to declare an example depth buffer if you need to use
-one for your application. The depth buffer must have the same dimensions as the color buffer:
-
-<pre>
-Allocation mDepthBuffer;
-
-...
-
-Type.Builder b = new Type.Builder(mRS, Element.createPixel(mRS, DataType.UNSIGNED_16,
- DataKind.PIXEL_DEPTH));
-b.setX(256).setY(256);
-mDepthBuffer = Allocation.createTyped(mRS, b.create(),
-Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-
-</pre>
-</p>
-</li>
-
- <li>Run and use the sample. The smaller, white quad on the top-left corner is using the
- framebuffer object as a texture, which renders the same scene as the main rendering.</li>
-</ol>
diff --git a/docs/html/guide/topics/location/strategies.jd b/docs/html/guide/topics/location/strategies.jd
index 6cc8f1a..2f7e6c3 100644
--- a/docs/html/guide/topics/location/strategies.jd
+++ b/docs/html/guide/topics/location/strategies.jd
@@ -1,6 +1,5 @@
page.title=Location Strategies
-parent.title=Location and Maps
-parent.link=index.html
+page.tags="geolocation","maps","mapview"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/media/audio-capture.jd b/docs/html/guide/topics/media/audio-capture.jd
index 75d294b..44c618f 100644
--- a/docs/html/guide/topics/media/audio-capture.jd
+++ b/docs/html/guide/topics/media/audio-capture.jd
@@ -1,6 +1,5 @@
page.title=Audio Capture
-parent.title=Multimedia and Camera
-parent.link=index.html
+page.tags="mediarecorder"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/media/camera.jd b/docs/html/guide/topics/media/camera.jd
index 3fe23f8..8ebb349 100644
--- a/docs/html/guide/topics/media/camera.jd
+++ b/docs/html/guide/topics/media/camera.jd
@@ -1,6 +1,5 @@
page.title=Camera
-parent.title=Multimedia and Camera
-parent.link=index.html
+page.tags="mediarecorder"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/media/mediaplayer.jd b/docs/html/guide/topics/media/mediaplayer.jd
index 45a58a7..fb272d2 100644
--- a/docs/html/guide/topics/media/mediaplayer.jd
+++ b/docs/html/guide/topics/media/mediaplayer.jd
@@ -1,6 +1,5 @@
page.title=Media Playback
-parent.title=Multimedia and Camera
-parent.link=index.html
+page.tags="mediaplayer","soundpool","audiomanager"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/resources/runtime-changes.jd b/docs/html/guide/topics/resources/runtime-changes.jd
index 5f39aa5..45a548a 100644
--- a/docs/html/guide/topics/resources/runtime-changes.jd
+++ b/docs/html/guide/topics/resources/runtime-changes.jd
@@ -1,6 +1,5 @@
page.title=Handling Runtime Changes
-parent.title=Application Resources
-parent.link=index.html
+page.tags="activity","lifecycle"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/search/adding-custom-suggestions.jd b/docs/html/guide/topics/search/adding-custom-suggestions.jd
index 02ee084..47ad2fe 100644
--- a/docs/html/guide/topics/search/adding-custom-suggestions.jd
+++ b/docs/html/guide/topics/search/adding-custom-suggestions.jd
@@ -1,6 +1,5 @@
page.title=Adding Custom Suggestions
-parent.title=Search
-parent.link=index.html
+page.tags="SearchRecentSuggestionsProvider",
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/search/adding-recent-query-suggestions.jd b/docs/html/guide/topics/search/adding-recent-query-suggestions.jd
index 2c9a461..c1d59d4 100644
--- a/docs/html/guide/topics/search/adding-recent-query-suggestions.jd
+++ b/docs/html/guide/topics/search/adding-recent-query-suggestions.jd
@@ -1,6 +1,6 @@
page.title=Adding Recent Query Suggestions
-parent.title=Search
-parent.link=index.html
+page.tags="SearchRecentSuggestions","SearchRecentSuggestionsProvider"
+
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/search/search-dialog.jd b/docs/html/guide/topics/search/search-dialog.jd
index e24681a..fc722b2 100644
--- a/docs/html/guide/topics/search/search-dialog.jd
+++ b/docs/html/guide/topics/search/search-dialog.jd
@@ -1,6 +1,5 @@
page.title=Creating a Search Interface
-parent.title=Search
-parent.link=index.html
+page.tags="searchview"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/search/searchable-config.jd b/docs/html/guide/topics/search/searchable-config.jd
index fb689f9..fc13c04 100644
--- a/docs/html/guide/topics/search/searchable-config.jd
+++ b/docs/html/guide/topics/search/searchable-config.jd
@@ -1,6 +1,5 @@
page.title=Searchable Configuration
-parent.title=Search
-parent.link=index.html
+
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/security/index.jd b/docs/html/guide/topics/security/index.jd
deleted file mode 100644
index 775fc03..0000000
--- a/docs/html/guide/topics/security/index.jd
+++ /dev/null
@@ -1,65 +0,0 @@
-page.title=Security and Permissions
-page.landing=true
-page.landing.intro=Android's security architecture gives the user full control over what resources are accessible to each app, protecting the system itself and all apps in it. Learn how to use system permissions to request access to the resources your app needs and design your app for optimal security.
-page.landing.image=
-
-@jd:body
-
-<div style="width=100%;padding-left:1em;">
-
- <div style="float:left;clear:both;padding-top:20px;">
- <p style="text-transform:uppercase;"><b style="color:#666;font-size:14px;">Blog Articles</b></p>
-
- <div class="" style="border-top:2px solid #DDD;margin:1em 0;background-color:#F7F7F7;width:336px">
-
- <div style="float:left;padding:8px;padding-right:16px;">
- <img src="/assets/images/resource-article.png">
- </div>
-
- <div class="caption">
- <p style="margin:0;padding:0;font-weight:bold;"><a href="">Accessibility: Are You Serving All Your Users?</a></p>
- <p style="margin:0;padding:0">In the upcoming weeks, some of the older Client Login authentication keys will expire.
- If you generated the token you’re currently using to authenticate with the C2DM servers before October 2011, it will stop working.</p>
-
- <p style="margin:0;padding:0;font-weight:bold;"><a href="">Android C2DM — Client Login key expiration</a></p>
- <p style="margin:0;padding:0">Accessibility is about making sure that Android users who have limited vision or other physical impairments can use your application just as well</p>
-
- <p style="margin:0;padding:0;font-weight:bold;"><a href="">A Faster Emulator with Better Hardware Support</a></p>
- <p style="margin:0;padding:0">The Android emulator is a key tool for Android developers in building and testing their apps.
- As the power and diversity of Android devices has grown quickly, it’s been hard for the emulator keep pace. </p>
-
- <a href="">More &raquo;</a>
- </div>
- </div>
- </div>
-
- <div style="float:right;padding-top:20px;">
- <p style="text-transform:uppercase;"><b style="color:#666;font-size:14px;">Training</b></p>
-
- <div class="" style="border-top:2px solid #DDD;bordddser-top:2px solid #FF8800;margin:1em 0;background-color:#F7F7F7;width:336px">
-
- <div style="float:left;padding:8px;padding-right:16px;">
- <img src="/assets/images/resource-tutorial.png">
- </div>
-
- <div class="caption">
- <p style="margin:0;padding:0;font-weight:bold;"><a href="">Managing the Activity Lifecycle</a></p>
- <p style="margin:0;padding:0">This class explains important lifecycle callback methods that each Activity
- instance receives and how you can use them so your activity does what the user expects and does not consume system
- resources when your activity doesn't need them.</p>
-
- <p style="margin:0;padding:0;font-weight:bold;"><a href="">Supporting Different Devices</a></p>
- <p style="margin:0;padding:0">This class teaches you how to use basic platform features that leverage alternative
- resources and other features so your app can provide an optimized user experience on a variety of Android-compatible devices,
- using a single application package (APK).</p>
-
- <p style="margin:0;padding:0;font-weight:bold;"><a href="">Sharing Content</a></p>
- <p style="margin:0;padding:0">This class covers some common ways you can send and receive content between
- applications using Intent APIs and the ActionProvider object.</p>
-
- <a href="">More &raquo;</a>
- </div>
- </div>
-</div>
-
-</div> \ No newline at end of file
diff --git a/docs/html/guide/topics/security/security.jd b/docs/html/guide/topics/security/security.jd
deleted file mode 100644
index 9cdccae..0000000
--- a/docs/html/guide/topics/security/security.jd
+++ /dev/null
@@ -1,767 +0,0 @@
-page.title=Designing for Security
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
-<h2>In this document</h2>
-<ol>
-<li><a href="#Dalvik">Using Davlik Code</a></li>
-<li><a href="#Native">Using Native Code</a></li>
-<li><a href="#Data">Storing Data</a></li>
-<li><a href="#IPC">Using IPC</a></li>
-<li><a href="#Permissions">Using Permissions</a></li>
-<li><a href="#Networking">Using Networking</a></li>
-<li><a href="#DynamicCode">Dynamically Loading Code</a></li>
-<li><a href="#Input">Performing Input Validation</a></li>
-<li><a href="#UserData">Handling User Data</a></li>
-<li><a href="#Crypto">Using Cryptography</a></li>
-</ol>
-<h2>See also</h2>
-<ol>
-<li><a href="http://source.android.com/tech/security/index.html">Android
-Security Overview</a></li>
-<li><a href="{@docRoot}guide/topics/security/security.html">Android Security
-And Permissions</a></li>
-</ol>
-</div></div>
-<p>Android was designed so that most developers will be able to build
-applications using the default settings and not be confronted with difficult
-decisions about security. Android also has a number of security features built
-into the operating system that significantly reduce the frequency and impact of
-application security issues.</p>
-
-<p>Some of the security features that help developers build secure applications
-include:
-<ul>
-<li>The Android Application Sandbox that isolates data and code execution on a
-per-application basis.</li>
-<li>Android application framework with robust implementations of common
-security functionality such as cryptography, permissions, and secure IPC.</li>
-<li>Technologies like ASLR, NX, ProPolice, safe_iop, OpenBSD dlmalloc, OpenBSD
-calloc, and Linux mmap_min_addr to mitigate risks associated with common memory
-management errors</li>
-<li>An encrypted filesystem that can be enabled to protect data on lost or
-stolen devices.</li>
-</ul></p>
-
-<p>Nevertheless, it is important for developers to be familiar with Android
-security best practices to make sure they take advantage of these capabilities
-and to reduce the likelihood of inadvertently introducing security issues that
-can affect their applications.</p>
-
-<p>This document is organized around common APIs and development techniques
-that can have security implications for your application and its users. As
-these best practices are constantly evolving, we recommend you check back
-occasionally throughout your application development process.</p>
-
-<a name="Dalvik"></a>
-<h2>Using Dalvik Code</h2>
-<p>Writing secure code that runs in virtual machines is a well-studied topic
-and many of the issues are not specific to Android. Rather than attempting to
-rehash these topics, we’d recommend that you familiarize yourself with the
-existing literature. Two of the more popular resources are:
-<ul>
-<li><a href="http://www.securingjava.com/toc.html">
-http://www.securingjava.com/toc.html</a></li>
-<li><a
-href="https://www.owasp.org/index.php/Java_Security_Resources">
-https://www.owasp.org/index.php/Java_Security_Resources</a></li>
-</ul></p>
-
-<p>This document is focused on the areas which are Android specific and/or
-different from other environments. For developers experienced with VM
-programming in other environments, there are two broad issues that may be
-different about writing apps for Android:
-<ul>
-<li>Some virtual machines, such as the JVM or .net runtime, act as a security
-boundary, isolating code from the underlying operating system capabilities. On
-Android, the Dalvik VM is not a security boundary -- the application sandbox is
-implemented at the OS level, so Dalvik can interoperate with native code in the
-same application without any security constraints.</li>
-<li>Given the limited storage on mobile devices, it’s common for developers
-to want to build modular applications and use dynamic class loading. When
-doing this consider both the source where you retrieve your application logic
-and where you store it locally. Do not use dynamic class loading from sources
-that are not verified, such as unsecured network sources or external storage,
-since that code can be modified to include malicious behavior.</li>
-</ul></p>
-
-<a name="Native"></a>
-<h2>Using Native Code</h2>
-
-<p>In general, we encourage developers to use the Android SDK for most
-application development, rather than using native code. Applications built
-with native code are more complex, less portable, and more like to include
-common memory corruption errors such as buffer overflows.</p>
-
-<p>Android is built using the Linux kernel and being familiar with Linux
-development security best practices is especially useful if you are going to
-use native code. This document is too short to discuss all of those best
-practices, but one of the most popular resources is “Secure Programming for
-Linux and Unix HOWTO”, available at <a
-href="http://www.dwheeler.com/secure-programs">
-http://www.dwheeler.com/secure-programs</a>.</p>
-
-<p>An important difference between Android and most Linux environments is the
-Application Sandbox. On Android, all applications run in the Application
-Sandbox, including those written with native code. At the most basic level, a
-good way to think about it for developers familiar with Linux is to know that
-every application is given a unique UID with very limited permissions. This is
-discussed in more detail in the <a
-href="http://source.android.com/tech/security/index.html">Android Security
-Overview</a> and you should be familiar with application permissions even if
-you are using native code.</p>
-
-<a name="Data"></a>
-<h2>Storing Data</h2>
-
-<h3>Using internal files</h3>
-
-<p>By default, files created on <a
-href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal
-storage</a> are only accessible to the application that created the file. This
-protection is implemented by Android and is sufficient for most
-applications.</p>
-
-<p>Use of <a
-href="{@docRoot}reference/android/content/Context.html#MODE_WORLD_WRITEABLE">
-world writable</a> or <a
-href="{@docRoot}reference/android/content/Context.html#MODE_WORLD_READABLE">world
-readable</a> files for IPC is discouraged because it does not provide
-the ability to limit data access to particular applications, nor does it
-provide any control on data format. As an alternative, you might consider using
-a ContentProvider which provides read and write permissions, and can make
-dynamic permission grants on a case-by-case basis.</p>
-
-<p>To provide additional protection for sensitive data, some applications
-choose to encrypt local files using a key that is not accessible to the
-application. (For example, a key can be placed in a {@link java.security.KeyStore} and
-protected with a user password that is not stored on the device). While this
-does not protect data from a root compromise that can monitor the user
-inputting the password, it can provide protection for a lost device without <a
-href="http://source.android.com/tech/encryption/index.html">file system
-encryption</a>.</p>
-
-<h3>Using external storage</h3>
-
-<p>Files created on <a
-href="{@docRoot}guide/topics/data/data-storage.html#filesExternal">external
-storage</a>, such as SD Cards, are globally readable and writable. Since
-external storage can be removed by the user and also modified by any
-application, applications should not store sensitive information using
-external storage.</p>
-
-<p>As with data from any untrusted source, applications should perform input
-validation when handling data from external storage (see Input Validation
-section). We strongly recommend that applications not store executables or
-class files on external storage prior to dynamic loading. If an application
-does retrieve executable files from external storage they should be signed and
-cryptographically verified prior to dynamic loading.</p>
-
-<h3>Using content providers</h3>
-
-<p>ContentProviders provide a structured storage mechanism that can be limited
-to your own application, or exported to allow access by other applications. By
-default, a <code>
-<a href="{@docRoot}reference/android/content/ContentProvider.html">
-ContentProvider</a></code> is
-<a href="{@docRoot}guide/topics/manifest/provider-element.html#exported">exported
-</a> for use by other applications. If you do not intend to provide other
-applications with access to your<code>
-<a href="{@docRoot}reference/android/content/ContentProvider.html">
-ContentProvider</a></code>, mark them as <code><a
-href="{@docRoot}guide/topics/manifest/provider-element.html#exported">
-android:exported=false</a></code> in the application manifest.</p>
-
-<p>When creating a <code>
-<a href="{@docRoot}reference/android/content/ContentProvider.html">ContentProvider
-</a></code> that will be exported for use by other applications, you can specify
-a single
-<a href="{@docRoot}guide/topics/manifest/provider-element.html#prmsn">permission
-</a> for reading and writing, or distinct permissions for reading and writing
-within the manifest. We recommend that you limit your permissions to those
-required to accomplish the task at hand. Keep in mind that it’s usually
-easier to add permissions later to expose new functionality than it is to take
-them away and break existing users.</p>
-
-<p>If you are using a <code>
-<a href="{@docRoot}reference/android/content/ContentProvider.html">
-ContentProvider</a></code> for sharing data between applications built by the
-same developer, it is preferable to use
-<a href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">signature
-level permissions</a>. Signature permissions do not require user confirmation,
-so they provide a better user experience and more controlled access to the
-<code>
-<a href="{@docRoot}reference/android/content/ContentProvider.html">
-ContentProvider</a></code>.</p>
-
-<p>ContentProviders can also provide more granular access by declaring the <a
-href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn">
-grantUriPermissions</a> element and using the <code><a
-href="{@docRoot}reference/android/content/Intent.html#FLAG_GRANT_READ_URI_PERMISSION">FLAG_GRANT_READ_URI_PERMISSION</a></code>
-and <code><a
-href="{@docRoot}reference/android/content/Intent.html#FLAG_GRANT_WRITE_URI_PERMISSION">FLAG_GRANT_WRITE_URI_PERMISSION</a></code>
-flags in the Intent object
-that activates the component. The scope of these permissions can be further
-limited by the <code><a
-href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html">
-grant-uri-permission element</a></code>.</p>
-
-<p>When accessing a <code>
-<a href="{@docRoot}reference/android/content/ContentProvider.html">
-ContentProvider</a></code>, use parameterized query methods such as <code>
-<a href="{@docRoot}reference/android/content/ContentProvider.html#query(android.net.Uri,%20java.lang.String[],%20java.lang.String,%20java.lang.String[],%20java.lang.String)">query()</a></code>, <code><a
-href="{@docRoot}reference/android/content/ContentProvider.html#update(android.net.Uri,%20android.content.ContentValues,%20java.lang.String,%20java.lang.String[])">update()</a></code>, and <code><a
-href="{@docRoot}reference/android/content/ContentProvider.html#delete(android.net.Uri,%20java.lang.String,%20java.lang.String[])">delete()</a></code> to avoid
-potential <a href="http://en.wikipedia.org/wiki/SQL_injection">SQL
-Injection</a> from untrusted data. Note that using parameterized methods is not
-sufficient if the <code>selection</code> is built by concatenating user data
-prior to submitting it to the method.</p>
-
-<p>Do not have a false sense of security about the write permission. Consider
-that the write permission allows SQL statements which make it possible for some
-data to be confirmed using creative <code>WHERE</code> clauses and parsing the
-results. For example, an attacker might probe for presence of a specific phone
-number in a call-log by modifying a row only if that phone number already
-exists. If the content provider data has predictable structure, the write
-permission may be equivalent to providing both reading and writing.</p>
-
-<a name="IPC"></a>
-<h2>Using Interprocess Communication (IPC)</h2>
-
-<p>Some Android applications attempt to implement IPC using traditional Linux
-techniques such as network sockets and shared files. We strongly encourage the
-use of Android system functionality for IPC such as Intents, Binders, Services,
-and Receivers. The Android IPC mechanisms allow you to verify the identity of
-the application connecting to your IPC and set security policy for each IPC
-mechanism.</p>
-
-<p>Many of the security elements are shared across IPC mechanisms. <a
-href="{@docRoot}reference/android/content/BroadcastReceiver.html">
-Broadcast Receivers</a>, <a
-href="{@docRoot}reference/android/R.styleable.html#AndroidManifestActivity">
-Activities</a>, and <a
-href="{@docRoot}reference/android/R.styleable.html#AndroidManifestService">
-Services</a> are all declared in the application manifest. If your IPC mechanism is
-not intended for use by other applications, set the <a
-href="{@docRoot}guide/topics/manifest/service-element.html#exported">{@code android:exported}</a>
-property to false. This is useful for applications that consist of multiple processes
-within the same UID, or if you decide late in development that you do not
-actually want to expose functionality as IPC but you don’t want to rewrite
-the code.</p>
-
-<p>If your IPC is intended to be accessible to other applications, you can
-apply a security policy by using the <a
-href="{@docRoot}reference/android/R.styleable.html#AndroidManifestPermission">
-Permission</a> tag. If IPC is between applications built by the same developer,
-it is preferable to use <a
-href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">signature
-level permissions</a>. Signature permissions do not require user confirmation,
-so they provide a better user experience and more controlled access to the IPC
-mechanism.</p>
-
-<p>One area that can introduce confusion is the use of intent filters. Note
-that Intent filters should not be considered a security feature -- components
-can be invoked directly and may not have data that would conform to the intent
-filter. You should perform input validation within your intent receiver to
-confirm that it is properly formatted for the invoked receiver, service, or
-activity.</p>
-
-<h3>Using intents</h3>
-
-<p>Intents are the preferred mechanism for asynchronous IPC in Android.
-Depending on your application requirements, you might use <code><a
-href="{@docRoot}reference/android/content/Context.html#sendBroadcast(android.content.Intent)">sendBroadcast()</a></code>,
-<code><a
-href="{@docRoot}reference/android/content/Context.html#sendOrderedBroadcast(android.content.Intent,%20java.lang.String)">sendOrderedBroadcast()</a></code>,
-or direct an intent to a specific application component.</p>
-
-<p>Note that ordered broadcasts can be “consumed” by a recipient, so they
-may not be delivered to all applications. If you are sending an Intent where
-delivery to a specific receiver is required, the intent must be delivered
-directly to the receiver.</p>
-
-<p>Senders of an intent can verify that the recipient has a permission
-specifying a non-Null Permission upon sending. Only applications with that
-Permission will receive the intent. If data within a broadcast intent may be
-sensitive, you should consider applying a permission to make sure that
-malicious applications cannot register to receive those messages without
-appropriate permissions. In those circumstances, you may also consider
-invoking the receiver directly, rather than raising a broadcast.</p>
-
-<h3>Using binder and AIDL interfaces</h3>
-
-<p><a href="{@docRoot}reference/android/os/Binder.html">Binders</a> are the
-preferred mechanism for RPC-style IPC in Android. They provide a well-defined
-interface that enables mutual authentication of the endpoints, if required.</p>
-
-<p>We strongly encourage designing interfaces in a manner that does not require
-interface specific permission checks. Binders are not declared within the
-application manifest, and therefore you cannot apply declarative permissions
-directly to a Binder. Binders generally inherit permissions declared in the
-application manifest for the Service or Activity within which they are
-implemented. If you are creating an interface that requires authentication
-and/or access controls on a specific binder interface, those controls must be
-explicitly added as code in the interface.</p>
-
-<p>If providing an interface that does require access controls, use <code><a
-href="{@docRoot}reference/android/content/Context.html#checkCallingPermission(java.lang.String)">checkCallingPermission()</a></code>
-to verify whether the
-caller of the Binder has a required permission. This is especially important
-before accessing a Service on behalf of the caller, as the identify of your
-application is passed to other interfaces. If invoking an interface provided
-by a Service, the <code><a
-href="{@docRoot}reference/android/content/Context.html#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int)">bindService()</a></code>
- invocation may fail if you do not have permission to access the given Service.
- If calling an interface provided locally by your own application, it may be
-useful to use the <code><a
-href="{@docRoot}reference/android/os/Binder.html#clearCallingIdentity()">
-clearCallingIdentity()</a></code> to satisfy internal security checks.</p>
-
-<h3>Using broadcast receivers</h3>
-
-<p>Broadcast receivers are used to handle asynchronous requests initiated via
-an intent.</p>
-
-<p>By default, receivers are exported and can be invoked by any other
-application. If your <code><a
-href="{@docRoot}reference/android/content/BroadcastReceiver.html">
-BroadcastReceivers</a></code> is intended for use by other applications, you
-may want to apply security permissions to receivers using the <code><a
-href="{@docRoot}guide/topics/manifest/receiver-element.html">
-&lt;receiver&gt;</a></code> element within the application manifest. This will
-prevent applications without appropriate permissions from sending an intent to
-the <code><a
-href="{@docRoot}reference/android/content/BroadcastReceiver.html">
-BroadcastReceivers</a></code>.</p>
-
-<h3>Using Services</h3>
-
-<p>Services are often used to supply functionality for other applications to
-use. Each service class must have a corresponding <service> declaration in its
-package's AndroidManifest.xml.</p>
-
-<p>By default, Services are exported and can be invoked by any other
-application. Services can be protected using the <a
-href="{@docRoot}guide/topics/manifest/service-element.html#prmsn">{@code android:permission}</a>
-attribute
-within the manifest’s <code><a
-href="{@docRoot}guide/topics/manifest/service-element.html">
-&lt;service&gt;</a></code> tag. By doing so, other applications will need to declare
-a corresponding <code><a
-href="{@docRoot}guide/topics/manifest/uses-permission-element.html">&lt;uses-permission&gt;</a>
-</code> element in their own manifest to be
-able to start, stop, or bind to the service.</p>
-
-<p>A Service can protect individual IPC calls into it with permissions, by
-calling <code><a
-href="{@docRoot}reference/android/content/Context.html#checkCallingPermission(java.lang.String)">checkCallingPermission()</a></code>
-before executing
-the implementation of that call. We generally recommend using the
-declarative permissions in the manifest, since those are less prone to
-oversight.</p>
-
-<h3>Using Activities</h3>
-
-<p>Activities are most often used for providing the core user-facing
-functionality of an application. By default, Activities are exported and
-invokable by other applications only if they have an intent filter or binder
-declared. In general, we recommend that you specifically declare a Receiver or
-Service to handle IPC, since this modular approach reduces the risk of exposing
-functionality that is not intended for use by other applications.</p>
-
-<p>If you do expose an Activity for purposes of IPC, the <code><a
-href="{@docRoot}guide/topics/manifest/activity-element.html#prmsn">android:permission</a></code>
-attribute in the <code><a
-href="{@docRoot}guide/topics/manifest/activity-element.html">
-&lt;activity&gt;</a></code> declaration in the application manifest can be used to
-restrict access to only those applications which have the stated
-permissions.</p>
-
-<a name="Permissions"></a>
-<h2>Using Permissions</h2>
-
-<h3>Requesting Permissions</h3>
-
-<p>We recommend minimizing the number of permissions requested by an
-application. Not having access to sensitive permissions reduces the risk of
-inadvertently misusing those permissions, can improve user adoption, and makes
-applications less attractive targets for attackers.</p>
-
-<p>If it is possible to design your application in a way that does not require
-a permission, that is preferable. For example, rather than requesting access
-to device information to create an identifier, create a <a
-href="{@docRoot}reference/java/util/UUID.html">GUID</a> for your application.
-(This specific example is also discussed in Handling User Data) Or, rather than
-using external storage, store data in your application directory.</p>
-
-<p>If a permission is not required, do not request it. This sounds simple, but
-there has been quite a bit of research into the frequency of over-requesting
-permissions. If you’re interested in the subject you might start with this
-research paper published by U.C. Berkeley: <a
-href="http://www.eecs.berkeley.edu/Pubs/TechRpts/2011/EECS-2011-48.pdf">
-http://www.eecs.berkeley.edu/Pubs/TechRpts/2011/EECS-2011-48.pdf</a></p>
-
-<p>In addition to requesting permissions, your application can use <a
-href="{@docRoot}guide/topics/manifest/permission-element.html">permissions</a>
-to protect IPC that is security sensitive and will be exposed to other
-applications -- such as a <code><a
-href="{@docRoot}reference/android/content/ContentProvider.html">
-ContentProvider</a></code>. In general, we recommend using access controls
-other than user confirmed permissions where possible since permissions can
-be confusing for users. For example, consider using the <a
-href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">signature
-protection level</a> on permissions for IPC communication between applications
-provided by a single developer.</p>
-
-<p>Do not cause permission re-delegation. This occurs when an app exposes data
-over IPC that is only available because it has a specific permission, but does
-not require that permission of any clients of it’s IPC interface. More
-details on the potential impacts, and frequency of this type of problem is
-provided in this research paper published at USENIX: <a
-href="http://www.cs.berkeley.edu/~afelt/felt_usenixsec2011.pdf">http://www.cs.be
-rkeley.edu/~afelt/felt_usenixsec2011.pdf</a></p>
-
-<h3>Creating Permissions</h3>
-
-<p>Generally, you should strive to create as few permissions as possible while
-satisfying your security requirements. Creating a new permission is relatively
-uncommon for most applications, since <a
-href="{@docRoot}reference/android/Manifest.permission.html">system-defined
-permissions</a> cover many situations. Where appropriate,
-perform access checks using existing permissions.</p>
-
-<p>If you must create a new permission, consider whether you can accomplish
-your task with a Signature permission. Signature permissions are transparent
-to the user and only allow access by applications signed by the same developer
-as application performing the permission check. If you create a Dangerous
-permission, then the user needs to decide whether to install the application.
-This can be confusing for other developers, as well as for users.</p>
-
-<p>If you create a Dangerous permission, there are a number of complexities
-that you need to consider.
-<ul>
-<li>The permission must have a string that concisely expresses to a user the
-security decision they will be required to make.</li>
-<li>The permission string must be localized to many different languages.</li>
-<li>Uses may choose not to install an application because a permission is
-confusing or perceived as risky.</li>
-<li>Applications may request the permission when the creator of the permission
-has not been installed.</li>
-</ul></p>
-
-<p>Each of these poses a significant non-technical challenge for an application
-developer, which is why we discourage the use of Dangerous permission.</p>
-
-<a name="Networking"></a>
-<h2>Using Networking</h2>
-
-<h3>Using IP Networking</h3>
-
-<p>Networking on Android is not significantly different from Linux
-environments. The key consideration is making sure that appropriate protocols
-are used for sensitive data, such as <a
-href="{@docRoot}reference/javax/net/ssl/HttpsURLConnection.html">HTTPS</a> for
-web traffic. We prefer use of HTTPS over HTTP anywhere that HTTPS is
-supported on the server, since mobile devices frequently connect on networks
-that are not secured, such as public WiFi hotspots.</p>
-
-<p>Authenticated, encrypted socket-level communication can be easily
-implemented using the <code><a
-href="{@docRoot}reference/javax/net/ssl/SSLSocket.html">SSLSocket</a></code>
-class. Given the frequency with which Android devices connect to unsecured
-wireless networks using WiFi, the use of secure networking is strongly
-encouraged for all applications.</p>
-
-<p>We have seen some applications use <a
-href="http://en.wikipedia.org/wiki/Localhost">localhost</a> network ports for
-handling sensitive IPC. We discourage this approach since these interfaces are
-accessible by other applications on the device. Instead, use an Android IPC
-mechanism where authentication is possible such as a Service and Binder. (Even
-worse than using loopback is to bind to INADDR_ANY since then your application
-may receive requests from anywhere. We’ve seen that, too.)</p>
-
-<p>Also, one common issue that warrants repeating is to make sure that you do
-not trust data downloaded from HTTP or other insecure protocols. This includes
-validation of input in <code><a
-href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code> and
-any responses to intents issued against HTTP.</p>
-
-<h3>Using Telephony Networking</h3>
-
-<p>SMS is the telephony protocol most frequently used by Android developers.
-Developers should keep in mind that this protocol was primarily designed for
-user-to-user communication and is not well-suited for some application
-purposes. Due to the limitations of SMS, we strongly recommend the use of <a
-href="http://code.google.com/android/c2dm/">C2DM</a> and IP networking for
-sending data messages to devices.</p>
-
-<p>Many developers do not realize that SMS is not encrypted or strongly
-authenticated on the network or on the device. In particular, any SMS receiver
-should expect that a malicious user may have sent the SMS to your application
--- do not rely on unauthenticated SMS data to perform sensitive commands.
-Also, you should be aware that SMS may be subject to spoofing and/or
-interception on the network. On the Android-powered device itself, SMS
-messages are transmitted as Broadcast intents, so they may be read or captured
-by other applications that have the READ_SMS permission.</p>
-
-<a name="DynamicCode"></a>
-<h2>Dynamically Loading Code</h2>
-
-<p>We strongly discourage loading code from outside of the application APK.
-Doing so significantly increases the likelihood of application compromise due
-to code injection or code tampering. It also adds complexity around version
-management and application testing. Finally, it can make it impossible to
-verify the behavior of an application, so it may be prohibited in some
-environments.</p>
-
-<p>If your application does dynamically load code, the most important thing to
-keep in mind about dynamically loaded code is that it runs with the same
-security permissions as the application APK. The user made a decision to
-install your application based on your identity, and they are expecting that
-you provide any code run within the application, including code that is
-dynamically loaded.</p>
-
-<p>The major security risk associated with dynamically loading code is that the
-code needs to come from a verifiable source. If the modules are included
-directly within your APK, then they cannot be modified by other applications.
-This is true whether the code is a native library or a class being loaded using
-<a href="{@docRoot}reference/dalvik/system/DexClassLoader.html">
-<code>DexClassLoader</code></a>. We have seen many instances of applications
-attempting to load code from insecure locations, such as downloaded from the
-network over unencrypted protocols or from world writable locations such as
-external storage. These locations could allow someone on the network to modify
-the content in transit, or another application on a users device to modify the
-content, respectively.</p>
-
-
-<h3>Using WebView</h3>
-
-<p>Since WebView consumes web content that can include HTML and JavaScript,
-improper use can introduce common web security issues such as <a
-href="http://en.wikipedia.org/wiki/Cross_site_scripting">cross-site-scripting</a
-> (JavaScript injection). Android includes a number of mechanisms to reduce
-the scope of these potential issues by limiting the capability of WebView to
-the minimum functionality required by your application.</p>
-
-<p>If your application does not directly use JavaScript within a <code><a
-href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code>, do
-not call
-<a href="{@docRoot}reference/android/webkit/WebSettings.html#setJavaScriptEnabled(boolean)">
-<code>setJavaScriptEnabled()</code></a>. We have seen this method invoked
-in sample code that might be repurposed in production application -- so
-remove it if necessary. By default, <code><a
-href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code> does
-not execute JavaScript so cross-site-scripting is not possible.</p>
-
-<p>Use <code><a
-href="{@docRoot}reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object,%20java.lang.String)">addJavaScriptInterface()</a></code> with
-particular care because it allows JavaScript to invoke operations that are
-normally reserved for Android applications. Only expose <code><a
-href="{@docRoot}reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object,%20java.lang.String)">addJavaScriptInterface()</a></code> to
-sources from which all input is trustworthy. If untrusted input is allowed,
-untrusted JavaScript may be able to invoke Android methods. In general, we
-recommend only exposing <code><a
-href="{@docRoot}reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object,%20java.lang.String)">addJavaScriptInterface()</a></code> to
-JavaScript that is contained within your application APK.</p>
-
-<p>Do not trust information downloaded over HTTP, use HTTPS instead. Even if
-you are connecting only to a single website that you trust or control, HTTP is
-subject to <a
-href="http://en.wikipedia.org/wiki/Man-in-the-middle_attack">MiTM</a> attacks
-and interception of data. Sensitive capabilities using <code><a
-href="{@docRoot}reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object,%20java.lang.String)">addJavaScriptInterface()</a></code> should
-not ever be exposed to unverified script downloaded over HTTP. Note that even
-with the use of HTTPS,
-<code><a
-href="{@docRoot}reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object,%20java.lang.String)">addJavaScriptInterface()</a></code>
-increases the attack surface of your application to include the server
-infrastructure and all CAs trusted by the Android-powered device.</p>
-
-<p>If your application accesses sensitive data with a <code><a
-href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code>, you
-may want to use the <code><a
-href="{@docRoot}reference/android/webkit/WebView.html#clearCache(boolean)">
-clearCache()</a></code> method to delete any files stored locally. Server side
-headers like no-cache can also be used to indicate that an application should
-not cache particular content.</p>
-
-<a name="Input"></a>
-<h2>Performing Input Validation</h2>
-
-<p>Insufficient input validation is one of the most common security problems
-affecting applications, regardless of what platform they run on. Android does
-have platform-level countermeasures that reduce the exposure of applications to
-input validation issues, you should use those features where possible. Also
-note that selection of type-safe languages tends to reduce the likelihood of
-input validation issues. We strongly recommend building your applications with
-the Android SDK.</p>
-
-<p>If you are using native code, then any data read from files, received over
-the network, or received from an IPC has the potential to introduce a security
-issue. The most common problems are <a
-href="http://en.wikipedia.org/wiki/Buffer_overflow">buffer overflows</a>, <a
-href="http://en.wikipedia.org/wiki/Double_free#Use_after_free">use after
-free</a>, and <a
-href="http://en.wikipedia.org/wiki/Off-by-one_error">off-by-one errors</a>.
-Android provides a number of technologies like ASLR and DEP that reduce the
-exploitability of these errors, but they do not solve the underlying problem.
-These can be prevented by careful handling of pointers and managing of
-buffers.</p>
-
-<p>Dynamic, string based languages such as JavaScript and SQL are also subject
-to input validation problems due to escape characters and <a
-href="http://en.wikipedia.org/wiki/Code_injection">script injection</a>.</p>
-
-<p>If you are using data within queries that are submitted to SQL Database or a
-Content Provider, SQL Injection may be an issue. The best defense is to use
-parameterized queries, as is discussed in the ContentProviders section.
-Limiting permissions to read-only or write-only can also reduce the potential
-for harm related to SQL Injection.</p>
-
-<p>If you are using <code><a
-href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code>, then
-you must consider the possibility of XSS. If your application does not
-directly use JavaScript within a <code><a
-href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code>, do
-not call setJavaScriptEnabled() and XSS is no longer possible. If you must
-enable JavaScript then the WebView section provides other security best
-practices.</p>
-
-<p>If you cannot use the security features above, we strongly recommend the use
-of well-structured data formats and verifying that the data conforms to the
-expected format. While blacklisting of characters or character-replacement can
-be an effective strategy, these techniques are error-prone in practice and
-should be avoided when possible.</p>
-
-<a name="UserData"></a>
-<h2>Handling User Data</h2>
-
-<p>In general, the best approach is to minimize use of APIs that access
-sensitive or personal user data. If you have access to data and can avoid
-storing or transmitting the information, do not store or transmit the data.
-Finally, consider if there is a way that your application logic can be
-implemented using a hash or non-reversible form of the data. For example, your
-application might use the hash of an an email address as a primary key, to
-avoid transmitting or storing the email address. This reduces the chances of
-inadvertently exposing data, and it also reduces the chance of attackers
-attempting to exploit your application.</p>
-
-<p>If your application accesses personal information such as passwords or
-usernames, keep in mind that some jurisdictions may require you to provide a
-privacy policy explaining your use and storage of that data. So following the
-security best practice of minimizing access to user data may also simplify
-compliance.</p>
-
-<p>You should also consider whether your application might be inadvertently
-exposing personal information to other parties such as third-party components
-for advertising or third-party services used by your application. If you don't
-know why a component or service requires a personal information, don’t
-provide it. In general, reducing the access to personal information by your
-application will reduce the potential for problems in this area.</p>
-
-<p>If access to sensitive data is required, evaluate whether that information
-must be transmitted to a server, or whether the operation can be performed on
-the client. Consider running any code using sensitive data on the client to
-avoid transmitting user data.</p>
-
-<p>Also, make sure that you do not inadvertently expose user data to other
-application on the device through overly permissive IPC, world writable files,
-or network sockets. This is a special case of permission redelegation,
-discussed in the Requesting Permissions section.</p>
-
-<p>If a GUID is required, create a large, unique number and store it. Do not
-use phone identifiers such as the phone number or IMEI which may be associated
-with personal information. This topic is discussed in more detail in the <a
-href="http://android-developers.blogspot.com/2011/03/identifying-app-installations.html">Android Developer Blog</a>.</p>
-
-<p>Application developers should be careful writing to on-device logs.
-In Android, logs are a shared resource, and are available
-to an application with the
-<a href="{@docRoot}reference/android/Manifest.permission.html#READ_LOGS">
-<code>READ_LOGS</code></a> permission. Even though the phone log data
-is temporary and erased on reboot, inappropriate logging of user information
-could inadvertently leak user data to other applications.</p>
-
-
-<h3>Handling Credentials</h3>
-
-<p>In general, we recommend minimizing the frequency of asking for user
-credentials -- to make phishing attacks more conspicuous, and less likely to be
-successful. Instead use an authorization token and refresh it.</p>
-
-<p>Where possible, username and password should not be stored on the device.
-Instead, perform initial authentication using the username and password
-supplied by the user, and then use a short-lived, service-specific
-authorization token.</p>
-
-<p>Services that will be accessible to multiple applications should be accessed
-using <code>
-<a href="{@docRoot}reference/android/accounts/AccountManager.html">
-AccountManager</a></code>. If possible, use the <code><a
-href="{@docRoot}reference/android/accounts/AccountManager.html">
-AccountManager</a></code> class to invoke a cloud-based service and do not store
-passwords on the device.</p>
-
-<p>After using <code><a
-href="{@docRoot}reference/android/accounts/AccountManager.html">
-AccountManager</a></code> to retrieve an Account, check the <code><a
-href="{@docRoot}reference/android/accounts/Account.html#CREATOR">CREATOR</a>
-</code> before passing in any credentials, so that you do not inadvertently pass
-credentials to the wrong application.</p>
-
-<p>If credentials are to be used only by applications that you create, then you
-can verify the application which accesses the <code><a
-href="{@docRoot}reference/android/accounts/AccountManager.html">
-AccountManager</a></code> using <code><a
-href="{@docRoot}reference/android/content/pm/PackageManager.html#checkSignatures(java.lang.String,%20java.lang.String)">checkSignature()</a></code>.
-Alternatively, if only one application will use the credential, you might use a
-{@link java.security.KeyStore} for
-storage.</p>
-
-<a name="Crypto"></a>
-<h2>Using Cryptography</h2>
-
-<p>In addition to providing data isolation, supporting full-filesystem
-encryption, and providing secure communications channels Android provides a
-wide array of algorithms for protecting data using cryptography.</p>
-
-<p>In general, try to use the highest level of pre-existing framework
-implementation that can support your use case. If you need to securely
-retrieve a file from a known location, a simple HTTPS URI may be adequate and
-require no knowledge of cryptography on your part. If you need a secure
-tunnel, consider using
-<a href="{@docRoot}reference/javax/net/ssl/HttpsURLConnection.html">
-<code>HttpsURLConnection</code></a> or <code><a
-href="{@docRoot}reference/javax/net/ssl/SSLSocket.html">SSLSocket</a></code>,
-rather than writing your own protocol.</p>
-
-<p>If you do find yourself needing to implement your own protocol, we strongly
-recommend that you not implement your own cryptographic algorithms. Use
-existing cryptographic algorithms such as those in the implementation of AES or
-RSA provided in the <code><a
-href="{@docRoot}reference/javax/crypto/Cipher.html">Cipher</a></code> class.</p>
-
-<p>Use a secure random number generator (
-<a href="{@docRoot}reference/java/security/SecureRandom.html">
-<code>SecureRandom</code></a>) to initialize any cryptographic keys (<a
-href="{@docRoot}reference/javax/crypto/KeyGenerator.html">
-<code>KeyGenerator</code></a>). Use of a key that is not generated with a secure random
-number generator significantly weakens the strength of the algorithm, and may
-allow offline attacks.</p>
-
-<p>If you need to store a key for repeated use, use a mechanism like {@link java.security.KeyStore} that
-provides a mechanism for long term storage and retrieval of cryptographic
-keys.</p>
-
-<h2>Conclusion</h2>
-
-<p>Android provides developers with the ability to design applications with a
-broad range of security requirements. These best practices will help you make
-sure that your application takes advantage of the security benefits provided by
-the platform.</p>
-
-<p>You can receive more information on these topics and discuss security best
-practices with other developers in the <a
-href="http://groups.google.com/group/android-security-discuss">Android Security
-Discuss</a> Google Group</p>
diff --git a/docs/html/guide/topics/sensors/sensors_motion.jd b/docs/html/guide/topics/sensors/sensors_motion.jd
index b6c3cb4..289c639 100644
--- a/docs/html/guide/topics/sensors/sensors_motion.jd
+++ b/docs/html/guide/topics/sensors/sensors_motion.jd
@@ -1,6 +1,5 @@
page.title=Motion Sensors
-parent.title=Sensors
-parent.link=index.html
+page.tags="sensorevent","accelerometer","gyroscope","gravity","rotation"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/sensors/sensors_position.jd b/docs/html/guide/topics/sensors/sensors_position.jd
index 869109b..55b282b 100644
--- a/docs/html/guide/topics/sensors/sensors_position.jd
+++ b/docs/html/guide/topics/sensors/sensors_position.jd
@@ -1,6 +1,5 @@
page.title=Position Sensors
-parent.title=Sensors
-parent.link=index.html
+page.tags="sensorevent","orientation","proximity"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/text/copy-paste.jd b/docs/html/guide/topics/text/copy-paste.jd
index 6c86f47..b34f0fa 100644
--- a/docs/html/guide/topics/text/copy-paste.jd
+++ b/docs/html/guide/topics/text/copy-paste.jd
@@ -1,4 +1,5 @@
page.title=Copy and Paste
+page.tags="clipboardmanager","clipdata","input"
@jd:body
<div id="qv-wrapper">
<div id="qv">
diff --git a/docs/html/guide/topics/text/creating-input-method.jd b/docs/html/guide/topics/text/creating-input-method.jd
index 7086824..7254594 100644
--- a/docs/html/guide/topics/text/creating-input-method.jd
+++ b/docs/html/guide/topics/text/creating-input-method.jd
@@ -1,5 +1,5 @@
page.title=Creating an Input Method
-parent.title=Articles
+page.tags="ime","keyboard","inputmethodservice"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/text/spell-checker-framework.jd b/docs/html/guide/topics/text/spell-checker-framework.jd
index 7f7a0b8..366f9cc 100644
--- a/docs/html/guide/topics/text/spell-checker-framework.jd
+++ b/docs/html/guide/topics/text/spell-checker-framework.jd
@@ -1,5 +1,5 @@
page.title=Spelling Checker Framework
-parent.title=Articles
+page.tags="input","spellcheckerservice"
@jd:body
<div id="qv-wrapper">
<div id="qv">
diff --git a/docs/html/guide/topics/ui/controls/button.jd b/docs/html/guide/topics/ui/controls/button.jd
index 8d48e9c..41b67b7 100644
--- a/docs/html/guide/topics/ui/controls/button.jd
+++ b/docs/html/guide/topics/ui/controls/button.jd
@@ -1,6 +1,5 @@
page.title=Buttons
-parent.title=Input Controls
-parent.link=../controls.html
+page.tags="button","imagebutton"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/ui/controls/checkbox.jd b/docs/html/guide/topics/ui/controls/checkbox.jd
index ea70980..99140b5 100644
--- a/docs/html/guide/topics/ui/controls/checkbox.jd
+++ b/docs/html/guide/topics/ui/controls/checkbox.jd
@@ -1,6 +1,5 @@
page.title=Checkboxes
-parent.title=Input Controls
-parent.link=../controls.html
+
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/ui/controls/pickers.jd b/docs/html/guide/topics/ui/controls/pickers.jd
index cf90f1d..a0e7afb 100644
--- a/docs/html/guide/topics/ui/controls/pickers.jd
+++ b/docs/html/guide/topics/ui/controls/pickers.jd
@@ -1,6 +1,5 @@
-page.title= Pickers
-parent.title=Form Controls
-parent.link=controls-form.html
+page.title=Pickers
+page.tags="datepicker","timepicker"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/ui/controls/radiobutton.jd b/docs/html/guide/topics/ui/controls/radiobutton.jd
index c96e576..d0c48ed 100644
--- a/docs/html/guide/topics/ui/controls/radiobutton.jd
+++ b/docs/html/guide/topics/ui/controls/radiobutton.jd
@@ -1,6 +1,5 @@
page.title=Radio Buttons
-parent.title=Input Controls
-parent.link=../controls.html
+page.tags="radiobutton","radiogroup"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/ui/controls/spinner.jd b/docs/html/guide/topics/ui/controls/spinner.jd
index deba3e6..85714b6 100644
--- a/docs/html/guide/topics/ui/controls/spinner.jd
+++ b/docs/html/guide/topics/ui/controls/spinner.jd
@@ -1,6 +1,5 @@
-page.title= Spinners
-parent.title=Input Controls
-parent.link=../controls.html
+page.title=Spinners
+page.tags="adapterview","spinneradapter"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/ui/controls/text.jd b/docs/html/guide/topics/ui/controls/text.jd
index 654883d..c0b9873 100644
--- a/docs/html/guide/topics/ui/controls/text.jd
+++ b/docs/html/guide/topics/ui/controls/text.jd
@@ -1,6 +1,5 @@
page.title=Text Fields
-parent.title=Input Controls
-parent.link=../controls.html
+page.tags="edittext","autocompletetextview"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/ui/controls/togglebutton.jd b/docs/html/guide/topics/ui/controls/togglebutton.jd
index dd7634b..3119cd9 100644
--- a/docs/html/guide/topics/ui/controls/togglebutton.jd
+++ b/docs/html/guide/topics/ui/controls/togglebutton.jd
@@ -1,6 +1,5 @@
page.title=Toggle Buttons
-parent.title=Input Controls
-parent.link=../controls.html
+page.tags="switch","togglebutton"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/ui/custom-components.jd b/docs/html/guide/topics/ui/custom-components.jd
index be82dbc..703a5ce 100644
--- a/docs/html/guide/topics/ui/custom-components.jd
+++ b/docs/html/guide/topics/ui/custom-components.jd
@@ -1,6 +1,5 @@
page.title=Custom Components
-parent.title=User Interface
-parent.link=index.html
+page.tags="view","widget"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/ui/declaring-layout.jd b/docs/html/guide/topics/ui/declaring-layout.jd
index 28e1418..6398646 100644
--- a/docs/html/guide/topics/ui/declaring-layout.jd
+++ b/docs/html/guide/topics/ui/declaring-layout.jd
@@ -1,6 +1,5 @@
page.title=Layouts
-parent.title=User Interface
-parent.link=index.html
+page.tags="view","viewgroup"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/ui/dialogs.jd b/docs/html/guide/topics/ui/dialogs.jd
index 3cfed13..7f48eac 100644
--- a/docs/html/guide/topics/ui/dialogs.jd
+++ b/docs/html/guide/topics/ui/dialogs.jd
@@ -1,4 +1,6 @@
page.title=Dialogs
+page.tags="alertdialog","dialogfragment"
+
@jd:body
diff --git a/docs/html/guide/topics/ui/drag-drop.jd b/docs/html/guide/topics/ui/drag-drop.jd
index 884a1b2..e989374 100644
--- a/docs/html/guide/topics/ui/drag-drop.jd
+++ b/docs/html/guide/topics/ui/drag-drop.jd
@@ -1,6 +1,5 @@
page.title=Drag and Drop
-parent.title=User Interface
-parent.link=index.html
+page.tags="clipdata","dragevent","onlongclicklistener"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/ui/layout-objects.jd b/docs/html/guide/topics/ui/layout-objects.jd
deleted file mode 100644
index 1d15ad6..0000000
--- a/docs/html/guide/topics/ui/layout-objects.jd
+++ /dev/null
@@ -1,6 +0,0 @@
-page.title=Layouts
-parent.title=User Interface
-parent.link=index.html
-@jd:body
-
-<p>You should have been redirected to <a href="declaring-layout.html">Layouts</a>.</p> \ No newline at end of file
diff --git a/docs/html/guide/topics/ui/layout/gridview.jd b/docs/html/guide/topics/ui/layout/gridview.jd
index 84c3dab..bc189c4 100644
--- a/docs/html/guide/topics/ui/layout/gridview.jd
+++ b/docs/html/guide/topics/ui/layout/gridview.jd
@@ -1,6 +1,5 @@
page.title=Grid View
-parent.title=Layouts
-parent.link=layout-objects.html
+page.tags="gridview"
@jd:body
<div id="qv-wrapper">
<div id="qv">
diff --git a/docs/html/guide/topics/ui/layout/linear.jd b/docs/html/guide/topics/ui/layout/linear.jd
index 8e33706..444dc71 100644
--- a/docs/html/guide/topics/ui/layout/linear.jd
+++ b/docs/html/guide/topics/ui/layout/linear.jd
@@ -1,6 +1,5 @@
page.title=Linear Layout
-parent.title=Layouts
-parent.link=layout-objects.html
+page.tags="linearlayout"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/ui/layout/listview.jd b/docs/html/guide/topics/ui/layout/listview.jd
index fee5292..6cdd725 100644
--- a/docs/html/guide/topics/ui/layout/listview.jd
+++ b/docs/html/guide/topics/ui/layout/listview.jd
@@ -1,6 +1,5 @@
page.title=List View
-parent.title=Layouts
-parent.link=declaring-layout.html
+page.tags="listview"
@jd:body
<div id="qv-wrapper">
<div id="qv">
diff --git a/docs/html/guide/topics/ui/layout/relative.jd b/docs/html/guide/topics/ui/layout/relative.jd
index 47f9417..65c5617 100644
--- a/docs/html/guide/topics/ui/layout/relative.jd
+++ b/docs/html/guide/topics/ui/layout/relative.jd
@@ -1,6 +1,5 @@
page.title=Relative Layout
-parent.title=Layouts
-parent.link=layout-objects.html
+page.tags="relativelayout"
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/guide/topics/ui/notifiers/index.jd b/docs/html/guide/topics/ui/notifiers/index.jd
deleted file mode 100644
index caf0df7..0000000
--- a/docs/html/guide/topics/ui/notifiers/index.jd
+++ /dev/null
@@ -1,92 +0,0 @@
-page.title=Notifications
-parent.title=User Interface
-parent.link=../index.html
-@jd:body
-
-<p>Several types of situations may arise that require you to notify the user
-about an event that occurs in your application. Some events require the user to respond
-and others do not. For example:</p>
-<ul>
- <li>When an event such as saving a file is complete, a small message
-should appear to confirm that the save was successful.</li>
- <li>If the application is running in the background and needs the user's attention,
-the application should create a notification that allows the user to respond at
-his or her convenience.</li>
- <li>If the application is
-performing work that the user must wait for (such as loading a file),
-the application should show a hovering progress wheel or bar.</li>
-</ul>
-
-<p>Each of these notification tasks can be achieved using a different technique:</p>
-<ul>
- <li>A <a href="#Toast">Toast Notification</a>, for brief messages that come
- from the background.</li>
- <li>A <a href="#StatusBar">Status Notification</a>, for persistent reminders
- that come from the background and request the user's response.</li>
- <li>A <a href="#Dialog">Dialog Notification</a>, for Activity-related notifications.</li>
-</ul>
-
-<p>This document summarizes each of these techniques for notifying the user and includes
-links to full documentation.</p>
-
-
-<h2 id="Toast">Toast Notification</h2>
-
-<img src="{@docRoot}images/toast.png" alt="" style="float:right" />
-
-<p>A toast notification is a message that pops up on the surface of the window.
-It only fills the amount of space required for the message and the user's current
-activity remains visible and interactive. The notification automatically fades in and
-out, and does not accept interaction events. Because a toast can be created from a background
-{@link android.app.Service}, it appears even if the application isn't visible.</p>
-
-<p>A toast is best for short text messages, such as "File saved,"
-when you're fairly certain the user is paying attention
-to the screen. A toast can not accept user interaction events; if you'd like
-the user to respond and take action, consider using a
-<a href="#StatusBar">Status Notification</a> instead.</p>
-
-<p>For more information, refer to <a href="toasts.html">Toast Notifications</a>.</p>
-
-
-<h2 id="StatusBar">Status Notification</h2>
-
-<img src="{@docRoot}images/notifications_window.png" alt="" style="float:right; clear:right;" />
-
-<p>A status notification adds an icon to the system's status bar
-(with an optional ticker-text message) and an expanded message in the "Notifications" window.
-When the user selects the expanded message, Android fires an
-{@link android.content.Intent} that is defined by the notification (usually to launch an
-{@link android.app.Activity}).
-You can also configure the notification to alert the user with a sound, a vibration, and flashing
-lights on the device.</p>
-
-<p>This kind of notification is ideal when your application is working in
-a background {@link android.app.Service} and needs to
-notify the user about an event. If you need to alert the user about an event that occurs
-while your Activity is still in focus, consider using a
-<a href="#Dialog">Dialog Notification</a> instead.</p>
-
-<p>For more information, refer to
-<a href="notifications.html">Status Notifications</a>.</p>
-
-
-<h2 id="Dialog">Dialog Notification</h2>
-
-<img src="{@docRoot}images/dialog_progress_spinning.png" alt="" style="float:right" />
-
-<p>A dialog is usually a small window that appears in front of the current Activity.
-The underlying Activity loses focus and the dialog accepts all user interaction.
-Dialogs are normally used
-for notifications and short activities that directly relate to the application in progress.</p>
-
-<p>You should use a dialog when you need to show a progress bar or a short
-message that requires confirmation from the user (such as an alert with "OK" and "Cancel" buttons).
-You can use also use dialogs as integral components
-in your application's UI and for other purposes besides notifications.
-For a complete discussion on all the available types of dialogs,
-including its uses for notifications, refer to
-<a href="{@docRoot}guide/topics/ui/dialogs.html">Dialogs</a>.</p>
-
-
-
diff --git a/docs/html/guide/topics/ui/settings.jd b/docs/html/guide/topics/ui/settings.jd
index 33e164b..d96447d 100644
--- a/docs/html/guide/topics/ui/settings.jd
+++ b/docs/html/guide/topics/ui/settings.jd
@@ -1,4 +1,6 @@
page.title=Settings
+page.tags="preference","preferenceactivity","preferencefragment"
+
@jd:body
diff --git a/docs/html/images/example-bad.png b/docs/html/images/example-bad.png
new file mode 100644
index 0000000..b19a9f7
--- /dev/null
+++ b/docs/html/images/example-bad.png
Binary files differ
diff --git a/docs/html/images/example-good.png b/docs/html/images/example-good.png
new file mode 100644
index 0000000..6bd2408
--- /dev/null
+++ b/docs/html/images/example-good.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-eula-violation.png b/docs/html/images/gp-policy-ads-eula-violation.png
new file mode 100644
index 0000000..e8ffa5b
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-eula-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-eula.png b/docs/html/images/gp-policy-ads-eula.png
new file mode 100644
index 0000000..68a6b95
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-eula.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-impersonate-violation.png b/docs/html/images/gp-policy-ads-impersonate-violation.png
new file mode 100644
index 0000000..385ae6e
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-impersonate-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-maturity-violation.png b/docs/html/images/gp-policy-ads-maturity-violation.png
new file mode 100644
index 0000000..d41870e
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-maturity-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-notif-attr-violation.png b/docs/html/images/gp-policy-ads-notif-attr-violation.png
new file mode 100644
index 0000000..af53f10
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-notif-attr-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-notif-attr.png b/docs/html/images/gp-policy-ads-notif-attr.png
new file mode 100644
index 0000000..4934d21
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-notif-attr.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-paywall-violation.png b/docs/html/images/gp-policy-ads-paywall-violation.png
new file mode 100644
index 0000000..8bbfd1b
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-paywall-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-paywall.png b/docs/html/images/gp-policy-ads-paywall.png
new file mode 100644
index 0000000..e7b1e19
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-paywall.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-terms.png b/docs/html/images/gp-policy-ads-terms.png
new file mode 100644
index 0000000..dcbdf4a
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-terms.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ip-copyright-violation.png b/docs/html/images/gp-policy-ip-copyright-violation.png
new file mode 100644
index 0000000..a4e96a8
--- /dev/null
+++ b/docs/html/images/gp-policy-ip-copyright-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ip-impersonation-violation.png b/docs/html/images/gp-policy-ip-impersonation-violation.png
new file mode 100644
index 0000000..b1d9923
--- /dev/null
+++ b/docs/html/images/gp-policy-ip-impersonation-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ip-trademark-violation.png b/docs/html/images/gp-policy-ip-trademark-violation.png
new file mode 100644
index 0000000..c05b67b
--- /dev/null
+++ b/docs/html/images/gp-policy-ip-trademark-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-spam-negreview.png b/docs/html/images/gp-policy-spam-negreview.png
new file mode 100644
index 0000000..f68eba3
--- /dev/null
+++ b/docs/html/images/gp-policy-spam-negreview.png
Binary files differ
diff --git a/docs/html/images/gp-policy-spam-reqrating.png b/docs/html/images/gp-policy-spam-reqrating.png
new file mode 100644
index 0000000..aaf9e53
--- /dev/null
+++ b/docs/html/images/gp-policy-spam-reqrating.png
Binary files differ
diff --git a/docs/html/sdk/1.0_r1/index.jd b/docs/html/sdk/1.0_r1/index.jd
index b380483..dea6620 100644
--- a/docs/html/sdk/1.0_r1/index.jd
+++ b/docs/html/sdk/1.0_r1/index.jd
@@ -1,5 +1,6 @@
page.title=Android 1.0 SDK, release 1
sdk.redirect=true
sdk.redirect.path=index.html
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/sdk/1.0_r1/upgrading.jd b/docs/html/sdk/1.0_r1/upgrading.jd
index d6d5dc4..d6a7ed5 100644
--- a/docs/html/sdk/1.0_r1/upgrading.jd
+++ b/docs/html/sdk/1.0_r1/upgrading.jd
@@ -1,5 +1,6 @@
page.title=Upgrading the SDK
sdk.version=1.0_r1
+excludeFromSuggestions=true
@jd:body
<p>For the current SDK release, see the links under <strong>Current SDK Release</strong> in the side navigation.</p>
diff --git a/docs/html/sdk/1.0_r2/index.jd b/docs/html/sdk/1.0_r2/index.jd
index c29c148..8556e3c 100644
--- a/docs/html/sdk/1.0_r2/index.jd
+++ b/docs/html/sdk/1.0_r2/index.jd
@@ -1,5 +1,6 @@
page.title=Android 1.0 SDK, release 2
sdk.redirect=true
sdk.redirect.path=index.html
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/sdk/1.0_r2/upgrading.jd b/docs/html/sdk/1.0_r2/upgrading.jd
index 409e30e..243950d 100644
--- a/docs/html/sdk/1.0_r2/upgrading.jd
+++ b/docs/html/sdk/1.0_r2/upgrading.jd
@@ -1,5 +1,6 @@
page.title=Upgrading the SDK
sdk.version=1.0_r2
+excludeFromSuggestions=true
@jd:body
<p>For the current SDK release, see the links under <strong>Current SDK Release</strong> in the side navigation.</p>
diff --git a/docs/html/sdk/1.1_r1/index.jd b/docs/html/sdk/1.1_r1/index.jd
index 63fe51d..44231ee 100644
--- a/docs/html/sdk/1.1_r1/index.jd
+++ b/docs/html/sdk/1.1_r1/index.jd
@@ -1,5 +1,6 @@
page.title=Android 1.1 SDK, Release 1
sdk.redirect=true
sdk.redirect.path=index.html
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/sdk/1.1_r1/upgrading.jd b/docs/html/sdk/1.1_r1/upgrading.jd
index bc71149..840ae6b 100644
--- a/docs/html/sdk/1.1_r1/upgrading.jd
+++ b/docs/html/sdk/1.1_r1/upgrading.jd
@@ -1,5 +1,6 @@
page.title=Upgrading the SDK
sdk.version=1.1_r1
+excludeFromSuggestions=true
@jd:body
<!--
diff --git a/docs/html/sdk/1.5_r1/index.jd b/docs/html/sdk/1.5_r1/index.jd
index 60dfc14..7232f57 100644
--- a/docs/html/sdk/1.5_r1/index.jd
+++ b/docs/html/sdk/1.5_r1/index.jd
@@ -1,5 +1,6 @@
page.title=Android 1.5 SDK, Release 1
sdk.redirect=true
sdk.redirect.path=index.html
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/sdk/1.5_r1/upgrading.jd b/docs/html/sdk/1.5_r1/upgrading.jd
index 0a12d62..0377069 100644
--- a/docs/html/sdk/1.5_r1/upgrading.jd
+++ b/docs/html/sdk/1.5_r1/upgrading.jd
@@ -1,5 +1,6 @@
page.title=Upgrading the SDK
sdk.version=1.5_r1
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/sdk/1.5_r2/index.jd b/docs/html/sdk/1.5_r2/index.jd
index 4fb99b6..fac4f13 100644
--- a/docs/html/sdk/1.5_r2/index.jd
+++ b/docs/html/sdk/1.5_r2/index.jd
@@ -1,5 +1,6 @@
page.title=Android 1.5 SDK, Release 2
sdk.redirect=true
sdk.redirect.path=index.html
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/sdk/1.5_r2/upgrading.jd b/docs/html/sdk/1.5_r2/upgrading.jd
index bb5fc60..31b2358 100644
--- a/docs/html/sdk/1.5_r2/upgrading.jd
+++ b/docs/html/sdk/1.5_r2/upgrading.jd
@@ -1,5 +1,6 @@
page.title=Upgrading the SDK
sdk.version=1.5_r2
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/sdk/1.5_r3/index.jd b/docs/html/sdk/1.5_r3/index.jd
index eb10f5e..e8cfaa1 100644
--- a/docs/html/sdk/1.5_r3/index.jd
+++ b/docs/html/sdk/1.5_r3/index.jd
@@ -1,5 +1,6 @@
page.title=Android 1.5 SDK, Release 3
sdk.redirect=true
sdk.redirect.path=index.html
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/sdk/1.5_r3/upgrading.jd b/docs/html/sdk/1.5_r3/upgrading.jd
index 18c1314..62b9a78 100644
--- a/docs/html/sdk/1.5_r3/upgrading.jd
+++ b/docs/html/sdk/1.5_r3/upgrading.jd
@@ -1,6 +1,7 @@
page.title=Upgrading the SDK
sdk.version=1.5
sdk.rel.id=3
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/sdk/1.6_r1/index.jd b/docs/html/sdk/1.6_r1/index.jd
index e7f9112..671d1cd 100644
--- a/docs/html/sdk/1.6_r1/index.jd
+++ b/docs/html/sdk/1.6_r1/index.jd
@@ -1,5 +1,6 @@
page.title=Android 1.6 SDK, Release 1
sdk.redirect=true
sdk.redirect.path=index.html
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/sdk/1.6_r1/upgrading.jd b/docs/html/sdk/1.6_r1/upgrading.jd
index 49535c9..e6dded0 100644
--- a/docs/html/sdk/1.6_r1/upgrading.jd
+++ b/docs/html/sdk/1.6_r1/upgrading.jd
@@ -1,5 +1,6 @@
page.title=Upgrading the SDK
sdk.version=1.6
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/sdk/installing/next.jd b/docs/html/sdk/installing/next.jd
deleted file mode 100644
index cb974a4..0000000
--- a/docs/html/sdk/installing/next.jd
+++ /dev/null
@@ -1,51 +0,0 @@
-page.title=Next Steps
-
-@jd:body
-
-
-<p>Now that you've installed the Android SDK, here are are a few ways to learn Android
-and start developing: </p>
-
-<h3>Start coding</h3>
-<ul>
- <li>Follow the training class for <a
-href="{@docRoot}training/basics/firstapp/index.html">Building Your First App</a>.
- <p>This class is an essential first step for new Android developers.</p>
- <p>It gives you step by step instructions for building a simple app. You’ll learn how to
-create an Android project and run a debuggable version of the app. You'll also learn some
-fundamentals of Android app design, including how to build a simple user interface and handle user
-input.</p>
-</li>
-</ul>
-
-
-<h3>Learn how to design your app</h3>
-<ul>
- <li>Learn the best practices for Android design and user experience by reading the Android <a
-href="{@docRoot}design/index.html">Design</a> guidelines.</li>
-</ul>
-
-
-<h3>Read up on the API framework</h3>
-<ul>
- <li>Start reading about fundamental framework topics in the collection of <a
-href="{@docRoot}guide/components/index.html">API Guides</a>.</li>
- <li>Browse the API specifications in the <a
- href="{@docRoot}reference/packages.html">Reference</a>.</li>
-</ul>
-
-
-<h3>Explore the development tools</h3>
-<ul>
- <li>Learn about developing an app with the Android Developer Tools plugin for Eclipse
- and other tools from the <a
- href="{@docRoot}tools/workflow/index.html">Workflow</a>.</li>
-</ul>
-
-
-<h3>Explore some code</h3>
-
-<ul>
- <li>Browse the sample apps available from the Android SDK Manager. You'll find them in
-<code><em>&lt;sdk&gt;</em>/samples/<em>&lt;platform-version&gt;/</em></code>. </li>
-</ul>
diff --git a/docs/html/tools/debugging/debugging-ui.jd b/docs/html/tools/debugging/debugging-ui.jd
index 8ca5192..f927d08 100644
--- a/docs/html/tools/debugging/debugging-ui.jd
+++ b/docs/html/tools/debugging/debugging-ui.jd
@@ -60,7 +60,9 @@ Sometimes your application's layout can slow down your application.
<p>The Hierarchy Viewer application allows you to debug and optimize your user interface. It
provides a visual representation of the layout's View hierarchy (the View Hierarchy window)
- and a magnified view of the display (the Pixel Perfect window).</p>
+ with performance information for each node in the layout,
+ and a magnified view of the display (the Pixel Perfect window) to closely examine the pixels
+ in your layout.</p>
<p>Android <code>lint</code> is a static code scanning tool that helps you optimize the layouts and layout
hierarchies of your applications, as well as detect other common coding problems. You can run it against your layout files or resource
diff --git a/docs/html/tools/extras/oem-usb.jd b/docs/html/tools/extras/oem-usb.jd
index 774fe87..87734a1 100644
--- a/docs/html/tools/extras/oem-usb.jd
+++ b/docs/html/tools/extras/oem-usb.jd
@@ -306,6 +306,10 @@ href="http://developer.motorola.com/docstools/USB_Drivers/">http://developer.mot
<tr><td>MTK</td> <td><a
href="http://online.mediatek.com/Public%20Documents/MTK_Android_USB_Driver.zip">http://online.mediatek.com/Public%20Documents/MTK_Android_USB_Driver.zip</a></td>
</tr>
+<tr><td>Oppo</td> <td><a
+href="http://www.oppo.com/index.php?q=software/view&sw_id=631"
+>http://www.oppo.com/index.php?q=software/view&sw_id=631</a></td>
+</tr>
<tr><td>Pantech</td> <td><a
href="http://www.isky.co.kr/cs/software/software.sky?fromUrl=index">http://www.isky.co.kr/cs/software/software.sky?fromUrl=index</a></td>
</tr><tr><td>Pegatron</td> <td><a
@@ -327,6 +331,9 @@ href="http://www.teleepoch.com/android.html">http://www.teleepoch.com/android.ht
href="http://www.yulong.com/product/product/product/downloadList.html#downListUL">http://www.yulong.com/product/product/product/downloadList.html#downListUL</a></td>
</tr>
+<tr><td>Xiaomi</td> <td><a
+href="http://www.xiaomi.com/c/driver/index.html">http://www.xiaomi.com/c/driver/index.html</a></td>
+</tr>
<tr>
<td>ZTE</td> <td><a
href="http://support.zte.com.cn/support/news/NewsDetail.aspx?newsId=1000442">http://support.zte.com.cn/support/news/NewsDetail.aspx?newsId=1000442</a></td></tr>
diff --git a/docs/html/tools/sdk/OLD_RELEASENOTES.jd b/docs/html/tools/sdk/OLD_RELEASENOTES.jd
index 6865db2..b7fd12f 100644
--- a/docs/html/tools/sdk/OLD_RELEASENOTES.jd
+++ b/docs/html/tools/sdk/OLD_RELEASENOTES.jd
@@ -1,4 +1,5 @@
page.title=Release Notes for Older SDK Versions
+excludeFromSuggestions=true
@jd:body
<div class="special">
diff --git a/docs/html/tools/sdk/RELEASENOTES.jd b/docs/html/tools/sdk/RELEASENOTES.jd
index c7ece42..cbcbb12 100644
--- a/docs/html/tools/sdk/RELEASENOTES.jd
+++ b/docs/html/tools/sdk/RELEASENOTES.jd
@@ -1,4 +1,5 @@
page.title=SDK Release Notes
+excludeFromSuggestions=true
@jd:body
<p>This document provides version-specific information about Android SDK
diff --git a/docs/html/tools/sdk/addons.jd b/docs/html/tools/sdk/addons.jd
deleted file mode 100644
index 8c5e1ed..0000000
--- a/docs/html/tools/sdk/addons.jd
+++ /dev/null
@@ -1,9 +0,0 @@
-page.title=SDK Add-Ons
-
-@jd:body
-
-
-
-<p>A page that lists SDK addons and links to release notes. Links to dashboards etc.</p>
-
-
diff --git a/docs/html/tools/sdk/adt-notes.jd b/docs/html/tools/sdk/adt-notes.jd
deleted file mode 100644
index 291b543..0000000
--- a/docs/html/tools/sdk/adt-notes.jd
+++ /dev/null
@@ -1,5 +0,0 @@
-page.title=ADT Plugin for Eclipse
-sdk.redirect=true
-sdk.redirect.path=eclipse-adt.html
-
-@jd:body
diff --git a/docs/html/tools/sdk/adt_download.html b/docs/html/tools/sdk/adt_download.html
deleted file mode 100644
index 5ba2ef5..0000000
--- a/docs/html/tools/sdk/adt_download.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<html>
-<head>
-<meta http-equiv="refresh" content="0;url=http://developer.android.com/sdk/eclipse-adt.html">
-<title>Redirecting...</title>
-</head>
-<body>
-<p>You should be redirected. Please <a
-href="http://developer.android.com/sdk/eclipse-adt.html">click here</a>.</p>
-</body>
-</html> \ No newline at end of file
diff --git a/docs/html/tools/sdk/libraries.jd b/docs/html/tools/sdk/libraries.jd
deleted file mode 100644
index 9e47c4a..0000000
--- a/docs/html/tools/sdk/libraries.jd
+++ /dev/null
@@ -1,9 +0,0 @@
-page.title=Libraries
-
-@jd:body
-
-
-
-<p>A page that lists libraries and links to release notes. Links to dashboards etc.</p>
-
-
diff --git a/docs/html/tools/sdk/ndk/1.5_r1/index.jd b/docs/html/tools/sdk/ndk/1.5_r1/index.jd
index 4c70a8a..2f6764b 100644
--- a/docs/html/tools/sdk/ndk/1.5_r1/index.jd
+++ b/docs/html/tools/sdk/ndk/1.5_r1/index.jd
@@ -1,6 +1,7 @@
page.title=Android 1.5 NDK, Release 1
sdk.redirect=true
sdk.redirect.path=ndk/index.html
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/tools/sdk/ndk/1.6_r1/index.jd b/docs/html/tools/sdk/ndk/1.6_r1/index.jd
index 090dcdc..1dc5b6f 100644
--- a/docs/html/tools/sdk/ndk/1.6_r1/index.jd
+++ b/docs/html/tools/sdk/ndk/1.6_r1/index.jd
@@ -1,5 +1,6 @@
page.title=Android 1.6 NDK, Release 1
sdk.redirect=true
sdk.redirect.path=ndk/index.html
+excludeFromSuggestions=true
@jd:body
diff --git a/docs/html/tools/sdk/ndk/overview.jd b/docs/html/tools/sdk/ndk/overview.jd
deleted file mode 100644
index 98ef1fc..0000000
--- a/docs/html/tools/sdk/ndk/overview.jd
+++ /dev/null
@@ -1,588 +0,0 @@
-page.title=What is the NDK?
-@jd:body
-
- <div id="qv-wrapper">
- <div id="qv">
- <h2>In this document</h2>
-
- <ol>
- <li><a href="#choosing">When to Develop in Native Code</a></li>
- <li>
- <a href="#contents">Contents of the NDK</a>
- <ol>
- <li><a href="#tools">Development tools</a></li>
-
- <li><a href="#docs">Documentation</a></li>
-
- <li><a href="#samples">Sample applications</a></li>
- </ol>
- </li>
- <li><a href="#reqs">System and Software Requirements</a></li>
- </ol>
- </div>
- </div>
-
- <p>The Android NDK is a toolset that lets you embed components that make use of native code in
- your Android applications.</p>
-
- <p>Android applications run in the Dalvik virtual machine. The NDK allows you to implement parts
- of your applications using native-code languages such as C and C++. This can provide benefits to
- certain classes of applications, in the form of reuse of existing code and in some cases
- increased speed.</p>
-
- <p>The NDK provides:</p>
-
- <ul>
- <li>A set of tools and build files used to generate native code libraries from C and C++
- sources</li>
-
- <li>A way to embed the corresponding native libraries into an application package file
- (<code>.apk</code>) that can be deployed on Android devices</li>
-
- <li>A set of native system headers and libraries that will be supported in all future versions
- of the Android platform, starting from Android 1.5. Applications that use native activities
- must be run on Android 2.3 or later.</li>
-
- <li>Documentation, samples, and tutorials</li>
- </ul>
-
- <p>The latest release of the NDK supports the following instruction sets:</p>
-
- <ul>
- <li>ARMv5TE, including Thumb-1 instructions (see {@code docs/CPU-ARCH-ABIS.html} for more
-information)</li>
-
- <li>ARMv7-A, including Thumb-2 and VFPv3-D16 instructions, with optional support for
- NEON/VFPv3-D32 instructions (see {@code docs/CPU-ARM-NEON.html} for more information)</li>
-
- <li>x86 instructions (see {@code docs/CPU-X86.html} for more information)</li>
-
- <li>MIPS instructions (see {@code docs/CPU-MIPS.html} for more information)</li>
- </ul>
-
- <p>ARMv5TE machine code will run on all ARM-based Android devices. ARMv7-A will run only on
- devices such as the Verizon Droid or Google Nexus One that have a compatible CPU. The main
- difference between the two instruction sets is that ARMv7-A supports hardware FPU, Thumb-2, and
- NEON instructions. You can target either or both of the instruction sets &mdash; ARMv5TE is the
- default, but switching to ARMv7-A is as easy as adding a single line to the application's
- <code>Application.mk</code> file, without needing to change anything else in the file. You can also build for
- both architectures at the same time and have everything stored in the final <code>.apk</code>.
- Complete information is provided in the CPU-ARCH-ABIS.HTML in the NDK package.</p>
-
- <p>The NDK provides stable headers for libc (the C library), libm (the Math library), OpenGL ES
- (3D graphics library), the JNI interface, and other libraries, as listed in the <a href=
- "#tools">Development Tools</a> section.</p>
-
- <h2 id="choosing">When to Develop in Native Code</h2>
-
- <p>The NDK will not benefit most applications. As a developer, you need to balance its benefits
- against its drawbacks; notably, using native code does not result in an automatic performance
- increase, but always increases application complexity. In general, you should only use native
- code if it is essential to your application, not just because you prefer to program in C/C++.</p>
-
- <p>Typical good candidates for the NDK are self-contained, CPU-intensive operations that don't
- allocate much memory, such as signal processing, physics simulation, and so on. Simply re-coding
- a method to run in C usually does not result in a large performance increase. When examining
- whether or not you should develop in native code, think about your requirements and see if the
- Android framework APIs provide the functionality that you need. The NDK can, however, can be an
- effective way to reuse a large corpus of existing C/C++ code.</p>
-
- <p>The Android framework provides two ways to use native code:</p>
-
- <ul>
- <li>Write your application using the Android framework and use JNI to access the APIs provided
- by the Android NDK. This technique allows you to take advantage of the convenience of the
- Android framework, but still allows you to write native code when necessary. If you use this
- approach, your application must target specific, minimum Android platform levels, see <a
- href="#platform-compat">Android platform compatibility</a> for more information.</li>
-
- <li>
- <p>Write a native activity, which allows you to implement the lifecycle callbacks in native
- code. The Android SDK provides the {@link android.app.NativeActivity} class, which is a
- convenience class that notifies your
- native code of any activity lifecycle callbacks (<code>onCreate()</code>, <code>onPause()</code>,
- <code>onResume()</code>, etc). You can implement the callbacks in your native code to handle
- these events when they occur. Applications that use native activities must be run on Android
- 2.3 (API Level 9) or later.</p>
-
- <p>You cannot access features such as Services and Content Providers natively, so if you want
- to use them or any other framework API, you can still write JNI code to do so.</p>
- </li>
- </ul>
-
- <h2 id="contents">Contents of the NDK</h2>The NDK contains the APIs, documentation, and sample
- applications that help you write your native code.
-
- <h3 id="tools">Development tools</h3>
-
- <p>The NDK includes a set of cross-toolchains (compilers, linkers, etc..) that can generate
- native ARM binaries on Linux, OS X, and Windows (with Cygwin) platforms.</p>
-
- <p>It provides a set of system headers for stable native APIs that are guaranteed to be supported
- in all later releases of the platform:</p>
-
- <ul>
- <li>libc (C library) headers</li>
-
- <li>libm (math library) headers</li>
-
- <li>JNI interface headers</li>
-
- <li>libz (Zlib compression) headers</li>
-
- <li>liblog (Android logging) header</li>
-
- <li>OpenGL ES 1.1 and OpenGL ES 2.0 (3D graphics libraries) headers</li>
-
- <li>libjnigraphics (Pixel buffer access) header (for Android 2.2 and above).</li>
-
- <li>A Minimal set of headers for C++ support</li>
-
- <li>OpenSL ES native audio libraries</li>
-
- <li>Android native application APIS</li>
- </ul>
-
- <p>The NDK also provides a build system that lets you work efficiently with your sources, without
- having to handle the toolchain/platform/CPU/ABI details. You create very short build files to
- describe which sources to compile and which Android application will use them &mdash; the build
- system compiles the sources and places the shared libraries directly in your application
- project.</p>
-
- <p class="caution"><strong>Important:</strong> With the exception of the libraries listed above,
- native system libraries in the Android platform are <em>not</em> stable and may change in future
- platform versions. Your applications should <em>only</em> make use of the stable native system
- libraries provided in this NDK.</p>
-
- <h3 id="docs">Documentation</h3>
-
- <p>The NDK package includes a set of documentation that describes the capabilities of the NDK and
- how to use it to create shared libraries for your Android applications. In this release, the
- documentation is provided only in the downloadable NDK package. You can find the documentation in
- the <code>&lt;ndk&gt;/docs/</code> directory. Included are these files (partial listing):</p>
-
- <ul>
- <li>
- INSTALL.HTML &mdash; describes how to install the NDK and configure it for your host
- system</li>
-
- <li>OVERVIEW.HTML &mdash; provides an overview of the NDK capabilities and usage</li>
-
- <li>ANDROID-MK.HTML &mdash; describes the use of the Android.mk file, which defines the native
- sources you want to compile</li>
-
- <li>APPLICATION-MK.HTML &mdash; describes the use of the Application.mk file, which describes
- the native sources required by your Android application</li>
- <li>CPLUSPLUS-SUPPORT.HTML &mdash; describes the C++ support provided in the Android NDK</li>
- <li>CPU-ARCH-ABIS.HTML &mdash; a description of supported CPU architectures and how to target
- them.</li>
-
- <li>CPU-FEATURES.HTML &mdash; a description of the <code>cpufeatures</code> static library that
- lets your application code detect the target device's CPU family and the optional features at
- runtime.</li>
-
- <li>CHANGES.HTML &mdash; a complete list of changes to the NDK across all releases.</li>
-
- <li>DEVELOPMENT.HTML &mdash; describes how to modify the NDK and generate release packages for it</li>
-
- <li>HOWTO.HTML &mdash; information about common tasks associated with NDK development</li>
-
- <li>IMPORT-MODULE.HTML &mdash; describes how to share and reuse modules</li>
-
- <li>LICENSES.HTML &mdash; information about the various open source licenses that govern the Android NDK</li>
-
- <li>NATIVE-ACTIVITY.HTML &mdash; describes how to implement native activities</li>
-
- <li>NDK-BUILD.HTML &mdash; describes the usage of the ndk-build script</li>
-
- <li>NDK-GDB.HTML &mdash; describes how to use the native code debugger</li>
-
- <li>PREBUILTS.HTML &mdash; information about how shared and static prebuilt libraries work </li>
-
- <li>STANDALONE-TOOLCHAIN.HTML &mdash; describes how to use Android NDK toolchain as a standalone
- compiler (still in beta).</li>
-
- <li>SYSTEM-ISSUES.HTML &mdash; known issues in the Android system images that you should be
- aware of, if you are developing using the NDK.</li>
-
- <li>STABLE-APIS.HTML &mdash; a complete list of the stable APIs exposed by headers in the
- NDK.</li>
-
- </ul>
-
- <p>Additionally, the package includes detailed information about the "bionic" C library provided
- with the Android platform that you should be aware of, if you are developing using the NDK. You
- can find the documentation in the <code>&lt;ndk&gt;/docs/system/libc/</code> directory:</p>
-
- <ul>
- <li>OVERVIEW.HTML &mdash; provides an overview of the "bionic" C library and the features it
- offers.</li>
- </ul>
-
- <h3 id="samples">Sample applications</h3>
-
-<p>The NDK includes sample applications that illustrate how to use native code in your Android
- applications:</p>
-
- <ul>
- <li><code>hello-jni</code> &mdash; a simple application that loads a string from a native
- method implemented in a shared library and then displays it in the application UI.</li>
-
- <li><code>two-libs</code> &mdash; a simple application that loads a shared library dynamically
- and calls a native method provided by the library. In this case, the method is implemented in a
- static library imported by the shared library.</li>
-
- <li><code>san-angeles</code> &mdash; a simple application that renders 3D graphics through the
- native OpenGL ES APIs, while managing activity lifecycle with a {@link
- android.opengl.GLSurfaceView} object.</li>
-
- <li><code>hello-gl2</code> &mdash; a simple application that renders a triangle using OpenGL ES
- 2.0 vertex and fragment shaders.</li>
-
- <li><code>hello-neon</code> &mdash; a simple application that shows how to use the
- <code>cpufeatures</code> library to check CPU capabilities at runtime, then use NEON intrinsics
- if supported by the CPU. Specifically, the application implements two versions of a tiny
- benchmark for a FIR filter loop, a C version and a NEON-optimized version for devices that
- support it.</li>
-
- <li><code>bitmap-plasma</code> &mdash; a simple application that demonstrates how to access the
- pixel buffers of Android {@link android.graphics.Bitmap} objects from native code, and uses
- this to generate an old-school "plasma" effect.</li>
-
- <li><code>native-activity</code> &mdash; a simple application that demonstrates how to use the
- native-app-glue static library to create a native activity</li>
-
- <li><code>native-plasma</code> &mdash; a version of bitmap-plasma implemented with a native
- activity.</li>
- </ul>
-
- <p>For each sample, the NDK includes the corresponding C source code and the necessary Android.mk
- and Application.mk files. There are located under <code>&lt;ndk&gt;/samples/&lt;name&gt;/</code>
- and their source code can be found under <code>&lt;ndk&gt;/samples/&lt;name&gt;/jni/</code>.</p>
-
- <p>You can build the shared libraries for the sample apps by going into
- <code>&lt;ndk&gt;/samples/&lt;name&gt;/</code> then calling the <code>ndk-build</code> command.
- The generated shared libraries will be located under
- <code>&lt;ndk&gt;/samples/&lt;name&gt;/libs/armeabi/</code> for (ARMv5TE machine code) and/or
- <code>&lt;ndk&gt;/samples/&lt;name&gt;/libs/armeabi-v7a/</code> for (ARMv7 machine code).</p>
-
- <p>Next, build the sample Android applications that use the shared libraries:</p>
-
- <ul>
- <li>If you are developing in Eclipse with ADT, use the New Project Wizard to create a new
- Android project for each sample, using the "Import from Existing Source" option and importing
- the source from <code>&lt;ndk&gt;/samples/&lt;name&gt;/</code>. Then, set up an AVD,
- if necessary, and build/run the application in the emulator.</li>
-
- <li>If you are developing with Ant, use the <code>android</code> tool to create the build file
- for each of the sample projects at <code>&lt;ndk&gt;/samples/&lt;name&gt;/</code>.
- Then set up an AVD, if necessary, build your project in the usual way, and run it in the
- emulator.</li>
-
- </ul>
-
- <p>For more information about developing with the Android SDK tools and what
- you need to do to create, build, and run your applications, see
- the <a href="{@docRoot}tools/workflow/index.html">Overview</a>
- section for developing on Android.</p>
-
- <h4 id="hello-jni">Exploring the hello-jni Sample</h4>
-
- <p>The hello-jni sample is a simple demonstration on how to use JNI from an Android application.
- The HelloJni activity receives a string from a simple C function and displays it in a
- TextView.</p>
-
- <p>The main components of the sample include:</p>
-
- <ul>
- <li>The familiar basic structure of an Android application (an <code>AndroidManifest.xml</code>
- file, a <code>src/</code> and <code>res</code> directories, and a main activity)</li>
-
- <li>A <code>jni/</code> directory that includes the implemented source file for the native code
- as well as the Android.mk file</li>
-
- <li>A <code>tests/</code> directory that contains unit test code.</li>
- </ul>
-
- <ol>
- <li>Create a new project in Eclipse from the existing sample source or use the
- <code>android</code> tool to update the project so it generates a build.xml file that you can
- use to build the sample.
-
- <ul>
- <li>In Eclipse:
-
- <ol type="a">
- <li>Click <strong>File &gt; New Android Project...</strong></li>
-
- <li>Select the <strong>Create project from existing source</strong> radio button.</li>
-
- <li>Select any API level above Android 1.5.</li>
-
- <li>In the <strong>Location</strong> field, click <strong>Browse...</strong> and select
- the <code>&lt;ndk-root&gt;/samples/hello-jni</code> directory.</li>
-
- <li>Click <strong>Finish</strong>.</li>
- </ol>
- </li>
-
- <li>On the command line:
-
- <ol type="a">
- <li>Change to the <code>&lt;ndk-root&gt;/samples/hello-jni</code> directory.</li>
-
- <li>Run the following command to generate a build.xml file:
- <pre class="no-pretty-print">android update project -p . -s</pre>
- </li>
- </ol>
- </li>
- </ul>
- </li>
-
- <li>Compile the native code using the <code>ndk-build</code> command.
- <pre class="no-pretty-print">
-cd &lt;ndk-root&gt;/samples/hello-jni
-&lt;ndk_root&gt;/ndk-build
-</pre>
- </li>
-
- <li>Build and install the application as you would a normal Android application. If you are
- using Eclipse, run the application to build and install it on a device. If you are using Ant,
- run the following commands from the project directory:
- <pre class="no-pretty-print">
-ant debug
-adb install bin/HelloJni-debug.apk
-</pre>
- </li>
- </ol>
-
- <p>When you run the application on the device, the string <code>Hello JNI</code> should appear on
- your device. You can explore the rest of the samples that are located in the
- <code>&lt;ndk-root&gt;/samples</code> directory for more examples on how to use the JNI.</p>
-
- <h4 id="native-activity">Exploring the native-activity Sample Application</h4>
-
- <p>The native-activity sample provided with the Android NDK demonstrates how to use the
- android_native_app_glue static library. This static library makes creating a native activity
- easier by providing you with an implementation that handles your callbacks in another thread, so
- you do not have to worry about them blocking your main UI thread. The main parts of the sample
- are described below:</p>
-
- <ul>
- <li>The familiar basic structure of an Android application (an <code>AndroidManifest.xml</code>
- file, a <code>src/</code> and <code>res</code> directories). The AndroidManifest.xml declares
- that the application is native and specifies the .so file of the native activity. See {@link
- android.app.NativeActivity} for the source or see the
- <code>&lt;ndk_root&gt;/platforms/samples/native-activity/AndroidManifest.xml</code> file.</li>
-
- <li>A <code>jni/</code> directory contains the native activity, main.c, which uses the
- <code>android_native_app_glue.h</code> interface to implement the activity. The Android.mk that
- describes the native module to the build system also exists here.</li>
- </ul>
-
- <p>To build this sample application:</p>
-
- <ol>
- <li>Create a new project in Eclipse from the existing sample source or use the
- <code>android</code> tool to update the project so it generates a build.xml file that you can
- use to build the sample.
-
- <ul>
- <li>In Eclipse:
-
- <ol type="a">
- <li>Click <strong>File &gt; New Android Project...</strong></li>
-
- <li>Select the <strong>Create project from existing source</strong> radio button.</li>
-
- <li>Select any API level above Android 2.3.</li>
-
- <li>In the <strong>Location</strong> field, click <strong>Browse...</strong> and select
- the <code>&lt;ndk-root&gt;/samples/native-activity</code> directory.</li>
-
- <li>Click <strong>Finish</strong>.</li>
- </ol>
- </li>
-
- <li>On the command line:
-
- <ol type="a">
- <li>Change to the <code>&lt;ndk-root&gt;/samples/native-activity</code> directory.</li>
-
- <li>Run the following command to generate a build.xml file:
- <pre class="no-pretty-print">
-android update project -p . -s
-</pre>
- </li>
- </ol>
- </li>
- </ul>
- </li>
-
- <li>Compile the native code using the <code>ndk-build</code> command.
- <pre class="no-pretty-print">
-cd &lt;ndk-root&gt;/platforms/samples/android-9/samples/native-activity
-&lt;ndk_root&gt;/ndk-build
-</pre>
- </li>
-
- <li>Build and install the application as you would a normal Android application. If you are
- using Eclipse, run the application to build and install it on a device. If you are using Ant,
- run the following commands in the project directory, then run the application on the device:
- <pre class="no-pretty-print">
-ant debug
-adb install bin/NativeActivity-debug.apk
-</pre>
- </li>
- </ol>
-
-
- <h2 id="reqs">System and Software Requirements</h2>
-
- <p>The sections below describe the system and software requirements for using the Android NDK, as
- well as platform compatibility considerations that affect appplications using libraries produced
- with the NDK.</p>
-
- <h4>The Android SDK</h4>
-
- <ul>
- <li>A complete Android SDK installation (including all dependencies) is required.</li>
-
- <li>Android 1.5 SDK or later version is required.</li>
- </ul>
-
- <h4>Supported operating systems</h4>
-
- <ul>
- <li>Windows XP (32-bit) or Vista (32- or 64-bit)</li>
-
- <li>Mac OS X 10.4.8 or later (x86 only)</li>
-
- <li>Linux (32 or 64-bit; Ubuntu 8.04, or other Linux distributions using GLibc 2.7 or
-later)</li>
- </ul>
-
- <h4>Required development tools</h4>
-
- <ul>
- <li>For all development platforms, GNU Make 3.81 or later is required. Earlier versions of GNU
- Make might work but have not been tested.</li>
-
- <li>A recent version of awk (either GNU Awk or Nawk) is also required.</li>
-
- <li>For Windows, <a href="http://www.cygwin.com">Cygwin</a> 1.7 or higher is required. The NDK
- will <em>not</em> work with Cygwin 1.5 installations.</li>
- </ul>
-
- <h4 id="platform-compat">Android platform compatibility</h4>
-
- <ul>
- <li>The native libraries created by the Android NDK can only be used on devices running
- specific minimum Android platform versions. The minimum required platform version depends on
- the CPU architecture of the devices you are targeting. The following table details which
- Android platform versions are compatible with native code developed for specific CPU
- architectures.
-
- <table style="margin:1em;">
- <tr>
- <th>Native Code CPU Architecture Used</th>
- <th>Compatible Android Platform(s)</th>
- </tr>
-
- <tr>
- <td>ARM, ARM-NEON</td>
- <td>Android 1.5 (API Level 3) and higher</td>
- </tr>
-
- <tr>
- <td>x86</td>
- <td>Android 2.3 (API Level 9) and higher</td>
- </tr>
-
- <tr>
- <td>MIPS</td>
- <td>Android 2.3 (API Level 9) and higher</td>
- </tr>
- </table>
-
- <p>These requirements mean you can use native libraries produced with the NDK in
- applications that are deployable to ARM-based devices running Android 1.5 or later. If you are
- deploying native libraries to x86 and MIPS-based devices, your application must target Android
- 2.3 or later.</p>
- </li>
-
- <li>To ensure compatibility, an application using a native library produced with the NDK
- <em>must</em> declare a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code>
- &lt;uses-sdk&gt;</code></a> element in its manifest file, with an
- <code>android:minSdkVersion</code> attribute value of "3" or higher. For example:
-
-<pre style="margin:1em;">
-&lt;manifest&gt;
- &lt;uses-sdk android:minSdkVersion="3" /&gt;
- ...
-&lt;/manifest&gt;
-</pre>
- </li>
-
- <li>If you use this NDK to create a native library that uses the OpenGL ES APIs, the
- application containing the library can be deployed only to devices running the minimum platform
- versions described in the table below. To ensure compatibility, make sure that your application
- declares the proper <code>android:minSdkVersion</code> attribute value, as shown in the
- following table.</li>
-
- <li style="list-style: none; display: inline">
- <table style="margin:1em;">
- <tr>
- <th>OpenGL ES Version Used</th>
-
- <th>Compatible Android Platform(s)</th>
-
- <th>Required uses-sdk Attribute</th>
- </tr>
-
- <tr>
- <td>OpenGL ES 1.1</td>
-
- <td>Android 1.6 (API Level 4) and higher</td>
-
- <td><code>android:minSdkVersion="4"</code></td>
- </tr>
-
- <tr>
- <td>OpenGL ES 2.0</td>
-
- <td>Android 2.0 (API Level 5) and higher</td>
-
- <td><code>android:minSdkVersion="5"</code></td>
- </tr>
- </table>
-
- <p>For more information about API Level and its relationship to Android platform versions,
- see <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">Android API Levels</a>.</p>
- </li>
-
- <li>Additionally, an application using the OpenGL ES APIs should declare a
- <code>&lt;uses-feature&gt;</code> element in its manifest, with an
- <code>android:glEsVersion</code> attribute that specifies the minimum OpenGl ES version
- required by the application. This ensures that Google Play will show your application only
- to users whose devices are capable of supporting your application. For example:
- <pre style="margin:1em;">
-&lt;manifest&gt;
-<!-- Declare that the application uses the OpenGL ES 2.0 API and is designed
- to run only on devices that support OpenGL ES 2.0 or higher. -->
- &lt;uses-feature android:glEsVersion="0x00020000" /&gt;
- ...
-&lt;/manifest&gt;
-</pre>
-
- <p>For more information, see the <a href=
- "{@docRoot}guide/topics/manifest/uses-feature-element.html"><code>&lt;uses-feature&gt;</code></a>
- documentation.</p>
- </li>
-
- <li>If you use this NDK to create a native library that uses the API to access Android {@link
- android.graphics.Bitmap} pixel buffers or utilizes native activities, the application
- containing the library can be deployed only to devices running Android 2.2 (API level 8) or
- higher. To ensure compatibility, make sure that your application declares <code>&lt;uses-sdk
- android:minSdkVersion="8" /&gt;</code> attribute value in its manifest.</li>
- </ul>
diff --git a/docs/html/tools/sdk/older_releases.jd b/docs/html/tools/sdk/older_releases.jd
index bb274b6..94baa92 100644
--- a/docs/html/tools/sdk/older_releases.jd
+++ b/docs/html/tools/sdk/older_releases.jd
@@ -1,4 +1,5 @@
page.title=SDK Archives
+excludeFromSuggestions=true
@jd:body
<p>This page provides a full list of archived and obsolete SDK releases,
diff --git a/docs/html/tools/sdk/platforms.jd b/docs/html/tools/sdk/platforms.jd
deleted file mode 100644
index 27e89de..0000000
--- a/docs/html/tools/sdk/platforms.jd
+++ /dev/null
@@ -1,9 +0,0 @@
-page.title=Android Development Platforms
-
-@jd:body
-
-
-
-<p>A page that lists platforms and links to release notes. Links to dashboards etc.</p>
-
-
diff --git a/docs/html/tools/sdk/tools-notes.jd b/docs/html/tools/sdk/tools-notes.jd
index 7d121844..4d8aa34 100644
--- a/docs/html/tools/sdk/tools-notes.jd
+++ b/docs/html/tools/sdk/tools-notes.jd
@@ -1,4 +1,5 @@
page.title=SDK Tools
+excludeFromSuggestions=true
@jd:body
<p>SDK Tools is a downloadable component for the Android SDK. It includes the
diff --git a/docs/html/tools/sdk/usb-drivers.jd b/docs/html/tools/sdk/usb-drivers.jd
deleted file mode 100644
index 27e89de..0000000
--- a/docs/html/tools/sdk/usb-drivers.jd
+++ /dev/null
@@ -1,9 +0,0 @@
-page.title=Android Development Platforms
-
-@jd:body
-
-
-
-<p>A page that lists platforms and links to release notes. Links to dashboards etc.</p>
-
-
diff --git a/docs/html/training/accessibility/index.jd b/docs/html/training/accessibility/index.jd
index 333f9f2..0af1d87 100644
--- a/docs/html/training/accessibility/index.jd
+++ b/docs/html/training/accessibility/index.jd
@@ -1,9 +1,8 @@
page.title=Implementing Accessibility
+page.tags="navigation","input"
trainingnavtop=true
startpage=true
-next.title=Developing Accessible Applications
-next.link=accessible-app.html
@jd:body
diff --git a/docs/html/training/articles/perf-anr.jd b/docs/html/training/articles/perf-anr.jd
index abef545..d3b2318 100644
--- a/docs/html/training/articles/perf-anr.jd
+++ b/docs/html/training/articles/perf-anr.jd
@@ -1,4 +1,6 @@
page.title=Keeping Your App Responsive
+page.tags="threads","asynctask"
+
page.article=true
@jd:body
diff --git a/docs/html/training/articles/perf-jni.jd b/docs/html/training/articles/perf-jni.jd
index 2abb000..0d1f04e 100644
--- a/docs/html/training/articles/perf-jni.jd
+++ b/docs/html/training/articles/perf-jni.jd
@@ -1,4 +1,6 @@
page.title=JNI Tips
+page.tags="ndk","native"
+
page.article=true
@jd:body
diff --git a/docs/html/training/articles/security-ssl.jd b/docs/html/training/articles/security-ssl.jd
index 9a6320b..d3f68e2 100644
--- a/docs/html/training/articles/security-ssl.jd
+++ b/docs/html/training/articles/security-ssl.jd
@@ -1,4 +1,6 @@
page.title=Security with HTTPS and SSL
+page.tags="network","certificates"
+
page.article=true
@jd:body
diff --git a/docs/html/training/articles/smp.jd b/docs/html/training/articles/smp.jd
index d46787d..0f667d7 100644
--- a/docs/html/training/articles/smp.jd
+++ b/docs/html/training/articles/smp.jd
@@ -1,4 +1,6 @@
page.title=SMP Primer for Android
+page.tags="ndk","native"
+
page.article=true
@jd:body
diff --git a/docs/html/training/backward-compatible-ui/index.jd b/docs/html/training/backward-compatible-ui/index.jd
index f81b5a7..82087a6 100644
--- a/docs/html/training/backward-compatible-ui/index.jd
+++ b/docs/html/training/backward-compatible-ui/index.jd
@@ -1,9 +1,8 @@
page.title=Creating Backward-Compatible UIs
+page.tags="widgets","support"
trainingnavtop=true
startpage=true
-next.title=Abstracting the New Implementation
-next.link=abstracting.html
@jd:body
diff --git a/docs/html/training/basics/firstapp/building-ui.jd b/docs/html/training/basics/firstapp/building-ui.jd
index 0f18861..2615bee 100644
--- a/docs/html/training/basics/firstapp/building-ui.jd
+++ b/docs/html/training/basics/firstapp/building-ui.jd
@@ -240,7 +240,7 @@ the "hello_world" string.)</p>
&lt;string name="app_name">My First App&lt;/string>
&lt;string name="edit_message">Enter a message&lt;/string>
&lt;string name="button_send">Send&lt;/string>
- &lt;string name="menu_settings">Settings&lt;/string>
+ &lt;string name="action_settings">Settings&lt;/string>
&lt;string name="title_activity_main">MainActivity&lt;/string>
&lt;/resources>
</pre>
diff --git a/docs/html/training/basics/location/index.jd b/docs/html/training/basics/location/index.jd
index 48cfbc3..240bbb2 100644
--- a/docs/html/training/basics/location/index.jd
+++ b/docs/html/training/basics/location/index.jd
@@ -1,9 +1,8 @@
page.title=Making Your App Location Aware
+page.tags="geolocation","maps"
trainingnavtop=true
startpage=true
-next.title=Using the Location Manager
-next.link=locationmanager.html
@jd:body
diff --git a/docs/html/training/custom-views/index.jd b/docs/html/training/custom-views/index.jd
index cec75b4..1c09e66 100644
--- a/docs/html/training/custom-views/index.jd
+++ b/docs/html/training/custom-views/index.jd
@@ -1,9 +1,8 @@
page.title=Creating Custom Views
+page.tags="widgets","ui","layout"
trainingnavtop=true
startpage=true
-next.title=Creating a View Class
-next.link=create-view.html
@jd:body
diff --git a/docs/html/training/design-navigation/index.jd b/docs/html/training/design-navigation/index.jd
index af60717..f888c93 100644
--- a/docs/html/training/design-navigation/index.jd
+++ b/docs/html/training/design-navigation/index.jd
@@ -2,8 +2,6 @@ page.title=Designing Effective Navigation
trainingnavtop=true
startpage=true
-next.title=Planning Screens and Their Relationships
-next.link=screen-planning.html
@jd:body
diff --git a/docs/html/training/enterprise/index.jd b/docs/html/training/enterprise/index.jd
index 0db9009..ac1b565 100644
--- a/docs/html/training/enterprise/index.jd
+++ b/docs/html/training/enterprise/index.jd
@@ -1,4 +1,5 @@
page.title=Developing for Enterprise
+page.tags="policy","privacy"
trainingnavtop=true
startpage=true
diff --git a/docs/html/training/gestures/index.jd b/docs/html/training/gestures/index.jd
index 16ca7b0..9d21b08 100644
--- a/docs/html/training/gestures/index.jd
+++ b/docs/html/training/gestures/index.jd
@@ -1,8 +1,8 @@
page.title=Using Touch Gestures
+page.tags="input","navigation","gesturedetector","scroller"
+
trainingnavtop=true
startpage=true
-next.title=Detect Built-in Gestures
-next.link=detector.html
@jd:body
diff --git a/docs/html/training/id-auth/index.jd b/docs/html/training/id-auth/index.jd
index 140545c..2bae9c4 100644
--- a/docs/html/training/id-auth/index.jd
+++ b/docs/html/training/id-auth/index.jd
@@ -1,9 +1,8 @@
page.title=Remembering Users
+page.tags="privacy","oauth","accounts"
trainingnavtop=true
startpage=true
-next.title=Remembering Your User
-next.link=identify.html
@jd:body
diff --git a/docs/html/training/implementing-navigation/index.jd b/docs/html/training/implementing-navigation/index.jd
index ebb4995..990bcfe 100644
--- a/docs/html/training/implementing-navigation/index.jd
+++ b/docs/html/training/implementing-navigation/index.jd
@@ -1,9 +1,8 @@
page.title=Implementing Effective Navigation
+page.tags="viewpager","tasks","back","up"
trainingnavtop=true
startpage=true
-next.title=Implementing Lateral Navigation
-next.link=lateral.html
@jd:body
diff --git a/docs/html/training/improving-layouts/index.jd b/docs/html/training/improving-layouts/index.jd
index a0ac13e..af29d3f 100644
--- a/docs/html/training/improving-layouts/index.jd
+++ b/docs/html/training/improving-layouts/index.jd
@@ -1,4 +1,5 @@
page.title=Improving Layout Performance
+page.tags="include","merge","viewstub","listview"
trainingnavtop=true
startpage=true
diff --git a/docs/html/training/in-app-billing/index.jd b/docs/html/training/in-app-billing/index.jd
index 3d07481..94708b8 100644
--- a/docs/html/training/in-app-billing/index.jd
+++ b/docs/html/training/in-app-billing/index.jd
@@ -1,4 +1,5 @@
page.title=Selling In-app Products
+page.tags="billing"
trainingnavtop=true
startpage=true
diff --git a/docs/html/training/keyboard-input/index.jd b/docs/html/training/keyboard-input/index.jd
index ba4e598..7ac79e6 100644
--- a/docs/html/training/keyboard-input/index.jd
+++ b/docs/html/training/keyboard-input/index.jd
@@ -1,4 +1,5 @@
page.title=Handling Keyboard Input
+page.tags="edittext","accessibility"
trainingnavtop=true
startpage=true
diff --git a/docs/html/training/load-data-background/index.jd b/docs/html/training/load-data-background/index.jd
index dc9d84a..221ae57 100644
--- a/docs/html/training/load-data-background/index.jd
+++ b/docs/html/training/load-data-background/index.jd
@@ -1,4 +1,6 @@
page.title=Loading Data in the Background
+page.tags="cursorloader"
+
trainingnavtop=true
startpage=true
diff --git a/docs/html/training/monitoring-device-state/index.jd b/docs/html/training/monitoring-device-state/index.jd
index 585b669..c3d700a 100644
--- a/docs/html/training/monitoring-device-state/index.jd
+++ b/docs/html/training/monitoring-device-state/index.jd
@@ -1,9 +1,8 @@
page.title=Optimizing Battery Life
+page.tags="network","internet"
trainingnavtop=true
startpage=true
-next.title=Monitoring the Battery Level and Charging State
-next.link=battery-monitoring.html
@jd:body
diff --git a/docs/html/training/multiple-apks/index.jd b/docs/html/training/multiple-apks/index.jd
index 37286c3..5754da9 100644
--- a/docs/html/training/multiple-apks/index.jd
+++ b/docs/html/training/multiple-apks/index.jd
@@ -1,9 +1,8 @@
page.title=Maintaining Multiple APKs
+page.tags="support"
trainingnavtop=true
startpage=true
-next.title=Creating Multiple APKs for Different API Levels
-next.link=api.html
@jd:body
diff --git a/docs/html/training/multiple-threads/index.jd b/docs/html/training/multiple-threads/index.jd
index 3ea57c5..cbd42b4 100644
--- a/docs/html/training/multiple-threads/index.jd
+++ b/docs/html/training/multiple-threads/index.jd
@@ -1,4 +1,5 @@
page.title=Sending Operations to Multiple Threads
+page.tags="threadpool","runnable"
trainingnavtop=true
startpage=true
diff --git a/docs/html/training/multiscreen/index.jd b/docs/html/training/multiscreen/index.jd
index 23f6564..d09540e 100644
--- a/docs/html/training/multiscreen/index.jd
+++ b/docs/html/training/multiscreen/index.jd
@@ -1,9 +1,8 @@
page.title=Designing for Multiple Screens
+page.tags="tablet","tv","fragments","support"
trainingnavtop=true
startpage=true
-next.title=Supporting Different Screen Sizes
-next.link=screensizes.html
@jd:body
diff --git a/docs/html/training/notify-user/index.jd b/docs/html/training/notify-user/index.jd
index 510f2c4..51f058f 100644
--- a/docs/html/training/notify-user/index.jd
+++ b/docs/html/training/notify-user/index.jd
@@ -1,8 +1,8 @@
page.title=Notifying the User
+page.tags="notifications"
+
trainingnavtop=true
startpage=true
-next.title=Build a Notification
-next.link=build-notification.html
@jd:body
diff --git a/docs/html/training/run-background-service/index.jd b/docs/html/training/run-background-service/index.jd
index 173b87a..3360df5 100644
--- a/docs/html/training/run-background-service/index.jd
+++ b/docs/html/training/run-background-service/index.jd
@@ -1,7 +1,11 @@
page.title=Running in a Background Service
+page.tags="intentservice"
+
trainingnavtop=true
startpage=true
+
@jd:body
+
<div id="tb-wrapper">
<div id="tb">
<h2>Dependencies and prerequisites</h2>
diff --git a/docs/html/training/search/index.jd b/docs/html/training/search/index.jd
index bfd1618..4070372 100644
--- a/docs/html/training/search/index.jd
+++ b/docs/html/training/search/index.jd
@@ -1,8 +1,8 @@
page.title=Adding Search Functionality
+page.tags="searchview","database"
+
trainingnavtop=true
startpage=true
-next.title=Setting Up the Search Interface
-next.link=setup.html
@jd:body
diff --git a/docs/html/training/tv/index.jd b/docs/html/training/tv/index.jd
index ae13c4a..9d15f46 100644
--- a/docs/html/training/tv/index.jd
+++ b/docs/html/training/tv/index.jd
@@ -1,9 +1,8 @@
page.title=Designing for TV
+page.tags="input","screens"
trainingnavtop=true
startpage=true
-next.title=Optimizing layouts for TV
-next.link=optimizing-layouts-tv.html
@jd:body
diff --git a/drm/jni/Android.mk b/drm/jni/Android.mk
index fff7eee..474b9b2 100644
--- a/drm/jni/Android.mk
+++ b/drm/jni/Android.mk
@@ -23,6 +23,7 @@ LOCAL_MODULE:= libdrmframework_jni
LOCAL_SHARED_LIBRARIES := \
libdrmframework \
+ liblog \
libutils \
libandroid_runtime \
libnativehelper \
@@ -43,4 +44,3 @@ LOCAL_C_INCLUDES += \
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
-
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index c90f400..d5183d5 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -17,6 +17,7 @@
package android.graphics.drawable;
import android.graphics.Insets;
+import android.os.Trace;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -745,7 +746,12 @@ public abstract class Drawable {
* Create a drawable from an inputstream
*/
public static Drawable createFromStream(InputStream is, String srcName) {
- return createFromResourceStream(null, null, is, srcName, null);
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
+ try {
+ return createFromResourceStream(null, null, is, srcName, null);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ }
}
/**
@@ -754,7 +760,12 @@ public abstract class Drawable {
*/
public static Drawable createFromResourceStream(Resources res, TypedValue value,
InputStream is, String srcName) {
- return createFromResourceStream(res, value, is, srcName, null);
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
+ try {
+ return createFromResourceStream(res, value, is, srcName, null);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ }
}
/**
@@ -900,9 +911,14 @@ public abstract class Drawable {
return null;
}
- Bitmap bm = BitmapFactory.decodeFile(pathName);
- if (bm != null) {
- return drawableFromBitmap(null, bm, null, null, null, pathName);
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
+ try {
+ Bitmap bm = BitmapFactory.decodeFile(pathName);
+ if (bm != null) {
+ return drawableFromBitmap(null, bm, null, null, null, pathName);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
return null;
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index a0c9701..8a4d598 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -512,7 +512,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
@Override
public int getChangingConfigurations() {
- return mChangingConfigurations;
+ return mChangingConfigurations | mChildrenChangingConfigurations;
}
public final int addChild(Drawable dr) {
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index b8564b6..5d1990a 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -18,6 +18,7 @@ package android.renderscript;
import java.io.IOException;
import java.io.InputStream;
+import java.util.HashMap;
import android.content.res.Resources;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
@@ -92,6 +93,9 @@ public class Allocation extends BaseObj {
int mCurrentDimY;
int mCurrentDimZ;
int mCurrentCount;
+ static HashMap<Integer, Allocation> mAllocationMap =
+ new HashMap<Integer, Allocation>();
+ IoInputNotifier mBufferNotifier;
/**
@@ -362,11 +366,20 @@ public class Allocation extends BaseObj {
*/
public void syncAll(int srcLocation) {
switch (srcLocation) {
+ case USAGE_GRAPHICS_TEXTURE:
case USAGE_SCRIPT:
+ if ((mUsage & USAGE_SHARED) != 0) {
+ copyFrom(mBitmap);
+ }
+ break;
case USAGE_GRAPHICS_CONSTANTS:
- case USAGE_GRAPHICS_TEXTURE:
case USAGE_GRAPHICS_VERTEX:
break;
+ case USAGE_SHARED:
+ if ((mUsage & USAGE_SHARED) != 0) {
+ copyTo(mBitmap);
+ }
+ break;
default:
throw new RSIllegalArgumentException("Source must be exactly one usage type.");
}
@@ -492,7 +505,9 @@ public class Allocation extends BaseObj {
*/
public void copyFromUnchecked(int[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
@@ -507,7 +522,9 @@ public class Allocation extends BaseObj {
*/
public void copyFromUnchecked(short[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
@@ -522,7 +539,9 @@ public class Allocation extends BaseObj {
*/
public void copyFromUnchecked(byte[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
@@ -537,7 +556,9 @@ public class Allocation extends BaseObj {
*/
public void copyFromUnchecked(float[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
@@ -553,7 +574,9 @@ public class Allocation extends BaseObj {
*/
public void copyFrom(int[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFrom(0, mCurrentCount, d);
@@ -569,7 +592,9 @@ public class Allocation extends BaseObj {
*/
public void copyFrom(short[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFrom(0, mCurrentCount, d);
@@ -585,7 +610,9 @@ public class Allocation extends BaseObj {
*/
public void copyFrom(byte[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFrom(0, mCurrentCount, d);
@@ -601,7 +628,9 @@ public class Allocation extends BaseObj {
*/
public void copyFrom(float[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFrom(0, mCurrentCount, d);
@@ -967,12 +996,144 @@ public class Allocation extends BaseObj {
Canvas c = new Canvas(newBitmap);
c.drawBitmap(data, 0, 0, null);
copy2DRangeFrom(xoff, yoff, newBitmap);
+ return;
}
validateBitmapFormat(data);
validate2DRange(xoff, yoff, data.getWidth(), data.getHeight());
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, data);
}
+ private void validate3DRange(int xoff, int yoff, int zoff, int w, int h, int d) {
+ if (mAdaptedAllocation != null) {
+
+ } else {
+
+ if (xoff < 0 || yoff < 0 || zoff < 0) {
+ throw new RSIllegalArgumentException("Offset cannot be negative.");
+ }
+ if (h < 0 || w < 0 || d < 0) {
+ throw new RSIllegalArgumentException("Height or width cannot be negative.");
+ }
+ if (((xoff + w) > mCurrentDimX) || ((yoff + h) > mCurrentDimY) || ((zoff + d) > mCurrentDimZ)) {
+ throw new RSIllegalArgumentException("Updated region larger than allocation.");
+ }
+ }
+ }
+
+ /**
+ * @hide
+ *
+ */
+ void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, byte[] data) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data, data.length);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, short[] data) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data, data.length * 2);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, int[] data) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data, data.length * 4);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, float[] data) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data, data.length * 4);
+ }
+
+
+ /**
+ * @hide
+ * Copy a rectangular region from the array into the allocation.
+ * The incoming array is assumed to be tightly packed.
+ *
+ * @param xoff X offset of the region to update
+ * @param yoff Y offset of the region to update
+ * @param zoff Z offset of the region to update
+ * @param w Width of the incoming region to update
+ * @param h Height of the incoming region to update
+ * @param d Depth of the incoming region to update
+ * @param data to be placed into the allocation
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, byte[] data) {
+ validateIsInt8();
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, short[] data) {
+ validateIsInt16();
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, int[] data) {
+ validateIsInt32();
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, float[] data) {
+ validateIsFloat32();
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ }
+
+ /**
+ * @hide
+ * Copy a rectangular region into the allocation from another
+ * allocation.
+ *
+ * @param xoff X offset of the region to update.
+ * @param yoff Y offset of the region to update.
+ * @param w Width of the incoming region to update.
+ * @param h Height of the incoming region to update.
+ * @param d Depth of the incoming region to update.
+ * @param data source allocation.
+ * @param dataXoff X offset in data of the region to update.
+ * @param dataYoff Y offset in data of the region to update.
+ * @param dataZoff Z offset in data of the region to update
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d,
+ Allocation data, int dataXoff, int dataYoff, int dataZoff) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data.getID(mRS), dataXoff, dataYoff, dataZoff,
+ data.mSelectedLOD);
+ }
+
/**
* Copy from the Allocation into a Bitmap. The bitmap must
@@ -1050,6 +1211,10 @@ public class Allocation extends BaseObj {
* A new type will be created with the new dimension.
*
* @param dimX The new size of the allocation.
+ *
+ * @deprecated Renderscript objects should be immutable once
+ * created. The replacement is to create a new allocation and copy the
+ * contents.
*/
public synchronized void resize(int dimX) {
if ((mType.getY() > 0)|| (mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
@@ -1064,38 +1229,6 @@ public class Allocation extends BaseObj {
updateCacheInfo(mType);
}
- /**
- * Resize a 2D allocation. The contents of the allocation are
- * preserved. If new elements are allocated objects are created
- * with null contents and the new region is otherwise undefined.
- *
- * If the new region is smaller the references of any objects
- * outside the new region will be released.
- *
- * A new type will be created with the new dimension.
- *
- * @param dimX The new size of the allocation.
- * @param dimY The new size of the allocation.
- */
- public synchronized void resize(int dimX, int dimY) {
- if ((mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
- throw new RSInvalidStateException(
- "Resize only support for 2D allocations at this time.");
- }
- if (mType.getY() == 0) {
- throw new RSInvalidStateException(
- "Resize only support for 2D allocations at this time.");
- }
- mRS.nAllocationResize2D(getID(mRS), dimX, dimY);
- mRS.finish(); // Necessary because resize is fifoed and update is async.
-
- int typeID = mRS.nAllocationGetType(getID(mRS));
- mType = new Type(typeID, mRS);
- mType.updateFromNative();
- updateCacheInfo(mType);
- }
-
-
// creation
@@ -1254,7 +1387,7 @@ public class Allocation extends BaseObj {
// enable optimized bitmap path only with no mipmap and script-only usage
if (mips == MipmapControl.MIPMAP_NONE &&
t.getElement().isCompatible(Element.RGBA_8888(rs)) &&
- usage == (USAGE_SHARED | USAGE_SCRIPT)) {
+ usage == (USAGE_SHARED | USAGE_SCRIPT | USAGE_GRAPHICS_TEXTURE)) {
int id = rs.nAllocationCreateBitmapBackedAllocation(t.getID(rs), mips.mID, b, usage);
if (id == 0) {
throw new RSRuntimeException("Load failed.");
@@ -1326,7 +1459,7 @@ public class Allocation extends BaseObj {
static public Allocation createFromBitmap(RenderScript rs, Bitmap b) {
if (rs.getApplicationContext().getApplicationInfo().targetSdkVersion >= 18) {
return createFromBitmap(rs, b, MipmapControl.MIPMAP_NONE,
- USAGE_SHARED | USAGE_SCRIPT);
+ USAGE_SHARED | USAGE_SCRIPT | USAGE_GRAPHICS_TEXTURE);
}
return createFromBitmap(rs, b, MipmapControl.MIPMAP_NONE,
USAGE_GRAPHICS_TEXTURE);
@@ -1544,7 +1677,7 @@ public class Allocation extends BaseObj {
if (rs.getApplicationContext().getApplicationInfo().targetSdkVersion >= 18) {
return createFromBitmapResource(rs, res, id,
MipmapControl.MIPMAP_NONE,
- USAGE_SHARED | USAGE_SCRIPT);
+ USAGE_SHARED | USAGE_SCRIPT | USAGE_GRAPHICS_TEXTURE);
}
return createFromBitmapResource(rs, res, id,
MipmapControl.MIPMAP_NONE,
@@ -1576,6 +1709,41 @@ public class Allocation extends BaseObj {
throw new RSRuntimeException("Could not convert string to utf-8.");
}
}
+
+ /**
+ * Interface to handle notification when new buffers are
+ * available via USAGE_IO_INPUT. An application will receive
+ * one notification when a buffer is available. Additional
+ * buffers will not trigger new notifications until a buffer is
+ * processed.
+ */
+ public interface IoInputNotifier {
+ public void onBufferAvailable(Allocation a);
+ }
+
+ /**
+ * Set a notification handler for USAGE_IO_INPUT
+ *
+ * @param callback instance of the IoInputNotifier class to be called
+ * when buffer arrive.
+ */
+ public void setIoInputNotificationHandler(IoInputNotifier callback) {
+ synchronized(mAllocationMap) {
+ mAllocationMap.put(new Integer(getID(mRS)), this);
+ mBufferNotifier = callback;
+ }
+ }
+
+ static void sendBufferNotification(int id) {
+ synchronized(mAllocationMap) {
+ Allocation a = mAllocationMap.get(new Integer(id));
+
+ if ((a != null) && (a.mBufferNotifier != null)) {
+ a.mBufferNotifier.onBufferAvailable(a);
+ }
+ }
+ }
+
}
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
index 0a7e882..decd0c7 100644
--- a/graphics/java/android/renderscript/FieldPacker.java
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -16,6 +16,8 @@
package android.renderscript;
+import android.util.Log;
+import java.util.BitSet;
/**
* Utility class for packing arguments and structures from Android system objects to
@@ -27,12 +29,14 @@ public class FieldPacker {
mPos = 0;
mLen = len;
mData = new byte[len];
+ mAlignment = new BitSet();
}
public FieldPacker(byte[] data) {
mPos = 0;
mLen = data.length;
mData = data;
+ mAlignment = new BitSet();
}
public void align(int v) {
@@ -41,10 +45,29 @@ public class FieldPacker {
}
while ((mPos & (v - 1)) != 0) {
+ mAlignment.flip(mPos);
mData[mPos++] = 0;
}
}
+ public void subalign(int v) {
+ if ((v & (v - 1)) != 0) {
+ throw new RSIllegalArgumentException("argument must be a non-negative non-zero power of 2: " + v);
+ }
+
+ while ((mPos & (v - 1)) != 0) {
+ mPos--;
+ }
+
+ if (mPos > 0) {
+ while (mAlignment.get(mPos - 1) == true) {
+ mPos--;
+ mAlignment.flip(mPos);
+ }
+ }
+
+ }
+
public void reset() {
mPos = 0;
}
@@ -67,12 +90,26 @@ public class FieldPacker {
mData[mPos++] = v;
}
+ public byte subI8() {
+ subalign(1);
+ return mData[--mPos];
+ }
+
public void addI16(short v) {
align(2);
mData[mPos++] = (byte)(v & 0xff);
mData[mPos++] = (byte)(v >> 8);
}
+ public short subI16() {
+ subalign(2);
+ short v = 0;
+ v = (short)((mData[--mPos] & 0xff) << 8);
+ v = (short)(v | (short)(mData[--mPos] & 0xff));
+ return v;
+ }
+
+
public void addI32(int v) {
align(4);
mData[mPos++] = (byte)(v & 0xff);
@@ -81,6 +118,17 @@ public class FieldPacker {
mData[mPos++] = (byte)((v >> 24) & 0xff);
}
+ public int subI32() {
+ subalign(4);
+ int v = 0;
+ v = ((mData[--mPos] & 0xff) << 24);
+ v = v | ((mData[--mPos] & 0xff) << 16);
+ v = v | ((mData[--mPos] & 0xff) << 8);
+ v = v | ((mData[--mPos] & 0xff));
+ return v;
+ }
+
+
public void addI64(long v) {
align(8);
mData[mPos++] = (byte)(v & 0xff);
@@ -93,6 +141,29 @@ public class FieldPacker {
mData[mPos++] = (byte)((v >> 56) & 0xff);
}
+ public long subI64() {
+ subalign(8);
+ long v = 0;
+ byte x = 0;
+ x = ((mData[--mPos]));
+ v = (long)(v | (((long)x) & 0xff) << 56l);
+ x = ((mData[--mPos]));
+ v = (long)(v | (((long)x) & 0xff) << 48l);
+ x = ((mData[--mPos]));
+ v = (long)(v | (((long)x) & 0xff) << 40l);
+ x = ((mData[--mPos]));
+ v = (long)(v | (((long)x) & 0xff) << 32l);
+ x = ((mData[--mPos]));
+ v = (long)(v | (((long)x) & 0xff) << 24l);
+ x = ((mData[--mPos]));
+ v = (long)(v | (((long)x) & 0xff) << 16l);
+ x = ((mData[--mPos]));
+ v = (long)(v | (((long)x) & 0xff) << 8l);
+ x = ((mData[--mPos]));
+ v = (long)(v | (((long)x) & 0xff));
+ return v;
+ }
+
public void addU8(short v) {
if ((v < 0) || (v > 0xff)) {
android.util.Log.e("rs", "FieldPacker.addU8( " + v + " )");
@@ -143,10 +214,18 @@ public class FieldPacker {
addI32(Float.floatToRawIntBits(v));
}
+ public float subF32() {
+ return Float.intBitsToFloat(subI32());
+ }
+
public void addF64(double v) {
addI64(Double.doubleToRawLongBits(v));
}
+ public double subF64() {
+ return Double.longBitsToDouble(subI64());
+ }
+
public void addObj(BaseObj obj) {
if (obj != null) {
addI32(obj.getID(null));
@@ -315,28 +394,195 @@ public class FieldPacker {
addU64(v.w);
}
+
+ public Float2 subFloat2() {
+ Float2 v = new Float2();
+ v.y = subF32();
+ v.x = subF32();
+ return v;
+ }
+ public Float3 subFloat3() {
+ Float3 v = new Float3();
+ v.z = subF32();
+ v.y = subF32();
+ v.x = subF32();
+ return v;
+ }
+ public Float4 subFloat4() {
+ Float4 v = new Float4();
+ v.w = subF32();
+ v.z = subF32();
+ v.y = subF32();
+ v.x = subF32();
+ return v;
+ }
+
+ public Double2 subDouble2() {
+ Double2 v = new Double2();
+ v.y = subF64();
+ v.x = subF64();
+ return v;
+ }
+ public Double3 subDouble3() {
+ Double3 v = new Double3();
+ v.z = subF64();
+ v.y = subF64();
+ v.x = subF64();
+ return v;
+ }
+ public Double4 subDouble4() {
+ Double4 v = new Double4();
+ v.w = subF64();
+ v.z = subF64();
+ v.y = subF64();
+ v.x = subF64();
+ return v;
+ }
+
+ public Byte2 subByte2() {
+ Byte2 v = new Byte2();
+ v.y = subI8();
+ v.x = subI8();
+ return v;
+ }
+ public Byte3 subByte3() {
+ Byte3 v = new Byte3();
+ v.z = subI8();
+ v.y = subI8();
+ v.x = subI8();
+ return v;
+ }
+ public Byte4 subByte4() {
+ Byte4 v = new Byte4();
+ v.w = subI8();
+ v.z = subI8();
+ v.y = subI8();
+ v.x = subI8();
+ return v;
+ }
+
+ public Short2 subShort2() {
+ Short2 v = new Short2();
+ v.y = subI16();
+ v.x = subI16();
+ return v;
+ }
+ public Short3 subShort3() {
+ Short3 v = new Short3();
+ v.z = subI16();
+ v.y = subI16();
+ v.x = subI16();
+ return v;
+ }
+ public Short4 subShort4() {
+ Short4 v = new Short4();
+ v.w = subI16();
+ v.z = subI16();
+ v.y = subI16();
+ v.x = subI16();
+ return v;
+ }
+
+ public Int2 subInt2() {
+ Int2 v = new Int2();
+ v.y = subI32();
+ v.x = subI32();
+ return v;
+ }
+ public Int3 subInt3() {
+ Int3 v = new Int3();
+ v.z = subI32();
+ v.y = subI32();
+ v.x = subI32();
+ return v;
+ }
+ public Int4 subInt4() {
+ Int4 v = new Int4();
+ v.w = subI32();
+ v.z = subI32();
+ v.y = subI32();
+ v.x = subI32();
+ return v;
+ }
+
+ public Long2 subLong2() {
+ Long2 v = new Long2();
+ v.y = subI64();
+ v.x = subI64();
+ return v;
+ }
+ public Long3 subLong3() {
+ Long3 v = new Long3();
+ v.z = subI64();
+ v.y = subI64();
+ v.x = subI64();
+ return v;
+ }
+ public Long4 subLong4() {
+ Long4 v = new Long4();
+ v.w = subI64();
+ v.z = subI64();
+ v.y = subI64();
+ v.x = subI64();
+ return v;
+ }
+
+
+
public void addMatrix(Matrix4f v) {
for (int i=0; i < v.mMat.length; i++) {
addF32(v.mMat[i]);
}
}
+ public Matrix4f subMatrix4f() {
+ Matrix4f v = new Matrix4f();
+ for (int i = v.mMat.length - 1; i >= 0; i--) {
+ v.mMat[i] = subF32();
+ }
+ return v;
+ }
+
public void addMatrix(Matrix3f v) {
for (int i=0; i < v.mMat.length; i++) {
addF32(v.mMat[i]);
}
}
+ public Matrix3f subMatrix3f() {
+ Matrix3f v = new Matrix3f();
+ for (int i = v.mMat.length - 1; i >= 0; i--) {
+ v.mMat[i] = subF32();
+ }
+ return v;
+ }
+
public void addMatrix(Matrix2f v) {
for (int i=0; i < v.mMat.length; i++) {
addF32(v.mMat[i]);
}
}
+ public Matrix2f subMatrix2f() {
+ Matrix2f v = new Matrix2f();
+ for (int i = v.mMat.length - 1; i >= 0; i--) {
+ v.mMat[i] = subF32();
+ }
+ return v;
+ }
+
public void addBoolean(boolean v) {
addI8((byte)(v ? 1 : 0));
}
+ public boolean subBoolean() {
+ byte v = subI8();
+ if (v == 1) {
+ return true;
+ }
+ return false;
+ }
+
public final byte[] getData() {
return mData;
}
@@ -344,6 +590,7 @@ public class FieldPacker {
private final byte mData[];
private int mPos;
private int mLen;
+ private BitSet mAlignment;
}
diff --git a/graphics/java/android/renderscript/FileA3D.java b/graphics/java/android/renderscript/FileA3D.java
index 42b508b..8b0222a 100644
--- a/graphics/java/android/renderscript/FileA3D.java
+++ b/graphics/java/android/renderscript/FileA3D.java
@@ -28,6 +28,7 @@ import android.util.Log;
import android.util.TypedValue;
/**
+ * @hide
* @deprecated in API 16
* FileA3D allows users to load Renderscript objects from files
* or resources stored on disk. It could be used to load items
diff --git a/graphics/java/android/renderscript/Font.java b/graphics/java/android/renderscript/Font.java
index 8a49abb..1a8d5bf 100644
--- a/graphics/java/android/renderscript/Font.java
+++ b/graphics/java/android/renderscript/Font.java
@@ -30,8 +30,9 @@ import android.util.Log;
import android.util.TypedValue;
/**
+ * @hide
* @deprecated in API 16
- * <p>This class gives users a simple way to draw hardware accelerated text.
+ * <p>This class gives users a simple way to draw hardware accelerated text.
* Internally, the glyphs are rendered using the Freetype library and an internal cache of
* rendered glyph bitmaps is maintained. Each font object represents a combination of a typeface,
* and point size. You can create multiple font objects to represent styles such as bold or italic text,
@@ -43,7 +44,7 @@ import android.util.TypedValue;
* render large batches of text in sequence. It is also more efficient to render multiple
* characters at once instead of one by one to improve draw call batching.</p>
* <p>Font color and transparency are not part of the font object and you can freely modify
- * them in the script to suit the user's rendering needs. Font colors work as a state machine.
+ * them in the script to suit the user's rendering needs. Font colors work as a state machine.
* Every new call to draw text uses the last color set in the script.</p>
**/
public class Font extends BaseObj {
diff --git a/graphics/java/android/renderscript/Mesh.java b/graphics/java/android/renderscript/Mesh.java
index 7210513..d0d383d 100644
--- a/graphics/java/android/renderscript/Mesh.java
+++ b/graphics/java/android/renderscript/Mesh.java
@@ -21,6 +21,7 @@ import java.util.Vector;
import android.util.Log;
/**
+ * @hide
* @deprecated in API 16
* <p>This class is a container for geometric data displayed with
* Renderscript. Internally, a mesh is a collection of allocations that
diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java
index d9f64c6..9bd103e 100644
--- a/graphics/java/android/renderscript/Program.java
+++ b/graphics/java/android/renderscript/Program.java
@@ -26,6 +26,7 @@ import android.util.Log;
/**
+ * @hide
*
* Program is a base class for all the objects that modify
* various stages of the graphics pipeline
diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java
index 69968ac..dd0f9f5 100644
--- a/graphics/java/android/renderscript/ProgramFragment.java
+++ b/graphics/java/android/renderscript/ProgramFragment.java
@@ -21,6 +21,7 @@ import android.util.Log;
/**
+ * @hide
* @deprecated in API 16
* <p>The Renderscript fragment program, also known as fragment shader is responsible
* for manipulating pixel data in a user defined way. It's constructed from a GLSL
diff --git a/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java b/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java
index 848c5a3..8ae1777 100644
--- a/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java
+++ b/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java
@@ -21,6 +21,7 @@ import android.util.Log;
/**
+ * @hide
* @deprecated in API 16
* <p>ProgramFragmentFixedFunction is a helper class that provides
* a way to make a simple fragment shader without writing any
diff --git a/graphics/java/android/renderscript/ProgramRaster.java b/graphics/java/android/renderscript/ProgramRaster.java
index c44521b..216cb4e 100644
--- a/graphics/java/android/renderscript/ProgramRaster.java
+++ b/graphics/java/android/renderscript/ProgramRaster.java
@@ -21,6 +21,7 @@ import android.util.Log;
/**
+ * @hide
* @deprecated in API 16
* Program raster is primarily used to specify whether point sprites are enabled and to control
* the culling mode. By default, back faces are culled.
diff --git a/graphics/java/android/renderscript/ProgramStore.java b/graphics/java/android/renderscript/ProgramStore.java
index d0fd6e5..dac9e76 100644
--- a/graphics/java/android/renderscript/ProgramStore.java
+++ b/graphics/java/android/renderscript/ProgramStore.java
@@ -21,6 +21,7 @@ import android.util.Log;
/**
+ * @hide
* <p>ProgramStore contains a set of parameters that control how
* the graphics hardware handles writes to the framebuffer.
* It could be used to:</p>
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index 2bd5124..50e32f6 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -14,7 +14,8 @@
* limitations under the License.
*/
- /**
+/**
+ * @hide
* <p>The Renderscript vertex program, also known as a vertex shader, describes a stage in
* the graphics pipeline responsible for manipulating geometric data in a user-defined way.
* The object is constructed by providing the Renderscript system with the following data:</p>
@@ -43,6 +44,7 @@ import android.util.Log;
/**
+ * @hide
* @deprecated in API 16
* ProgramVertex, also know as a vertex shader, describes a
* stage in the graphics pipeline responsible for manipulating
@@ -76,14 +78,15 @@ public class ProgramVertex extends Program {
}
/**
- * @deprecated in API 16
- * Builder class for creating ProgramVertex objects.
- * The builder starts empty and the user must minimally provide
- * the GLSL shader code, and the varying inputs. Constant, or
- * uniform parameters to the shader may optionally be provided as
- * well.
- *
- **/
+ * @hide
+ * @deprecated in API 16
+ * Builder class for creating ProgramVertex objects.
+ * The builder starts empty and the user must minimally provide
+ * the GLSL shader code, and the varying inputs. Constant, or
+ * uniform parameters to the shader may optionally be provided as
+ * well.
+ *
+ **/
public static class Builder extends BaseProgramBuilder {
/**
* @deprecated in API 16
diff --git a/graphics/java/android/renderscript/ProgramVertexFixedFunction.java b/graphics/java/android/renderscript/ProgramVertexFixedFunction.java
index 88cade4..ad486f3 100644
--- a/graphics/java/android/renderscript/ProgramVertexFixedFunction.java
+++ b/graphics/java/android/renderscript/ProgramVertexFixedFunction.java
@@ -22,6 +22,7 @@ import android.util.Log;
/**
+ * @hide
* @deprecated in API 16
* ProgramVertexFixedFunction is a helper class that provides a
* simple way to create a fixed function emulation vertex shader
diff --git a/graphics/java/android/renderscript/RSSurfaceView.java b/graphics/java/android/renderscript/RSSurfaceView.java
index 82ed95c..3c6c720 100644
--- a/graphics/java/android/renderscript/RSSurfaceView.java
+++ b/graphics/java/android/renderscript/RSSurfaceView.java
@@ -30,6 +30,7 @@ import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
+ * @hide
* @deprecated in API 16
* The Surface View for a graphics renderscript (RenderScriptGL) to draw on.
*
diff --git a/graphics/java/android/renderscript/RSTextureView.java b/graphics/java/android/renderscript/RSTextureView.java
index ed04000..7eeeeae 100644
--- a/graphics/java/android/renderscript/RSTextureView.java
+++ b/graphics/java/android/renderscript/RSTextureView.java
@@ -29,6 +29,7 @@ import android.util.Log;
import android.view.TextureView;
/**
+ * @hide
* @deprecated in API 16
* The Texture View for a graphics renderscript (RenderScriptGL)
* to draw on.
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index bef28aa..6f614c3 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -83,11 +83,7 @@ public class RenderScript {
native void nContextInitToClient(int con);
native void nContextDeinitToClient(int con);
- /**
- * Name of the file that holds the object cache.
- */
- private static final String CACHE_PATH = "com.android.renderscript.cache";
- static String mCachePath;
+ static File mCacheDir;
/**
* Sets the directory to use as a persistent storage for the
@@ -97,14 +93,33 @@ public class RenderScript {
* @param cacheDir A directory the current process can write to
*/
public static void setupDiskCache(File cacheDir) {
- File f = new File(cacheDir, CACHE_PATH);
- mCachePath = f.getAbsolutePath();
- f.mkdirs();
+ // Defer creation of cache path to nScriptCCreate().
+ mCacheDir = cacheDir;
}
+ /**
+ * ContextType specifies the specific type of context to be created.
+ *
+ */
public enum ContextType {
+ /**
+ * NORMAL context, this is the default and what shipping apps should
+ * use.
+ */
NORMAL (0),
+
+ /**
+ * DEBUG context, perform extra runtime checks to validate the
+ * kernels and APIs are being used as intended. Get and SetElementAt
+ * will be bounds checked in this mode.
+ */
DEBUG (1),
+
+ /**
+ * PROFILE context, Intended to be used once the first time an
+ * application is run on a new device. This mode allows the runtime to
+ * do additional testing and performance tuning.
+ */
PROFILE (2);
int mID;
@@ -420,6 +435,46 @@ public class RenderScript {
rsnAllocationData2D(mContext, id, xoff, yoff, mip, face, b);
}
+ native void rsnAllocationData3D(int con,
+ int dstAlloc, int dstXoff, int dstYoff, int dstZoff,
+ int dstMip,
+ int width, int height, int depth,
+ int srcAlloc, int srcXoff, int srcYoff, int srcZoff,
+ int srcMip);
+ synchronized void nAllocationData3D(int dstAlloc, int dstXoff, int dstYoff, int dstZoff,
+ int dstMip,
+ int width, int height, int depth,
+ int srcAlloc, int srcXoff, int srcYoff, int srcZoff,
+ int srcMip) {
+ validate();
+ rsnAllocationData3D(mContext,
+ dstAlloc, dstXoff, dstYoff, dstZoff,
+ dstMip, width, height, depth,
+ srcAlloc, srcXoff, srcYoff, srcZoff, srcMip);
+ }
+
+ native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, byte[] d, int sizeBytes);
+ synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, byte[] d, int sizeBytes) {
+ validate();
+ rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+ }
+ native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, short[] d, int sizeBytes);
+ synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, short[] d, int sizeBytes) {
+ validate();
+ rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+ }
+ native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, int[] d, int sizeBytes);
+ synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, int[] d, int sizeBytes) {
+ validate();
+ rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+ }
+ native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, float[] d, int sizeBytes);
+ synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, float[] d, int sizeBytes) {
+ validate();
+ rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+ }
+
+
native void rsnAllocationRead(int con, int id, byte[] d);
synchronized void nAllocationRead(int id, byte[] d) {
validate();
@@ -451,11 +506,6 @@ public class RenderScript {
validate();
rsnAllocationResize1D(mContext, id, dimX);
}
- native void rsnAllocationResize2D(int con, int id, int dimX, int dimY);
- synchronized void nAllocationResize2D(int id, int dimX, int dimY) {
- validate();
- rsnAllocationResize2D(mContext, id, dimX, dimY);
- }
native int rsnFileA3DCreateFromAssetStream(int con, int assetStream);
synchronized int nFileA3DCreateFromAssetStream(int assetStream) {
@@ -550,31 +600,59 @@ public class RenderScript {
validate();
rsnScriptInvokeV(mContext, id, slot, params);
}
+
native void rsnScriptSetVarI(int con, int id, int slot, int val);
synchronized void nScriptSetVarI(int id, int slot, int val) {
validate();
rsnScriptSetVarI(mContext, id, slot, val);
}
+ native int rsnScriptGetVarI(int con, int id, int slot);
+ synchronized int nScriptGetVarI(int id, int slot) {
+ validate();
+ return rsnScriptGetVarI(mContext, id, slot);
+ }
+
native void rsnScriptSetVarJ(int con, int id, int slot, long val);
synchronized void nScriptSetVarJ(int id, int slot, long val) {
validate();
rsnScriptSetVarJ(mContext, id, slot, val);
}
+ native long rsnScriptGetVarJ(int con, int id, int slot);
+ synchronized long nScriptGetVarJ(int id, int slot) {
+ validate();
+ return rsnScriptGetVarJ(mContext, id, slot);
+ }
+
native void rsnScriptSetVarF(int con, int id, int slot, float val);
synchronized void nScriptSetVarF(int id, int slot, float val) {
validate();
rsnScriptSetVarF(mContext, id, slot, val);
}
+ native float rsnScriptGetVarF(int con, int id, int slot);
+ synchronized float nScriptGetVarF(int id, int slot) {
+ validate();
+ return rsnScriptGetVarF(mContext, id, slot);
+ }
native void rsnScriptSetVarD(int con, int id, int slot, double val);
synchronized void nScriptSetVarD(int id, int slot, double val) {
validate();
rsnScriptSetVarD(mContext, id, slot, val);
}
+ native double rsnScriptGetVarD(int con, int id, int slot);
+ synchronized double nScriptGetVarD(int id, int slot) {
+ validate();
+ return rsnScriptGetVarD(mContext, id, slot);
+ }
native void rsnScriptSetVarV(int con, int id, int slot, byte[] val);
synchronized void nScriptSetVarV(int id, int slot, byte[] val) {
validate();
rsnScriptSetVarV(mContext, id, slot, val);
}
+ native void rsnScriptGetVarV(int con, int id, int slot, byte[] val);
+ synchronized void nScriptGetVarV(int id, int slot, byte[] val) {
+ validate();
+ rsnScriptGetVarV(mContext, id, slot, val);
+ }
native void rsnScriptSetVarVE(int con, int id, int slot, byte[] val,
int e, int[] dims);
synchronized void nScriptSetVarVE(int id, int slot, byte[] val,
@@ -853,7 +931,8 @@ public class RenderScript {
}
/**
- * @hide
+ * Place a message into the message queue to be sent back to the message
+ * handler once all previous commands have been executed.
*
* @param id
* @param data
@@ -934,6 +1013,7 @@ public class RenderScript {
static final int RS_MESSAGE_TO_CLIENT_RESIZE = 2;
static final int RS_MESSAGE_TO_CLIENT_ERROR = 3;
static final int RS_MESSAGE_TO_CLIENT_USER = 4;
+ static final int RS_MESSAGE_TO_CLIENT_NEW_BUFFER = 5;
static final int RS_ERROR_FATAL_UNKNOWN = 0x1000;
@@ -993,6 +1073,11 @@ public class RenderScript {
continue;
}
+ if (msg == RS_MESSAGE_TO_CLIENT_NEW_BUFFER) {
+ Allocation.sendBufferNotification(subID);
+ continue;
+ }
+
// 2: teardown.
// But we want to avoid starving other threads during
// teardown by yielding until the next line in the destructor
@@ -1061,9 +1146,9 @@ public class RenderScript {
/**
* Create a basic RenderScript context.
*
- * @hide
*
* @param ctx The context.
+ * @param ct The type of context to be created.
* @return RenderScript
*/
public static RenderScript create(Context ctx, ContextType ct) {
diff --git a/graphics/java/android/renderscript/RenderScriptGL.java b/graphics/java/android/renderscript/RenderScriptGL.java
index 5269405..52034b1 100644
--- a/graphics/java/android/renderscript/RenderScriptGL.java
+++ b/graphics/java/android/renderscript/RenderScriptGL.java
@@ -29,6 +29,7 @@ import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
+ * @hide
* @deprecated in API 16
* The Graphics derivitive of Renderscript. Extends the basic context to add a
* root script which is the display window for graphical output. When the
diff --git a/graphics/java/android/renderscript/Script.java b/graphics/java/android/renderscript/Script.java
index b405588..b4ba943 100644
--- a/graphics/java/android/renderscript/Script.java
+++ b/graphics/java/android/renderscript/Script.java
@@ -220,6 +220,9 @@ public class Script extends BaseObj {
public void setVar(int index, float v) {
mRS.nScriptSetVarF(getID(mRS), index, v);
}
+ public float getVarF(int index) {
+ return mRS.nScriptGetVarF(getID(mRS), index);
+ }
/**
* Only intended for use by generated reflected code.
@@ -230,6 +233,9 @@ public class Script extends BaseObj {
public void setVar(int index, double v) {
mRS.nScriptSetVarD(getID(mRS), index, v);
}
+ public double getVarD(int index) {
+ return mRS.nScriptGetVarD(getID(mRS), index);
+ }
/**
* Only intended for use by generated reflected code.
@@ -240,6 +246,10 @@ public class Script extends BaseObj {
public void setVar(int index, int v) {
mRS.nScriptSetVarI(getID(mRS), index, v);
}
+ public int getVarI(int index) {
+ return mRS.nScriptGetVarI(getID(mRS), index);
+ }
+
/**
* Only intended for use by generated reflected code.
@@ -250,6 +260,10 @@ public class Script extends BaseObj {
public void setVar(int index, long v) {
mRS.nScriptSetVarJ(getID(mRS), index, v);
}
+ public long getVarJ(int index) {
+ return mRS.nScriptGetVarJ(getID(mRS), index);
+ }
+
/**
* Only intended for use by generated reflected code.
@@ -260,6 +274,9 @@ public class Script extends BaseObj {
public void setVar(int index, boolean v) {
mRS.nScriptSetVarI(getID(mRS), index, v ? 1 : 0);
}
+ public boolean getVarB(int index) {
+ return mRS.nScriptGetVarI(getID(mRS), index) > 0 ? true : false;
+ }
/**
* Only intended for use by generated reflected code.
@@ -293,6 +310,10 @@ public class Script extends BaseObj {
mRS.nScriptSetVarVE(getID(mRS), index, v.getData(), e.getID(mRS), dims);
}
+ public void getVarV(int index, FieldPacker v) {
+ mRS.nScriptGetVarV(getID(mRS), index, v.getData());
+ }
+
public void setTimeZone(String timeZone) {
mRS.validate();
try {
diff --git a/graphics/java/android/renderscript/ScriptC.java b/graphics/java/android/renderscript/ScriptC.java
index 108b230..221f760 100644
--- a/graphics/java/android/renderscript/ScriptC.java
+++ b/graphics/java/android/renderscript/ScriptC.java
@@ -62,6 +62,12 @@ public class ScriptC extends Script {
setID(id);
}
+ /**
+ * Name of the file that holds the object cache.
+ */
+ private static final String CACHE_PATH = "com.android.renderscript.cache";
+
+ static String mCachePath;
private static synchronized int internalCreate(RenderScript rs, Resources resources, int resourceID) {
byte[] pgm;
@@ -94,7 +100,13 @@ public class ScriptC extends Script {
String resName = resources.getResourceEntryName(resourceID);
+ // Create the RS cache path if we haven't done so already.
+ if (mCachePath == null) {
+ File f = new File(rs.mCacheDir, CACHE_PATH);
+ mCachePath = f.getAbsolutePath();
+ f.mkdirs();
+ }
Log.v(TAG, "Create script for resource = " + resName);
- return rs.nScriptCCreate(resName, rs.mCachePath, pgm, pgmLength);
+ return rs.nScriptCCreate(resName, mCachePath, pgm, pgmLength);
}
}
diff --git a/graphics/java/android/renderscript/ScriptIntrinsic3DLUT.java b/graphics/java/android/renderscript/ScriptIntrinsic3DLUT.java
index 3e58b87..86f37d8 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsic3DLUT.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsic3DLUT.java
@@ -20,7 +20,11 @@ import android.util.Log;
/**
*
- * @hide
+ * Intrinsic for converting RGB to RGBA by using a 3D lookup table. The
+ * incoming r,g,b values are use as normalized x,y,z coordinates into a 3D
+ * allocation. The 8 nearest values are sampled and linearly interpolated. The
+ * result is placed in the output.
+ *
**/
public final class ScriptIntrinsic3DLUT extends ScriptIntrinsic {
private Allocation mLUT;
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index d7c8255..a5e24ce 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -112,7 +112,7 @@ public class Type extends BaseObj {
/**
* Get the YUV format
*
- * @hide
+ *
* @return int
*/
public int getYuv() {
@@ -277,7 +277,9 @@ public class Type extends BaseObj {
}
/**
- * @hide
+ * Set the YUV layout for a Type. This controls how the memory is
+ * interpreted. Generally and application should not need to call this
+ * function and it would be set by the Camera.
*
* only NV21, YV12. Enums from ImageFormat
*/
diff --git a/graphics/java/android/renderscript/package.html b/graphics/java/android/renderscript/package.html
index 5eab23c..eb178c1 100644
--- a/graphics/java/android/renderscript/package.html
+++ b/graphics/java/android/renderscript/package.html
@@ -1,86 +1,10 @@
<HTML>
<BODY>
-<p>The Renderscript rendering and computational APIs offer a low-level, high performance means of
-carrying out mathematical calculations and 3D graphics rendering.</p>
+<p>RenderScript provides support for high-performance computation across heterogeneous processors.</p>
<p>For more information, see the
-<a href="{@docRoot}guide/topics/renderscript/index.html">Renderscript</a> developer guide.</p>
+<a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> developer guide.</p>
{@more}
-<p>An example of Renderscript in applications include the 3D carousel view that is present in
-Android 3.0 applications such as the Books and YouTube applications. This API is intended for
-developers who are comfortable working with native code and want to maximize their performance
-critical applications.</p>
-
-<p>Renderscript adopts a control and slave architecture where the low-level native code is controlled by the
-higher level Android system that runs in the virtual machine (VM). The VM code handles resource
-allocation and lifecycle management of the Renderscript enabled application and calls the Renderscript
-code through high level entry points. The Android build tools generate these entry points through reflection on
-the native Renderscript code, which you write in C (C99 standard). The Renderscript code
-does the intensive computation and returns the result back to the Android VM.</p>
-
-<p>You can find the Renderscript native
-APIs in the <code>&lt;sdk_root&gt;/platforms/android-11/renderscript</code> directory.
-The Android system APIs are broken into a few main groups:</p>
-
-<h4>Core</h4>
-<p>These classes are used internally by the system for memory allocation. They are used by the classes that
-are generated by the build tools:</p>
-<ul>
- <li>Allocation</li>
- <li>Element</li>
- <li>Type</li>
- <li>Script</li>
-</ul>
-
-
-<h4>Data Types</h4>
-<p>These data types are used by the classes that are generated
-by the build tools. They are the reflected counterparts of the native data types that
-are defined by the native Renderscript APIs and used by your Renderscript code. The
-classes include:</p>
-<ul>
- <li>Byte2, Byte3, and Byte4</li>
- <li>Float2, Float3, Float4</li>
- <li>Int2, Int3, Int4</li>
- <li>Long2, Long3, Long4</li>
- <li>Matrix2f, Matrix3f, Matrix4f</li>
- <li>Short2, Short3, Short4</li>
-</ul>
-
-<p>For example, if you declared the following struct in your .rs Renderscript file:</p>
-
-<pre>struct Hello { float3 position; rs_matrix4x4 transform; }</pre>
-
-<p>The build tools generate a class through reflection that looks like the following:</p>
-<pre>
-class Hello {
- static public class Item {
- Float4 position;
- Matrix4f transform;
- }
-Element createElement(RenderScript rs) {
- Element.Builder eb = new Element.Builder(rs);
- eb.add(Element.F32_3(rs), "position");
- eb.add(Element.MATRIX_4X4(rs), "transform");
- return eb.create();
- }
-}
-</pre>
-
-<h4>Graphics</h4>
-<p>These classes are specific to graphics Renderscripts and support a typical rendering
-pipeline.</p>
-<ul>
-<li>Mesh</li>
-<li>ProgramFragment</li>
-<li>ProgramRaster</li>
-<li>ProgramStore</li>
-<li>ProgramVertex</li>
-<li>RSSurfaceView</li>
-<li>Sampler</li>
-</ul>
-
-</p>
</BODY>
</HTML>
diff --git a/graphics/jni/Android.mk b/graphics/jni/Android.mk
index 80d7728..e8beae5 100644
--- a/graphics/jni/Android.mk
+++ b/graphics/jni/Android.mk
@@ -10,6 +10,7 @@ LOCAL_SHARED_LIBRARIES := \
libnativehelper \
libRS \
libcutils \
+ liblog \
libskia \
libutils \
libui \
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 8757b19..b9f8713 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -725,6 +725,72 @@ nAllocationData2D_alloc(JNIEnv *_env, jobject _this, RsContext con,
}
static void
+nAllocationData3D_s(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
+ jint w, jint h, jint d, jshortArray data, int sizeBytes)
+{
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation3DData_s, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
+ jshort *ptr = _env->GetShortArrayElements(data, NULL);
+ rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ _env->ReleaseShortArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData3D_b(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
+ jint w, jint h, jint d, jbyteArray data, int sizeBytes)
+{
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation3DData_b, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
+ jbyte *ptr = _env->GetByteArrayElements(data, NULL);
+ rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData3D_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
+ jint w, jint h, jint d, jintArray data, int sizeBytes)
+{
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation3DData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
+ jint *ptr = _env->GetIntArrayElements(data, NULL);
+ rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData3D_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
+ jint w, jint h, jint d, jfloatArray data, int sizeBytes)
+{
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation3DData_f, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
+ jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+ rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData3D_alloc(JNIEnv *_env, jobject _this, RsContext con,
+ jint dstAlloc, jint dstXoff, jint dstYoff, jint dstZoff,
+ jint dstMip,
+ jint width, jint height, jint depth,
+ jint srcAlloc, jint srcXoff, jint srcYoff, jint srcZoff,
+ jint srcMip)
+{
+ LOG_API("nAllocationData3D_alloc, con(%p), dstAlloc(%p), dstXoff(%i), dstYoff(%i),"
+ " dstMip(%i), width(%i), height(%i),"
+ " srcAlloc(%p), srcXoff(%i), srcYoff(%i), srcMip(%i)",
+ con, (RsAllocation)dstAlloc, dstXoff, dstYoff, dstMip, dstFace,
+ width, height, (RsAllocation)srcAlloc, srcXoff, srcYoff, srcMip, srcFace);
+
+ rsAllocationCopy3DRange(con,
+ (RsAllocation)dstAlloc,
+ dstXoff, dstYoff, dstZoff, dstMip,
+ width, height, depth,
+ (RsAllocation)srcAlloc,
+ srcXoff, srcYoff, srcZoff, srcMip);
+}
+
+static void
nAllocationRead_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jintArray data)
{
jint len = _env->GetArrayLength(data);
@@ -782,13 +848,6 @@ nAllocationResize1D(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint
rsAllocationResize1D(con, (RsAllocation)alloc, dimX);
}
-static void
-nAllocationResize2D(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint dimX, jint dimY)
-{
- LOG_API("nAllocationResize1D, con(%p), alloc(%p), sizeX(%i), sizeY(%i)", con, (RsAllocation)alloc, dimX, dimY);
- rsAllocationResize2D(con, (RsAllocation)alloc, dimX, dimY);
-}
-
// -----------------------------------
static int
@@ -928,6 +987,15 @@ nScriptSetVarI(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slo
rsScriptSetVarI(con, (RsScript)script, slot, val);
}
+static jint
+nScriptGetVarI(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot)
+{
+ LOG_API("nScriptGetVarI, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+ int value = 0;
+ rsScriptGetVarV(con, (RsScript)script, slot, &value, sizeof(value));
+ return value;
+}
+
static void
nScriptSetVarObj(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jint val)
{
@@ -942,6 +1010,15 @@ nScriptSetVarJ(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slo
rsScriptSetVarJ(con, (RsScript)script, slot, val);
}
+static jlong
+nScriptGetVarJ(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot)
+{
+ LOG_API("nScriptGetVarJ, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+ jlong value = 0;
+ rsScriptGetVarV(con, (RsScript)script, slot, &value, sizeof(value));
+ return value;
+}
+
static void
nScriptSetVarF(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, float val)
{
@@ -949,6 +1026,15 @@ nScriptSetVarF(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slo
rsScriptSetVarF(con, (RsScript)script, slot, val);
}
+static jfloat
+nScriptGetVarF(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot)
+{
+ LOG_API("nScriptGetVarF, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+ jfloat value = 0;
+ rsScriptGetVarV(con, (RsScript)script, slot, &value, sizeof(value));
+ return value;
+}
+
static void
nScriptSetVarD(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, double val)
{
@@ -956,6 +1042,15 @@ nScriptSetVarD(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slo
rsScriptSetVarD(con, (RsScript)script, slot, val);
}
+static jdouble
+nScriptGetVarD(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot)
+{
+ LOG_API("nScriptGetVarD, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+ jdouble value = 0;
+ rsScriptGetVarV(con, (RsScript)script, slot, &value, sizeof(value));
+ return value;
+}
+
static void
nScriptSetVarV(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jbyteArray data)
{
@@ -967,6 +1062,16 @@ nScriptSetVarV(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slo
}
static void
+nScriptGetVarV(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jbyteArray data)
+{
+ LOG_API("nScriptSetVarV, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+ jint len = _env->GetArrayLength(data);
+ jbyte *ptr = _env->GetByteArrayElements(data, NULL);
+ rsScriptGetVarV(con, (RsScript)script, slot, ptr, len);
+ _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
nScriptSetVarVE(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jbyteArray data, jint elem, jintArray dims)
{
LOG_API("nScriptSetVarVE, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
@@ -1519,13 +1624,17 @@ static JNINativeMethod methods[] = {
{"rsnAllocationData2D", "(IIIIIIII[BI)V", (void*)nAllocationData2D_b },
{"rsnAllocationData2D", "(IIIIIIII[FI)V", (void*)nAllocationData2D_f },
{"rsnAllocationData2D", "(IIIIIIIIIIIII)V", (void*)nAllocationData2D_alloc },
+{"rsnAllocationData3D", "(IIIIIIIII[II)V", (void*)nAllocationData3D_i },
+{"rsnAllocationData3D", "(IIIIIIIII[SI)V", (void*)nAllocationData3D_s },
+{"rsnAllocationData3D", "(IIIIIIIII[BI)V", (void*)nAllocationData3D_b },
+{"rsnAllocationData3D", "(IIIIIIIII[FI)V", (void*)nAllocationData3D_f },
+{"rsnAllocationData3D", "(IIIIIIIIIIIIII)V", (void*)nAllocationData3D_alloc },
{"rsnAllocationRead", "(II[I)V", (void*)nAllocationRead_i },
{"rsnAllocationRead", "(II[S)V", (void*)nAllocationRead_s },
{"rsnAllocationRead", "(II[B)V", (void*)nAllocationRead_b },
{"rsnAllocationRead", "(II[F)V", (void*)nAllocationRead_f },
{"rsnAllocationGetType", "(II)I", (void*)nAllocationGetType},
{"rsnAllocationResize1D", "(III)V", (void*)nAllocationResize1D },
-{"rsnAllocationResize2D", "(IIII)V", (void*)nAllocationResize2D },
{"rsnAllocationGenerateMipmaps", "(II)V", (void*)nAllocationGenerateMipmaps },
{"rsnScriptBindAllocation", "(IIII)V", (void*)nScriptBindAllocation },
@@ -1537,10 +1646,15 @@ static JNINativeMethod methods[] = {
{"rsnScriptForEachClipped", "(IIIIIIIIIII)V", (void*)nScriptForEachClipped },
{"rsnScriptForEachClipped", "(IIIII[BIIIIII)V", (void*)nScriptForEachClippedV },
{"rsnScriptSetVarI", "(IIII)V", (void*)nScriptSetVarI },
+{"rsnScriptGetVarI", "(III)I", (void*)nScriptGetVarI },
{"rsnScriptSetVarJ", "(IIIJ)V", (void*)nScriptSetVarJ },
+{"rsnScriptGetVarJ", "(III)J", (void*)nScriptGetVarJ },
{"rsnScriptSetVarF", "(IIIF)V", (void*)nScriptSetVarF },
+{"rsnScriptGetVarF", "(III)F", (void*)nScriptGetVarF },
{"rsnScriptSetVarD", "(IIID)V", (void*)nScriptSetVarD },
+{"rsnScriptGetVarD", "(III)D", (void*)nScriptGetVarD },
{"rsnScriptSetVarV", "(III[B)V", (void*)nScriptSetVarV },
+{"rsnScriptGetVarV", "(III[B)V", (void*)nScriptGetVarV },
{"rsnScriptSetVarVE", "(III[BI[I)V", (void*)nScriptSetVarVE },
{"rsnScriptSetVarObj", "(IIII)V", (void*)nScriptSetVarObj },
diff --git a/include/androidfw/InputDevice.h b/include/androidfw/InputDevice.h
index 1aecf80..45dc2db 100644
--- a/include/androidfw/InputDevice.h
+++ b/include/androidfw/InputDevice.h
@@ -64,6 +64,7 @@ public:
float max;
float flat;
float fuzz;
+ float resolution;
};
void initialize(int32_t id, int32_t generation, const InputDeviceIdentifier& identifier,
@@ -83,7 +84,7 @@ public:
void addSource(uint32_t source);
void addMotionRange(int32_t axis, uint32_t source,
- float min, float max, float flat, float fuzz);
+ float min, float max, float flat, float fuzz, float resolution);
void addMotionRange(const MotionRange& range);
inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
diff --git a/include/androidfw/InputTransport.h b/include/androidfw/InputTransport.h
index 5706bce..8712995 100644
--- a/include/androidfw/InputTransport.h
+++ b/include/androidfw/InputTransport.h
@@ -168,6 +168,9 @@ public:
*/
status_t receiveMessage(InputMessage* msg);
+ /* Returns a new object that has a duplicate of this channel's fd. */
+ sp<InputChannel> dup() const;
+
private:
String8 mName;
int mFd;
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index c42001b..6975583 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -49,10 +49,7 @@ import java.security.spec.X509EncodedKeySpec;
*
* {@hide}
*/
-@SuppressWarnings("deprecation")
public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
- public static final String NAME = "AndroidKeyPairGenerator";
-
private android.security.KeyStore mKeyStore;
private AndroidKeyPairGeneratorSpec mSpec;
@@ -79,12 +76,21 @@ public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
"Must call initialize with an AndroidKeyPairGeneratorSpec first");
}
+ if (((mSpec.getFlags() & KeyStore.FLAG_ENCRYPTED) != 0)
+ && (mKeyStore.state() != KeyStore.State.UNLOCKED)) {
+ throw new IllegalStateException(
+ "Android keystore must be in initialized and unlocked state "
+ + "if encryption is required");
+ }
+
final String alias = mSpec.getKeystoreAlias();
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
- mKeyStore.generate(privateKeyAlias);
+ if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mSpec.getFlags())) {
+ throw new IllegalStateException("could not generate key in keystore");
+ }
final PrivateKey privKey;
final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
@@ -131,7 +137,8 @@ public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
throw new IllegalStateException("Can't get encoding of certificate", e);
}
- if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes)) {
+ if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF,
+ mSpec.getFlags())) {
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
}
diff --git a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
index 83faf35..b126f03 100644
--- a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
@@ -32,10 +32,9 @@ import javax.security.auth.x500.X500Principal;
* {@code KeyPairGenerator} that works with <a href="{@docRoot}
* guide/topics/security/keystore.html">Android KeyStore facility</a>. The
* Android KeyStore facility is accessed through a
- * {@link java.security.KeyPairGenerator} API using the
- * {@code AndroidKeyPairGenerator} provider. The {@code context} passed in may
- * be used to pop up some UI to ask the user to unlock or initialize the Android
- * keystore facility.
+ * {@link java.security.KeyPairGenerator} API using the {@code AndroidKeyStore}
+ * provider. The {@code context} passed in may be used to pop up some UI to ask
+ * the user to unlock or initialize the Android KeyStore facility.
* <p>
* After generation, the {@code keyStoreAlias} is used with the
* {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
@@ -47,12 +46,10 @@ import javax.security.auth.x500.X500Principal;
* Distinguished Name along with the other parameters specified with the
* {@link Builder}.
* <p>
- * The self-signed certificate may be replaced at a later time by a certificate
- * signed by a real Certificate Authority.
- *
- * @hide
+ * The self-signed X.509 certificate may be replaced at a later time by a
+ * certificate signed by a real Certificate Authority.
*/
-public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
+public final class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
private final String mKeystoreAlias;
private final Context mContext;
@@ -65,6 +62,8 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
private final Date mEndDate;
+ private final int mFlags;
+
/**
* Parameter specification for the "{@code AndroidKeyPairGenerator}"
* instance of the {@link java.security.KeyPairGenerator} API. The
@@ -95,7 +94,8 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
* @hide should be built with AndroidKeyPairGeneratorSpecBuilder
*/
public AndroidKeyPairGeneratorSpec(Context context, String keyStoreAlias,
- X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate) {
+ X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate,
+ int flags) {
if (context == null) {
throw new IllegalArgumentException("context == null");
} else if (TextUtils.isEmpty(keyStoreAlias)) {
@@ -118,51 +118,72 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
mSerialNumber = serialNumber;
mStartDate = startDate;
mEndDate = endDate;
+ mFlags = flags;
}
/**
- * @hide
+ * Returns the alias that will be used in the {@code java.security.KeyStore}
+ * in conjunction with the {@code AndroidKeyStore}.
*/
- String getKeystoreAlias() {
+ public String getKeystoreAlias() {
return mKeystoreAlias;
}
/**
- * @hide
+ * Gets the Android context used for operations with this instance.
*/
- Context getContext() {
+ public Context getContext() {
return mContext;
}
/**
- * @hide
+ * Gets the subject distinguished name to be used on the X.509 certificate
+ * that will be put in the {@link java.security.KeyStore}.
*/
- X500Principal getSubjectDN() {
+ public X500Principal getSubjectDN() {
return mSubjectDN;
}
/**
- * @hide
+ * Gets the serial number to be used on the X.509 certificate that will be
+ * put in the {@link java.security.KeyStore}.
*/
- BigInteger getSerialNumber() {
+ public BigInteger getSerialNumber() {
return mSerialNumber;
}
/**
- * @hide
+ * Gets the start date to be used on the X.509 certificate that will be put
+ * in the {@link java.security.KeyStore}.
*/
- Date getStartDate() {
+ public Date getStartDate() {
return mStartDate;
}
/**
- * @hide
+ * Gets the end date to be used on the X.509 certificate that will be put in
+ * the {@link java.security.KeyStore}.
*/
- Date getEndDate() {
+ public Date getEndDate() {
return mEndDate;
}
/**
+ * @hide
+ */
+ int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Returns {@code true} if this parameter will require generated keys to be
+ * encrypted in the {@link java.security.KeyStore}.
+ */
+ public boolean isEncryptionRequired() {
+ return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
+ }
+
+ /**
* Builder class for {@link AndroidKeyPairGeneratorSpec} objects.
* <p>
* This will build a parameter spec for use with the <a href="{@docRoot}
@@ -177,16 +198,17 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
* Calendar end = new Calendar();
* end.add(1, Calendar.YEAR);
*
- * AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec.Builder(mContext)
- * .setAlias("myKey")
- * .setSubject(new X500Principal("CN=myKey"))
- * .setSerial(BigInteger.valueOf(1337))
- * .setStartDate(start.getTime())
- * .setEndDate(end.getTime())
- * .build();
+ * AndroidKeyPairGeneratorSpec spec =
+ * new AndroidKeyPairGeneratorSpec.Builder(mContext)
+ * .setAlias(&quot;myKey&quot;)
+ * .setSubject(new X500Principal(&quot;CN=myKey&quot;))
+ * .setSerial(BigInteger.valueOf(1337))
+ * .setStartDate(start.getTime())
+ * .setEndDate(end.getTime())
+ * .build();
* </pre>
*/
- public static class Builder {
+ public final static class Builder {
private final Context mContext;
private String mKeystoreAlias;
@@ -199,6 +221,14 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
private Date mEndDate;
+ private int mFlags;
+
+ /**
+ * Creates a new instance of the {@code Builder} with the given
+ * {@code context}. The {@code context} passed in may be used to pop up
+ * some UI to ask the user to unlock or initialize the Android KeyStore
+ * facility.
+ */
public Builder(Context context) {
if (context == null) {
throw new NullPointerException("context == null");
@@ -268,6 +298,17 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
}
/**
+ * Indicates that this key must be encrypted at rest on storage. Note
+ * that enabling this will require that the user enable a strong lock
+ * screen (e.g., PIN, password) before creating or using the generated
+ * key is successful.
+ */
+ public Builder setEncryptionRequired() {
+ mFlags |= KeyStore.FLAG_ENCRYPTED;
+ return this;
+ }
+
+ /**
* Builds the instance of the {@code AndroidKeyPairGeneratorSpec}.
*
* @throws IllegalArgumentException if a required field is missing
@@ -275,7 +316,7 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
*/
public AndroidKeyPairGeneratorSpec build() {
return new AndroidKeyPairGeneratorSpec(mContext, mKeystoreAlias, mSubjectDN,
- mSerialNumber, mStartDate, mEndDate);
+ mSerialNumber, mStartDate, mEndDate, mFlags);
}
}
}
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 8a9826b..dcc9516 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -27,6 +27,10 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.Key;
+import java.security.KeyStore.Entry;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStore.ProtectionParameter;
+import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
@@ -198,14 +202,14 @@ public class AndroidKeyStore extends KeyStoreSpi {
}
if (key instanceof PrivateKey) {
- setPrivateKeyEntry(alias, (PrivateKey) key, chain);
+ setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
} else {
throw new KeyStoreException("Only PrivateKeys are supported");
}
}
- private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain)
- throws KeyStoreException {
+ private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
+ AndroidKeyStoreParameter params) throws KeyStoreException {
byte[] keyBytes = null;
final String pkeyAlias;
@@ -317,15 +321,20 @@ public class AndroidKeyStore extends KeyStoreSpi {
Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
}
+ final int flags = (params == null) ? 0 : params.getFlags();
+
if (shouldReplacePrivateKey
- && !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes)) {
+ && !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes,
+ android.security.KeyStore.UID_SELF, flags)) {
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
throw new KeyStoreException("Couldn't put private key in keystore");
- } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes)) {
+ } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes,
+ android.security.KeyStore.UID_SELF, flags)) {
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
throw new KeyStoreException("Couldn't put certificate #1 in keystore");
} else if (chainBytes != null
- && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes)) {
+ && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes,
+ android.security.KeyStore.UID_SELF, flags)) {
Credentials.deleteAllTypesForAlias(mKeyStore, alias);
throw new KeyStoreException("Couldn't put certificate chain in keystore");
}
@@ -355,7 +364,8 @@ public class AndroidKeyStore extends KeyStoreSpi {
throw new KeyStoreException(e);
}
- if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded)) {
+ if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded,
+ android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE)) {
throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
}
}
@@ -517,4 +527,37 @@ public class AndroidKeyStore extends KeyStoreSpi {
mKeyStore = android.security.KeyStore.getInstance();
}
+ @Override
+ public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
+ throws KeyStoreException {
+ if (entry == null) {
+ throw new KeyStoreException("entry == null");
+ }
+
+ if (engineContainsAlias(alias)) {
+ engineDeleteEntry(alias);
+ }
+
+ if (entry instanceof KeyStore.TrustedCertificateEntry) {
+ KeyStore.TrustedCertificateEntry trE = (KeyStore.TrustedCertificateEntry) entry;
+ engineSetCertificateEntry(alias, trE.getTrustedCertificate());
+ return;
+ }
+
+ if (param != null && !(param instanceof AndroidKeyStoreParameter)) {
+ throw new KeyStoreException("protParam should be AndroidKeyStoreParameter; was: "
+ + param.getClass().getName());
+ }
+
+ if (entry instanceof PrivateKeyEntry) {
+ PrivateKeyEntry prE = (PrivateKeyEntry) entry;
+ setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(),
+ (AndroidKeyStoreParameter) param);
+ return;
+ }
+
+ throw new KeyStoreException(
+ "Entry must be a PrivateKeyEntry or TrustedCertificateEntry; was " + entry);
+ }
+
}
diff --git a/keystore/java/android/security/AndroidKeyStoreParameter.java b/keystore/java/android/security/AndroidKeyStoreParameter.java
new file mode 100644
index 0000000..44f57c4
--- /dev/null
+++ b/keystore/java/android/security/AndroidKeyStoreParameter.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.content.Context;
+import android.security.AndroidKeyPairGeneratorSpec.Builder;
+
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.KeyStore.ProtectionParameter;
+import java.security.cert.Certificate;
+
+/**
+ * This provides the optional parameters that can be specified for
+ * {@code KeyStore} entries that work with <a href="{@docRoot}
+ * guide/topics/security/keystore.html">Android KeyStore facility</a>. The
+ * Android KeyStore facility is accessed through a
+ * {@link java.security.KeyStore} API using the {@code AndroidKeyStore}
+ * provider. The {@code context} passed in may be used to pop up some UI to ask
+ * the user to unlock or initialize the Android KeyStore facility.
+ * <p>
+ * Any entries placed in the {@code KeyStore} may be retrieved later. Note that
+ * there is only one logical instance of the {@code KeyStore} per application
+ * UID so apps using the {@code sharedUid} facility will also share a
+ * {@code KeyStore}.
+ * <p>
+ * Keys may be generated using the {@link KeyPairGenerator} facility with a
+ * {@link AndroidKeyPairGeneratorSpec} to specify the entry's {@code alias}. A
+ * self-signed X.509 certificate will be attached to generated entries, but that
+ * may be replaced at a later time by a certificate signed by a real Certificate
+ * Authority.
+ */
+public final class AndroidKeyStoreParameter implements ProtectionParameter {
+ private int mFlags;
+
+ private AndroidKeyStoreParameter(int flags) {
+ mFlags = flags;
+ }
+
+ /**
+ * @hide
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Returns {@code true} if this parameter requires entries to be encrypted
+ * on the disk.
+ */
+ public boolean isEncryptionRequired() {
+ return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
+ }
+
+ /**
+ * Builder class for {@link AndroidKeyStoreParameter} objects.
+ * <p>
+ * This will build protection parameters for use with the <a
+ * href="{@docRoot} guide/topics/security/keystore.html">Android KeyStore
+ * facility</a>.
+ * <p>
+ * This can be used to require that KeyStore entries be stored encrypted.
+ * <p>
+ * Example:
+ *
+ * <pre class="prettyprint">
+ * AndroidKeyStoreParameter params =
+ * new AndroidKeyStoreParameter.Builder(mContext).setEncryptionRequired().build();
+ * </pre>
+ */
+ public final static class Builder {
+ private int mFlags;
+
+ /**
+ * Creates a new instance of the {@code Builder} with the given
+ * {@code context}. The {@code context} passed in may be used to pop up
+ * some UI to ask the user to unlock or initialize the Android KeyStore
+ * facility.
+ */
+ public Builder(Context context) {
+ if (context == null) {
+ throw new NullPointerException("context == null");
+ }
+
+ // Context is currently not used, but will be in the future.
+ }
+
+ /**
+ * Indicates that this key must be encrypted at rest on storage. Note
+ * that enabling this will require that the user enable a strong lock
+ * screen (e.g., PIN, password) before creating or using the generated
+ * key is successful.
+ */
+ public Builder setEncryptionRequired() {
+ mFlags |= KeyStore.FLAG_ENCRYPTED;
+ return this;
+ }
+
+ /**
+ * Builds the instance of the {@code AndroidKeyPairGeneratorSpec}.
+ *
+ * @throws IllegalArgumentException if a required field is missing
+ * @return built instance of {@code AndroidKeyPairGeneratorSpec}
+ */
+ public AndroidKeyStoreParameter build() {
+ return new AndroidKeyStoreParameter(mFlags);
+ }
+ }
+}
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index 40d7e1a..8ca301e 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -33,7 +33,6 @@ public class AndroidKeyStoreProvider extends Provider {
put("KeyStore." + AndroidKeyStore.NAME, AndroidKeyStore.class.getName());
// java.security.KeyPairGenerator
- put("KeyPairGenerator." + AndroidKeyPairGenerator.NAME,
- AndroidKeyPairGenerator.class.getName());
+ put("KeyPairGenerator." + AndroidKeyStore.NAME, AndroidKeyPairGenerator.class.getName());
}
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 852f0bb..fb5e039 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -40,6 +40,13 @@ public class KeyStore {
public static final int UNDEFINED_ACTION = 9;
public static final int WRONG_PASSWORD = 10;
+ // Used for UID field to indicate the calling UID.
+ public static final int UID_SELF = -1;
+
+ // Flags for "put" "import" and "generate"
+ public static final int FLAG_NONE = 0;
+ public static final int FLAG_ENCRYPTED = 1;
+
// States
public enum State { UNLOCKED, LOCKED, UNINITIALIZED };
@@ -87,19 +94,15 @@ public class KeyStore {
}
}
- public boolean put(String key, byte[] value, int uid) {
+ public boolean put(String key, byte[] value, int uid, int flags) {
try {
- return mBinder.insert(key, value, uid) == NO_ERROR;
+ return mBinder.insert(key, value, uid, flags) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
}
}
- public boolean put(String key, byte[] value) {
- return put(key, value, -1);
- }
-
public boolean delete(String key, int uid) {
try {
return mBinder.del(key, uid) == NO_ERROR;
@@ -110,7 +113,7 @@ public class KeyStore {
}
public boolean delete(String key) {
- return delete(key, -1);
+ return delete(key, UID_SELF);
}
public boolean contains(String key, int uid) {
@@ -123,7 +126,7 @@ public class KeyStore {
}
public boolean contains(String key) {
- return contains(key, -1);
+ return contains(key, UID_SELF);
}
public String[] saw(String prefix, int uid) {
@@ -136,7 +139,7 @@ public class KeyStore {
}
public String[] saw(String prefix) {
- return saw(prefix, -1);
+ return saw(prefix, UID_SELF);
}
public boolean reset() {
@@ -185,32 +188,24 @@ public class KeyStore {
}
}
- public boolean generate(String key, int uid) {
+ public boolean generate(String key, int uid, int flags) {
try {
- return mBinder.generate(key, uid) == NO_ERROR;
+ return mBinder.generate(key, uid, flags) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
}
}
- public boolean generate(String key) {
- return generate(key, -1);
- }
-
- public boolean importKey(String keyName, byte[] key, int uid) {
+ public boolean importKey(String keyName, byte[] key, int uid, int flags) {
try {
- return mBinder.import_key(keyName, key, uid) == NO_ERROR;
+ return mBinder.import_key(keyName, key, uid, flags) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
}
}
- public boolean importKey(String keyName, byte[] key) {
- return importKey(keyName, key, -1);
- }
-
public byte[] getPubkey(String key) {
try {
return mBinder.get_pubkey(key);
@@ -230,7 +225,7 @@ public class KeyStore {
}
public boolean delKey(String key) {
- return delKey(key, -1);
+ return delKey(key, UID_SELF);
}
public byte[] sign(String key, byte[] data) {
diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
index 3d275cd..5d4ab9c 100644
--- a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
@@ -39,8 +39,9 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase {
private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
public void testConstructor_Success() throws Exception {
- AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec(getContext(),
- TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW, NOW_PLUS_10_YEARS);
+ AndroidKeyPairGeneratorSpec spec =
+ new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1,
+ NOW, NOW_PLUS_10_YEARS, 0);
assertEquals("Context should be the one specified", getContext(), spec.getContext());
@@ -60,6 +61,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase {
.setSerialNumber(SERIAL_1)
.setStartDate(NOW)
.setEndDate(NOW_PLUS_10_YEARS)
+ .setEncryptionRequired()
.build();
assertEquals("Context should be the one specified", getContext(), spec.getContext());
@@ -71,12 +73,14 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase {
assertEquals("startDate should be the one specified", NOW, spec.getStartDate());
assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate());
+
+ assertEquals("encryption flag should be on", KeyStore.FLAG_ENCRYPTED, spec.getFlags());
}
public void testConstructor_NullContext_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(null, TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW,
- NOW_PLUS_10_YEARS);
+ NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when context is null");
} catch (IllegalArgumentException success) {
}
@@ -85,7 +89,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase {
public void testConstructor_NullKeystoreAlias_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(getContext(), null, TEST_DN_1, SERIAL_1, NOW,
- NOW_PLUS_10_YEARS);
+ NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when keystoreAlias is null");
} catch (IllegalArgumentException success) {
}
@@ -94,7 +98,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase {
public void testConstructor_NullSubjectDN_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, null, SERIAL_1, NOW,
- NOW_PLUS_10_YEARS);
+ NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when subjectDN is null");
} catch (IllegalArgumentException success) {
}
@@ -103,7 +107,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase {
public void testConstructor_NullSerial_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, null, NOW,
- NOW_PLUS_10_YEARS);
+ NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when startDate is null");
} catch (IllegalArgumentException success) {
}
@@ -112,7 +116,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase {
public void testConstructor_NullStartDate_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1, null,
- NOW_PLUS_10_YEARS);
+ NOW_PLUS_10_YEARS, 0);
fail("Should throw IllegalArgumentException when startDate is null");
} catch (IllegalArgumentException success) {
}
@@ -121,7 +125,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase {
public void testConstructor_NullEndDate_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW,
- null);
+ null, 0);
fail("Should throw IllegalArgumentException when keystoreAlias is null");
} catch (IllegalArgumentException success) {
}
@@ -130,7 +134,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase {
public void testConstructor_EndBeforeStart_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1,
- NOW_PLUS_10_YEARS, NOW);
+ NOW_PLUS_10_YEARS, NOW, 0);
fail("Should throw IllegalArgumentException when end is before start");
} catch (IllegalArgumentException success) {
}
diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
index 69007c4..c5cf514 100644
--- a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
@@ -27,6 +27,7 @@ import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
import java.util.Date;
import javax.security.auth.x500.X500Principal;
@@ -64,22 +65,34 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
assertFalse(mAndroidKeyStore.isUnlocked());
+ mGenerator = java.security.KeyPairGenerator.getInstance("AndroidKeyStore");
+ }
+
+ private void setupPassword() {
assertTrue(mAndroidKeyStore.password("1111"));
assertTrue(mAndroidKeyStore.isUnlocked());
String[] aliases = mAndroidKeyStore.saw("");
assertNotNull(aliases);
assertEquals(0, aliases.length);
-
- mGenerator = java.security.KeyPairGenerator.getInstance(AndroidKeyPairGenerator.NAME);
}
- public void testKeyPairGenerator_Initialize_Params_Success() throws Exception {
- mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1,
- TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS));
+ public void testKeyPairGenerator_Initialize_Params_Encrypted_Success() throws Exception {
+ setupPassword();
+
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .setEncryptionRequired()
+ .build());
}
- public void testKeyPairGenerator_Initialize_KeySize_Failure() throws Exception {
+ public void testKeyPairGenerator_Initialize_KeySize_Encrypted_Failure() throws Exception {
+ setupPassword();
+
try {
mGenerator.initialize(1024);
fail("KeyPairGenerator should not support setting the key size");
@@ -87,7 +100,10 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
}
}
- public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Failure() throws Exception {
+ public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Encrypted_Failure()
+ throws Exception {
+ setupPassword();
+
try {
mGenerator.initialize(1024, new SecureRandom());
fail("KeyPairGenerator should not support setting the key size");
@@ -95,14 +111,48 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
}
}
- public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Failure() throws Exception {
- mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1,
- TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS), new SecureRandom());
+ public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Encrypted_Failure()
+ throws Exception {
+ setupPassword();
+
+ mGenerator.initialize(
+ new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .setEncryptionRequired()
+ .build(),
+ new SecureRandom());
}
- public void testKeyPairGenerator_GenerateKeyPair_Success() throws Exception {
- mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1,
- TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS));
+ public void testKeyPairGenerator_GenerateKeyPair_Encrypted_Success() throws Exception {
+ setupPassword();
+
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .setEncryptionRequired()
+ .build());
+
+ final KeyPair pair = mGenerator.generateKeyPair();
+ assertNotNull("The KeyPair returned should not be null", pair);
+
+ assertKeyPairCorrect(pair, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS);
+ }
+
+ public void testKeyPairGenerator_GenerateKeyPair_Unencrypted_Success() throws Exception {
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
final KeyPair pair = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair);
@@ -113,8 +163,13 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
public void testKeyPairGenerator_GenerateKeyPair_Replaced_Success() throws Exception {
// Generate the first key
{
- mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1,
- TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS));
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
final KeyPair pair1 = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair1);
assertKeyPairCorrect(pair1, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW,
@@ -123,8 +178,13 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
// Replace the original key
{
- mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_2,
- TEST_DN_2, TEST_SERIAL_2, NOW, NOW_PLUS_10_YEARS));
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_2)
+ .setSubject(TEST_DN_2)
+ .setSerialNumber(TEST_SERIAL_2)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
final KeyPair pair2 = mGenerator.generateKeyPair();
assertNotNull("The KeyPair returned should not be null", pair2);
assertKeyPairCorrect(pair2, TEST_ALIAS_2, TEST_DN_2, TEST_SERIAL_2, NOW,
@@ -132,6 +192,49 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
}
}
+ public void testKeyPairGenerator_GenerateKeyPair_Replaced_UnencryptedToEncrypted_Success()
+ throws Exception {
+ // Generate the first key
+ {
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
+ final KeyPair pair1 = mGenerator.generateKeyPair();
+ assertNotNull("The KeyPair returned should not be null", pair1);
+ assertKeyPairCorrect(pair1, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+
+ // Attempt to replace previous key
+ {
+ mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_2)
+ .setSerialNumber(TEST_SERIAL_2)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .setEncryptionRequired()
+ .build());
+ try {
+ mGenerator.generateKeyPair();
+ fail("Should not be able to generate encrypted key while not initialized");
+ } catch (IllegalStateException expected) {
+ }
+
+ assertTrue(mAndroidKeyStore.password("1111"));
+ assertTrue(mAndroidKeyStore.isUnlocked());
+
+ final KeyPair pair2 = mGenerator.generateKeyPair();
+ assertNotNull("The KeyPair returned should not be null", pair2);
+ assertKeyPairCorrect(pair2, TEST_ALIAS_1, TEST_DN_2, TEST_SERIAL_2, NOW,
+ NOW_PLUS_10_YEARS);
+ }
+ }
+
private void assertKeyPairCorrect(KeyPair pair, String alias, X500Principal dn,
BigInteger serial, Date start, Date end) throws Exception {
final PublicKey pubKey = pair.getPublic();
@@ -163,10 +266,10 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
assertEquals("The Serial should be the one passed into the params", serial,
x509userCert.getSerialNumber());
- assertEquals("The notBefore date should be the one passed into the params", start,
+ assertDateEquals("The notBefore date should be the one passed into the params", start,
x509userCert.getNotBefore());
- assertEquals("The notAfter date should be the one passed into the params", end,
+ assertDateEquals("The notAfter date should be the one passed into the params", end,
x509userCert.getNotAfter());
x509userCert.verify(pubKey);
@@ -178,4 +281,13 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
assertNotNull("The keystore should return the public key for the generated key",
pubKeyBytes);
}
+
+ private static void assertDateEquals(String message, Date date1, Date date2) throws Exception {
+ SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
+
+ String result1 = formatter.format(date1);
+ String result2 = formatter.format(date2);
+
+ assertEquals(message, result1, result2);
+ }
}
diff --git a/keystore/tests/src/android/security/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
index 8928e06..507d41c 100644
--- a/keystore/tests/src/android/security/AndroidKeyStoreTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
@@ -469,12 +469,14 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
assertTrue(mAndroidKeyStore.reset());
assertFalse(mAndroidKeyStore.isUnlocked());
+ mKeyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
+ }
+
+ private void setupPassword() {
assertTrue(mAndroidKeyStore.password("1111"));
assertTrue(mAndroidKeyStore.isUnlocked());
assertEquals(0, mAndroidKeyStore.saw("").length);
-
- mKeyStore = java.security.KeyStore.getInstance(AndroidKeyStore.NAME);
}
private void assertAliases(final String[] expectedAliases) throws KeyStoreException {
@@ -495,21 +497,27 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
expectedAliases.length, count);
}
- public void testKeyStore_Aliases_Success() throws Exception {
+ public void testKeyStore_Aliases_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertAliases(new String[] {});
- assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1));
+ assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertAliases(new String[] { TEST_ALIAS_1 });
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
}
- public void testKeyStore_Aliases_NotInitialized_Failure() throws Exception {
+ public void testKeyStore_Aliases_NotInitialized_Encrypted_Failure() throws Exception {
+ setupPassword();
+
try {
mKeyStore.aliases();
fail("KeyStore should throw exception when not initialized");
@@ -517,16 +525,20 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_ContainsAliases_PrivateAndCA_Success() throws Exception {
+ public void testKeyStore_ContainsAliases_PrivateAndCA_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertAliases(new String[] {});
- assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1));
+ assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should contain generated private key", mKeyStore.containsAlias(TEST_ALIAS_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_2));
@@ -534,34 +546,45 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
mKeyStore.containsAlias(TEST_ALIAS_3));
}
- public void testKeyStore_ContainsAliases_CAOnly_Success() throws Exception {
+ public void testKeyStore_ContainsAliases_CAOnly_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_2));
}
- public void testKeyStore_ContainsAliases_NonExistent_Failure() throws Exception {
+ public void testKeyStore_ContainsAliases_NonExistent_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertFalse("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_1));
}
- public void testKeyStore_DeleteEntry_Success() throws Exception {
+ public void testKeyStore_DeleteEntry_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
// TEST_ALIAS_1
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
// TEST_ALIAS_2
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
// TEST_ALIAS_3
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_3, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_3, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2, TEST_ALIAS_3 });
@@ -578,30 +601,39 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
assertAliases(new String[] { });
}
- public void testKeyStore_DeleteEntry_EmptyStore_Success() throws Exception {
+ public void testKeyStore_DeleteEntry_EmptyStore_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
// Should not throw when a non-existent entry is requested for delete.
mKeyStore.deleteEntry(TEST_ALIAS_1);
}
- public void testKeyStore_DeleteEntry_NonExistent_Success() throws Exception {
+ public void testKeyStore_DeleteEntry_NonExistent_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
// TEST_ALIAS_1
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
// Should not throw when a non-existent entry is requested for delete.
mKeyStore.deleteEntry(TEST_ALIAS_2);
}
- public void testKeyStore_GetCertificate_Single_Success() throws Exception {
+ public void testKeyStore_GetCertificate_Single_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertAliases(new String[] { TEST_ALIAS_1 });
@@ -618,17 +650,22 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
assertEquals("Actual and retrieved certificates should be the same", actual, retrieved);
}
- public void testKeyStore_GetCertificate_NonExist_Failure() throws Exception {
+ public void testKeyStore_GetCertificate_NonExist_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertNull("Certificate should not exist in keystore",
mKeyStore.getCertificate(TEST_ALIAS_1));
}
- public void testKeyStore_GetCertificateAlias_CAEntry_Success() throws Exception {
+ public void testKeyStore_GetCertificateAlias_CAEntry_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
CertificateFactory f = CertificateFactory.getInstance("X.509");
Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
@@ -637,13 +674,18 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
mKeyStore.getCertificateAlias(actual));
}
- public void testKeyStore_GetCertificateAlias_PrivateKeyEntry_Success() throws Exception {
+ public void testKeyStore_GetCertificateAlias_PrivateKeyEntry_Encrypted_Success()
+ throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
CertificateFactory f = CertificateFactory.getInstance("X.509");
Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
@@ -652,18 +694,23 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
mKeyStore.getCertificateAlias(actual));
}
- public void testKeyStore_GetCertificateAlias_CAEntry_WithPrivateKeyUsingCA_Success()
+ public void testKeyStore_GetCertificateAlias_CAEntry_WithPrivateKeyUsingCA_Encrypted_Success()
throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
// Insert TrustedCertificateEntry with CA name
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
// Insert PrivateKeyEntry that uses the same CA
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
CertificateFactory f = CertificateFactory.getInstance("X.509");
Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
@@ -672,7 +719,10 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
mKeyStore.getCertificateAlias(actual));
}
- public void testKeyStore_GetCertificateAlias_NonExist_Empty_Failure() throws Exception {
+ public void testKeyStore_GetCertificateAlias_NonExist_Empty_Encrypted_Failure()
+ throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -682,10 +732,13 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
mKeyStore.getCertificateAlias(actual));
}
- public void testKeyStore_GetCertificateAlias_NonExist_Failure() throws Exception {
+ public void testKeyStore_GetCertificateAlias_NonExist_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
CertificateFactory f = CertificateFactory.getInstance("X.509");
Certificate userCert = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
@@ -694,13 +747,17 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
mKeyStore.getCertificateAlias(userCert));
}
- public void testKeyStore_GetCertificateChain_SingleLength_Success() throws Exception {
+ public void testKeyStore_GetCertificateChain_SingleLength_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate[] expected = new Certificate[2];
@@ -720,20 +777,26 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
mKeyStore.getCertificateChain(TEST_ALIAS_2));
}
- public void testKeyStore_GetCertificateChain_NonExist_Failure() throws Exception {
+ public void testKeyStore_GetCertificateChain_NonExist_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertNull("Stored certificate alias should not be found",
mKeyStore.getCertificateChain(TEST_ALIAS_1));
}
- public void testKeyStore_GetCreationDate_PrivateKeyEntry_Success() throws Exception {
+ public void testKeyStore_GetCreationDate_PrivateKeyEntry_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
Date now = new Date();
Date actual = mKeyStore.getCreationDate(TEST_ALIAS_1);
@@ -745,10 +808,33 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
assertTrue("Time should be close to current time", actual.after(expectedAfter));
}
- public void testKeyStore_GetCreationDate_CAEntry_Success() throws Exception {
+ public void testKeyStore_GetCreationDate_PrivateKeyEntry_Unencrypted_Success() throws Exception {
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+
+ Date now = new Date();
+ Date actual = mKeyStore.getCreationDate(TEST_ALIAS_1);
+
+ Date expectedAfter = new Date(now.getTime() - SLOP_TIME_MILLIS);
+ Date expectedBefore = new Date(now.getTime() + SLOP_TIME_MILLIS);
+
+ assertTrue("Time should be close to current time", actual.before(expectedBefore));
+ assertTrue("Time should be close to current time", actual.after(expectedAfter));
+ }
+
+ public void testKeyStore_GetCreationDate_CAEntry_Encrypted_Success() throws Exception {
+ setupPassword();
+
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
Date now = new Date();
Date actual = mKeyStore.getCreationDate(TEST_ALIAS_1);
@@ -761,13 +847,37 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
assertTrue("Time should be close to current time", actual.after(expectedAfter));
}
- public void testKeyStore_GetEntry_NullParams_Success() throws Exception {
+ public void testKeyStore_GetEntry_NullParams_Encrypted_Success() throws Exception {
+ setupPassword();
+
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+ Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Entry should exist", entry);
+
+ assertTrue("Should be a PrivateKeyEntry", entry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
+
+ assertPrivateKeyEntryEquals(keyEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ public void testKeyStore_GetEntry_NullParams_Unencrypted_Success() throws Exception {
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE));
Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
assertNotNull("Entry should exist", entry);
@@ -801,8 +911,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, PrivateKey expectedKey,
Certificate expectedCert, Collection<Certificate> expectedChain) throws Exception {
- assertEquals("Returned PrivateKey should be what we inserted", expectedKey,
- keyEntry.getPrivateKey());
+ assertEquals("Returned PrivateKey should be what we inserted",
+ ((RSAPrivateKey) expectedKey).getModulus(),
+ ((RSAPrivateKey) keyEntry.getPrivateKey()).getModulus());
assertEquals("Returned Certificate should be what we inserted", expectedCert,
keyEntry.getCertificate());
@@ -823,20 +934,33 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_GetEntry_Nonexistent_NullParams_Failure() throws Exception {
+ public void testKeyStore_GetEntry_Nonexistent_NullParams_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertNull("A non-existent entry should return null",
mKeyStore.getEntry(TEST_ALIAS_1, null));
}
- public void testKeyStore_GetKey_NoPassword_Success() throws Exception {
+ public void testKeyStore_GetEntry_Nonexistent_NullParams_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertNull("A non-existent entry should return null",
+ mKeyStore.getEntry(TEST_ALIAS_1, null));
+ }
+
+ public void testKeyStore_GetKey_NoPassword_Encrypted_Success() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
assertNotNull("Key should exist", key);
@@ -848,89 +972,143 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
KeyFactory keyFact = KeyFactory.getInstance("RSA");
PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
- assertEquals("Inserted key should be same as retrieved key", actualKey, expectedKey);
+ assertEquals("Inserted key should be same as retrieved key",
+ ((RSAPrivateKey) expectedKey).getModulus(), actualKey.getModulus());
}
- public void testKeyStore_GetKey_Certificate_Failure() throws Exception {
+ public void testKeyStore_GetKey_NoPassword_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+
+ Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
+ assertNotNull("Key should exist", key);
+
+ assertTrue("Should be a RSAPrivateKey", key instanceof RSAPrivateKey);
+
+ RSAPrivateKey actualKey = (RSAPrivateKey) key;
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ assertEquals("Inserted key should be same as retrieved key",
+ ((RSAPrivateKey) expectedKey).getModulus(), actualKey.getModulus());
+ }
+
+ public void testKeyStore_GetKey_Certificate_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertNull("Certificate entries should return null", mKeyStore.getKey(TEST_ALIAS_1, null));
}
- public void testKeyStore_GetKey_NonExistent_Failure() throws Exception {
+ public void testKeyStore_GetKey_NonExistent_Encrypted_Failure() throws Exception {
+ setupPassword();
+
mKeyStore.load(null, null);
assertNull("A non-existent entry should return null", mKeyStore.getKey(TEST_ALIAS_1, null));
}
- public void testKeyStore_GetProvider_Success() throws Exception {
+ public void testKeyStore_GetProvider_Encrypted_Success() throws Exception {
+ assertEquals(AndroidKeyStoreProvider.PROVIDER_NAME, mKeyStore.getProvider().getName());
+ setupPassword();
assertEquals(AndroidKeyStoreProvider.PROVIDER_NAME, mKeyStore.getProvider().getName());
}
- public void testKeyStore_GetType_Success() throws Exception {
+ public void testKeyStore_GetType_Encrypted_Success() throws Exception {
+ assertEquals(AndroidKeyStore.NAME, mKeyStore.getType());
+ setupPassword();
assertEquals(AndroidKeyStore.NAME, mKeyStore.getType());
}
- public void testKeyStore_IsCertificateEntry_CA_Success() throws Exception {
+ public void testKeyStore_IsCertificateEntry_CA_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should return true for CA certificate",
mKeyStore.isCertificateEntry(TEST_ALIAS_1));
}
- public void testKeyStore_IsCertificateEntry_PrivateKey_Failure() throws Exception {
+ public void testKeyStore_IsCertificateEntry_PrivateKey_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertFalse("Should return false for PrivateKeyEntry",
mKeyStore.isCertificateEntry(TEST_ALIAS_1));
}
- public void testKeyStore_IsCertificateEntry_NonExist_Failure() throws Exception {
+ public void testKeyStore_IsCertificateEntry_NonExist_Encrypted_Failure() throws Exception {
+ setupPassword();
+ mKeyStore.load(null, null);
+
+ assertFalse("Should return false for non-existent entry",
+ mKeyStore.isCertificateEntry(TEST_ALIAS_1));
+ }
+
+ public void testKeyStore_IsCertificateEntry_NonExist_Unencrypted_Failure() throws Exception {
mKeyStore.load(null, null);
assertFalse("Should return false for non-existent entry",
mKeyStore.isCertificateEntry(TEST_ALIAS_1));
}
- public void testKeyStore_IsKeyEntry_PrivateKey_Success() throws Exception {
+ public void testKeyStore_IsKeyEntry_PrivateKey_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should return true for PrivateKeyEntry", mKeyStore.isKeyEntry(TEST_ALIAS_1));
}
- public void testKeyStore_IsKeyEntry_CA_Failure() throws Exception {
+ public void testKeyStore_IsKeyEntry_CA_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertFalse("Should return false for CA certificate", mKeyStore.isKeyEntry(TEST_ALIAS_1));
}
- public void testKeyStore_IsKeyEntry_NonExist_Failure() throws Exception {
+ public void testKeyStore_IsKeyEntry_NonExist_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertFalse("Should return false for non-existent entry",
mKeyStore.isKeyEntry(TEST_ALIAS_1));
}
- public void testKeyStore_SetCertificate_CA_Success() throws Exception {
+ public void testKeyStore_SetCertificate_CA_Encrypted_Success() throws Exception {
final CertificateFactory f = CertificateFactory.getInstance("X.509");
final Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+ setupPassword();
mKeyStore.load(null, null);
mKeyStore.setCertificateEntry(TEST_ALIAS_1, actual);
@@ -942,10 +1120,12 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
retrieved);
}
- public void testKeyStore_SetCertificate_CAExists_Overwrite_Success() throws Exception {
+ public void testKeyStore_SetCertificate_CAExists_Overwrite_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertAliases(new String[] { TEST_ALIAS_1 });
@@ -958,13 +1138,16 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
assertAliases(new String[] { TEST_ALIAS_1 });
}
- public void testKeyStore_SetCertificate_PrivateKeyExists_Failure() throws Exception {
+ public void testKeyStore_SetCertificate_PrivateKeyExists_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- FAKE_KEY_1));
- assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1));
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertAliases(new String[] { TEST_ALIAS_1 });
@@ -978,7 +1161,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_SetEntry_PrivateKeyEntry_Success() throws Exception {
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
KeyFactory keyFact = KeyFactory.getInstance("RSA");
@@ -1005,8 +1189,63 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
}
- public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_PrivateKeyEntry_Success()
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Unencrypted_Success() throws Exception {
+ mKeyStore.load(null, null);
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+ Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+ assertNotNull("Retrieved entry should exist", actualEntry);
+
+ assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+ actualEntry instanceof PrivateKeyEntry);
+
+ PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+ assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
+ }
+
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Params_Unencrypted_Failure() throws Exception {
+ mKeyStore.load(null, null);
+
+ KeyFactory keyFact = KeyFactory.getInstance("RSA");
+ PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1));
+
+ final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+ final Certificate[] expectedChain = new Certificate[2];
+ expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1));
+ expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1));
+
+ PrivateKeyEntry entry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+ try {
+ mKeyStore.setEntry(TEST_ALIAS_1, entry,
+ new AndroidKeyStoreParameter.Builder(getContext())
+ .setEncryptionRequired()
+ .build());
+ fail("Shouldn't be able to insert encrypted entry when KeyStore uninitialized");
+ } catch (KeyStoreException expected) {
+ }
+
+ assertNull(mKeyStore.getEntry(TEST_ALIAS_1, null));
+ }
+
+ public void
+ testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_PrivateKeyEntry_Encrypted_Success()
throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final KeyFactory keyFact = KeyFactory.getInstance("RSA");
@@ -1060,7 +1299,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_SetEntry_CAEntry_Overwrites_PrivateKeyEntry_Success() throws Exception {
+ public void testKeyStore_SetEntry_CAEntry_Overwrites_PrivateKeyEntry_Encrypted_Success()
+ throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1104,7 +1345,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_CAEntry_Success() throws Exception {
+ public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_CAEntry_Encrypted_Success()
+ throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1148,8 +1391,11 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_ShortPrivateKeyEntry_Success()
+ public
+ void
+ testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_ShortPrivateKeyEntry_Encrypted_Success()
throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1198,7 +1444,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_SetEntry_CAEntry_Overwrites_CAEntry_Success() throws Exception {
+ public void testKeyStore_SetEntry_CAEntry_Overwrites_CAEntry_Encrypted_Success()
+ throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1239,7 +1487,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_SetKeyEntry_ProtectedKey_Failure() throws Exception {
+ public void testKeyStore_SetKeyEntry_ProtectedKey_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1259,7 +1508,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_SetKeyEntry_Success() throws Exception {
+ public void testKeyStore_SetKeyEntry_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1285,7 +1535,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1);
}
- public void testKeyStore_SetKeyEntry_Replaced_Success() throws Exception {
+ public void testKeyStore_SetKeyEntry_Replaced_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
final CertificateFactory f = CertificateFactory.getInstance("X.509");
@@ -1376,13 +1627,15 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
return cert;
}
- public void testKeyStore_SetKeyEntry_ReplacedChain_Success() throws Exception {
+ public void testKeyStore_SetKeyEntry_ReplacedChain_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
// Create key #1
{
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
- assertTrue(mAndroidKeyStore.generate(privateKeyAlias));
+ assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
+ KeyStore.FLAG_ENCRYPTED));
Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
@@ -1394,7 +1647,7 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
TEST_SERIAL_1, TEST_DN_1, NOW, NOW_PLUS_10_YEARS);
assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
- expectedCert.getEncoded()));
+ expectedCert.getEncoded(), KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
@@ -1429,32 +1682,35 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_SetKeyEntry_ReplacedChain_DifferentPrivateKey_Failure()
+ public void testKeyStore_SetKeyEntry_ReplacedChain_DifferentPrivateKey_Encrypted_Failure()
throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
// Create key #1
{
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
- assertTrue(mAndroidKeyStore.generate(privateKeyAlias));
+ assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
+ KeyStore.FLAG_ENCRYPTED));
X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_1,
TEST_SERIAL_1, TEST_DN_1, NOW, NOW_PLUS_10_YEARS);
assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
- cert.getEncoded()));
+ cert.getEncoded(), KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
}
// Create key #2
{
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_2;
- assertTrue(mAndroidKeyStore.generate(privateKeyAlias));
+ assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
+ KeyStore.FLAG_ENCRYPTED));
X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_2,
TEST_SERIAL_2, TEST_DN_2, NOW, NOW_PLUS_10_YEARS);
assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_2,
- cert.getEncoded()));
+ cert.getEncoded(), KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
}
// Replace key #1 with key #2
@@ -1472,20 +1728,64 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_Size_Success() throws Exception {
+ public void testKeyStore_SetKeyEntry_ReplacedChain_UnencryptedToEncrypted_Failure()
+ throws Exception {
+ mKeyStore.load(null, null);
+
+ // Create key #1
+ {
+ final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
+ assertTrue(mAndroidKeyStore.generate(privateKeyAlias,
+ android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE));
+
+ X509Certificate cert =
+ generateCertificate(mAndroidKeyStore, TEST_ALIAS_1, TEST_SERIAL_1, TEST_DN_1,
+ NOW, NOW_PLUS_10_YEARS);
+
+ assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
+ cert.getEncoded(), android.security.KeyStore.UID_SELF,
+ android.security.KeyStore.FLAG_NONE));
+ }
+
+ // Replace with one that requires encryption
+ {
+ Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+
+ try {
+ mKeyStore.setEntry(TEST_ALIAS_1, entry, new AndroidKeyStoreParameter.Builder(
+ getContext()).setEncryptionRequired().build());
+ fail("Should not allow setting of Entry without unlocked keystore");
+ } catch (KeyStoreException success) {
+ }
+
+ assertTrue(mAndroidKeyStore.password("1111"));
+ assertTrue(mAndroidKeyStore.isUnlocked());
+
+ mKeyStore.setEntry(TEST_ALIAS_1, entry,
+ new AndroidKeyStoreParameter.Builder(getContext())
+ .setEncryptionRequired()
+ .build());
+ }
+ }
+
+ public void testKeyStore_Size_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertEquals("The keystore size should match expected", 1, mKeyStore.size());
assertAliases(new String[] { TEST_ALIAS_1 });
- assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1));
+ assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertEquals("The keystore size should match expected", 2, mKeyStore.size());
assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
- assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3));
+ assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3,
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertEquals("The keystore size should match expected", 3, mKeyStore.size());
assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2, TEST_ALIAS_3 });
@@ -1501,7 +1801,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
assertAliases(new String[] { TEST_ALIAS_2 });
}
- public void testKeyStore_Store_LoadStoreParam_Failure() throws Exception {
+ public void testKeyStore_Store_LoadStoreParam_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
try {
@@ -1511,7 +1812,7 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_Load_InputStreamSupplied_Failure() throws Exception {
+ public void testKeyStore_Load_InputStreamSupplied_Encrypted_Failure() throws Exception {
byte[] buf = "FAKE KEYSTORE".getBytes();
ByteArrayInputStream is = new ByteArrayInputStream(buf);
@@ -1522,7 +1823,7 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_Load_PasswordSupplied_Failure() throws Exception {
+ public void testKeyStore_Load_PasswordSupplied_Encrypted_Failure() throws Exception {
try {
mKeyStore.load(null, "password".toCharArray());
fail("Should throw IllegalArgumentException when password is supplied");
@@ -1530,7 +1831,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
}
}
- public void testKeyStore_Store_OutputStream_Failure() throws Exception {
+ public void testKeyStore_Store_OutputStream_Encrypted_Failure() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
OutputStream sink = new ByteArrayOutputStream();
@@ -1549,16 +1851,18 @@ public class AndroidKeyStoreTest extends AndroidTestCase {
private void setupKey() throws Exception {
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
- assertTrue(mAndroidKeyStore.generate(privateKeyAlias));
+ assertTrue(mAndroidKeyStore
+ .generate(privateKeyAlias, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_1, TEST_SERIAL_1,
TEST_DN_1, NOW, NOW_PLUS_10_YEARS);
assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
- cert.getEncoded()));
+ cert.getEncoded(), KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
}
- public void testKeyStore_KeyOperations_Wrap_Success() throws Exception {
+ public void testKeyStore_KeyOperations_Wrap_Encrypted_Success() throws Exception {
+ setupPassword();
mKeyStore.load(null, null);
setupKey();
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index 1de1eaf..815f4ac 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -142,42 +142,51 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertNull(mKeyStore.get(TEST_KEYNAME));
mKeyStore.password(TEST_PASSWD);
assertNull(mKeyStore.get(TEST_KEYNAME));
- assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE));
+ assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF,
+ KeyStore.FLAG_ENCRYPTED));
assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
}
public void testPut() throws Exception {
assertNull(mKeyStore.get(TEST_KEYNAME));
- assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE));
+ assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF,
+ KeyStore.FLAG_ENCRYPTED));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
mKeyStore.password(TEST_PASSWD);
- assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE));
+ assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF,
+ KeyStore.FLAG_ENCRYPTED));
assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
}
public void testPut_grantedUid_Wifi() throws Exception {
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
- assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID));
+ assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID,
+ KeyStore.FLAG_ENCRYPTED));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
mKeyStore.password(TEST_PASSWD);
- assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID));
+ assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID,
+ KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
}
public void testPut_ungrantedUid_Bluetooth() throws Exception {
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
- assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID));
+ assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID,
+ KeyStore.FLAG_ENCRYPTED));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
mKeyStore.password(TEST_PASSWD);
- assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID));
+ assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID,
+ KeyStore.FLAG_ENCRYPTED));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
}
public void testI18n() throws Exception {
- assertFalse(mKeyStore.put(TEST_I18N_KEY, TEST_I18N_VALUE));
+ assertFalse(mKeyStore.put(TEST_I18N_KEY, TEST_I18N_VALUE, KeyStore.UID_SELF,
+ KeyStore.FLAG_ENCRYPTED));
assertFalse(mKeyStore.contains(TEST_I18N_KEY));
mKeyStore.password(TEST_I18N_KEY);
- assertTrue(mKeyStore.put(TEST_I18N_KEY, TEST_I18N_VALUE));
+ assertTrue(mKeyStore.put(TEST_I18N_KEY, TEST_I18N_VALUE, KeyStore.UID_SELF,
+ KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_I18N_KEY));
}
@@ -186,7 +195,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
mKeyStore.password(TEST_PASSWD);
assertFalse(mKeyStore.delete(TEST_KEYNAME));
- assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE));
+ assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF,
+ KeyStore.FLAG_ENCRYPTED));
assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
assertTrue(mKeyStore.delete(TEST_KEYNAME));
assertNull(mKeyStore.get(TEST_KEYNAME));
@@ -197,7 +207,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
mKeyStore.password(TEST_PASSWD);
assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.WIFI_UID));
- assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID));
+ assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID,
+ KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
assertTrue(mKeyStore.delete(TEST_KEYNAME, Process.WIFI_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
@@ -208,7 +219,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
mKeyStore.password(TEST_PASSWD);
assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.BLUETOOTH_UID));
- assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID));
+ assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID,
+ KeyStore.FLAG_ENCRYPTED));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.BLUETOOTH_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
@@ -220,7 +232,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertTrue(mKeyStore.password(TEST_PASSWD));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
- assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE));
+ assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF,
+ KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
}
@@ -230,7 +243,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertTrue(mKeyStore.password(TEST_PASSWD));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
- assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID));
+ assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID,
+ KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
}
@@ -240,7 +254,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertTrue(mKeyStore.password(TEST_PASSWD));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
- assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID));
+ assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID,
+ KeyStore.FLAG_ENCRYPTED));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
}
@@ -250,8 +265,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertEquals(0, emptyResult.length);
mKeyStore.password(TEST_PASSWD);
- mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE);
- mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE);
+ mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
+ mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
String[] results = mKeyStore.saw(TEST_KEYNAME);
assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
@@ -264,8 +279,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertNull(results1);
mKeyStore.password(TEST_PASSWD);
- mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE);
- mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE);
+ mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
+ mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID);
assertNull(results2);
@@ -277,8 +292,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertEquals(0, results1.length);
mKeyStore.password(TEST_PASSWD);
- mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, Process.WIFI_UID);
- mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, Process.WIFI_UID);
+ mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, Process.WIFI_UID, KeyStore.FLAG_ENCRYPTED);
+ mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, Process.WIFI_UID, KeyStore.FLAG_ENCRYPTED);
String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.WIFI_UID);
assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
@@ -292,8 +307,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertEquals(0, results1.length);
mKeyStore.password(TEST_PASSWD);
- mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, Process.VPN_UID);
- mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, Process.VPN_UID);
+ mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, Process.VPN_UID, KeyStore.FLAG_ENCRYPTED);
+ mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, Process.VPN_UID, KeyStore.FLAG_ENCRYPTED);
String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.VPN_UID);
assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
@@ -324,7 +339,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertTrue(mKeyStore.isEmpty());
mKeyStore.password(TEST_PASSWD);
assertTrue(mKeyStore.isEmpty());
- mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE);
+ mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
assertFalse(mKeyStore.isEmpty());
mKeyStore.reset();
assertTrue(mKeyStore.isEmpty());
@@ -332,20 +347,21 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
public void testGenerate_NotInitialized_Fail() throws Exception {
assertFalse("Should fail when keystore is not initialized",
- mKeyStore.generate(TEST_KEYNAME));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
}
public void testGenerate_Locked_Fail() throws Exception {
mKeyStore.password(TEST_PASSWD);
mKeyStore.lock();
- assertFalse("Should fail when keystore is locked", mKeyStore.generate(TEST_KEYNAME));
+ assertFalse("Should fail when keystore is locked",
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
}
public void testGenerate_Success() throws Exception {
assertTrue(mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key when unlocked",
- mKeyStore.generate(TEST_KEYNAME));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
}
@@ -354,7 +370,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertTrue(mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key when unlocked",
- mKeyStore.generate(TEST_KEYNAME, Process.WIFI_UID));
+ mKeyStore.generate(TEST_KEYNAME, Process.WIFI_UID, KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
}
@@ -362,7 +378,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
public void testGenerate_ungrantedUid_Bluetooth_Failure() throws Exception {
assertTrue(mKeyStore.password(TEST_PASSWD));
- assertFalse(mKeyStore.generate(TEST_KEYNAME, Process.BLUETOOTH_UID));
+ assertFalse(mKeyStore.generate(TEST_KEYNAME, Process.BLUETOOTH_UID, KeyStore.FLAG_ENCRYPTED));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
@@ -371,8 +387,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
public void testImport_Success() throws Exception {
assertTrue(mKeyStore.password(TEST_PASSWD));
- assertTrue("Should be able to import key when unlocked",
- mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES));
+ assertTrue("Should be able to import key when unlocked", mKeyStore.importKey(TEST_KEYNAME,
+ PRIVKEY_BYTES, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
}
@@ -380,8 +396,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
public void testImport_grantedUid_Wifi_Success() throws Exception {
assertTrue(mKeyStore.password(TEST_PASSWD));
- assertTrue("Should be able to import key when unlocked",
- mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES, Process.WIFI_UID));
+ assertTrue("Should be able to import key when unlocked", mKeyStore.importKey(TEST_KEYNAME,
+ PRIVKEY_BYTES, Process.WIFI_UID, KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
}
@@ -389,7 +405,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
public void testImport_ungrantedUid_Bluetooth_Failure() throws Exception {
assertTrue(mKeyStore.password(TEST_PASSWD));
- assertFalse(mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES, Process.BLUETOOTH_UID));
+ assertFalse(mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES, Process.BLUETOOTH_UID,
+ KeyStore.FLAG_ENCRYPTED));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
@@ -398,8 +415,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
public void testImport_Failure_BadEncoding() throws Exception {
mKeyStore.password(TEST_PASSWD);
- assertFalse("Invalid DER-encoded key should not be imported",
- mKeyStore.importKey(TEST_KEYNAME, TEST_DATA));
+ assertFalse("Invalid DER-encoded key should not be imported", mKeyStore.importKey(
+ TEST_KEYNAME, TEST_DATA, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
}
@@ -407,7 +424,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
public void testSign_Success() throws Exception {
mKeyStore.password(TEST_PASSWD);
- assertTrue(mKeyStore.generate(TEST_KEYNAME));
+ assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
@@ -417,7 +434,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
public void testVerify_Success() throws Exception {
mKeyStore.password(TEST_PASSWD);
- assertTrue(mKeyStore.generate(TEST_KEYNAME));
+ assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
@@ -444,7 +461,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should be able to grant key to other user",
mKeyStore.grant(TEST_KEYNAME, 0));
@@ -453,8 +470,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
public void testGrant_Imported_Success() throws Exception {
assertTrue("Password should work for keystore", mKeyStore.password(TEST_PASSWD));
- assertTrue("Should be able to import key for testcase",
- mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES));
+ assertTrue("Should be able to import key for testcase", mKeyStore.importKey(TEST_KEYNAME,
+ PRIVKEY_BYTES, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should be able to grant key to other user", mKeyStore.grant(TEST_KEYNAME, 0));
}
@@ -477,7 +494,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should be able to grant key to other user",
mKeyStore.grant(TEST_KEYNAME, 0));
@@ -490,8 +507,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertTrue("Password should work for keystore",
mKeyStore.password(TEST_PASSWD));
- assertTrue("Should be able to import key for testcase",
- mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES));
+ assertTrue("Should be able to import key for testcase", mKeyStore.importKey(TEST_KEYNAME,
+ PRIVKEY_BYTES, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should be able to grant key to other user",
mKeyStore.grant(TEST_KEYNAME, 0));
@@ -510,7 +527,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertFalse("Should not be able to revoke not existent grant",
mKeyStore.ungrant(TEST_KEYNAME, 0));
@@ -521,7 +538,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should be able to grant key to other user",
mKeyStore.grant(TEST_KEYNAME, 0));
@@ -538,7 +555,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME));
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue("Should be able to grant key to other user",
mKeyStore.grant(TEST_KEYNAME, 0));
@@ -558,7 +575,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertFalse(mKeyStore.contains(TEST_KEYNAME));
- assertTrue(mKeyStore.generate(TEST_KEYNAME));
+ assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
@@ -596,7 +613,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertFalse(mKeyStore.contains(TEST_KEYNAME));
- assertTrue(mKeyStore.generate(TEST_KEYNAME));
+ assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
@@ -619,8 +636,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertTrue("Password should work for keystore",
mKeyStore.password(TEST_PASSWD));
- assertTrue("Should be able to import key when unlocked",
- mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES));
+ assertTrue("Should be able to import key when unlocked", mKeyStore.importKey(TEST_KEYNAME,
+ PRIVKEY_BYTES, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
long now = System.currentTimeMillis();
long actual = mKeyStore.getmtime(TEST_KEYNAME);
@@ -650,8 +667,8 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertTrue("Password should work for keystore",
mKeyStore.password(TEST_PASSWD));
- assertTrue("Should be able to import key when unlocked",
- mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES));
+ assertTrue("Should be able to import key when unlocked", mKeyStore.importKey(TEST_KEYNAME,
+ PRIVKEY_BYTES, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
assertEquals("-1 should be returned for non-existent key",
-1L, mKeyStore.getmtime(TEST_KEYNAME2));
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 8bd805c..b08c36b 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -19,6 +19,7 @@
//
#define LOG_TAG "asset"
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
//#define LOG_NDEBUG 0
#include <androidfw/Asset.h>
@@ -32,6 +33,9 @@
#include <utils/threads.h>
#include <utils/Timers.h>
#include <utils/ZipFileRO.h>
+#ifdef HAVE_ANDROID_OS
+#include <cutils/trace.h>
+#endif
#include <assert.h>
#include <dirent.h>
@@ -51,6 +55,14 @@
_rc; })
#endif
+#ifdef HAVE_ANDROID_OS
+#define MY_TRACE_BEGIN(x) ATRACE_BEGIN(x)
+#define MY_TRACE_END() ATRACE_END()
+#else
+#define MY_TRACE_BEGIN(x)
+#define MY_TRACE_END()
+#endif
+
using namespace android;
/*
@@ -638,6 +650,7 @@ const ResTable* AssetManager::getResTable(bool required) const
ResTable* sharedRes = NULL;
bool shared = true;
const asset_path& ap = mAssetPaths.itemAt(i);
+ MY_TRACE_BEGIN(ap.path.string());
Asset* idmap = openIdmapLocked(ap);
ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
if (ap.type != kFileTypeDirectory) {
@@ -702,6 +715,7 @@ const ResTable* AssetManager::getResTable(bool required) const
if (idmap != NULL) {
delete idmap;
}
+ MY_TRACE_END();
}
if (required && !rt) ALOGW("Unable to find resources file resources.arsc");
diff --git a/libs/androidfw/InputDevice.cpp b/libs/androidfw/InputDevice.cpp
index fe891cb..f742052 100644
--- a/libs/androidfw/InputDevice.cpp
+++ b/libs/androidfw/InputDevice.cpp
@@ -172,8 +172,8 @@ void InputDeviceInfo::addSource(uint32_t source) {
}
void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max,
- float flat, float fuzz) {
- MotionRange range = { axis, source, min, max, flat, fuzz };
+ float flat, float fuzz, float resolution) {
+ MotionRange range = { axis, source, min, max, flat, fuzz, resolution };
mMotionRanges.add(range);
}
diff --git a/libs/androidfw/InputTransport.cpp b/libs/androidfw/InputTransport.cpp
index 351c666..498389e 100644
--- a/libs/androidfw/InputTransport.cpp
+++ b/libs/androidfw/InputTransport.cpp
@@ -219,6 +219,11 @@ status_t InputChannel::receiveMessage(InputMessage* msg) {
return OK;
}
+sp<InputChannel> InputChannel::dup() const {
+ int fd = ::dup(getFd());
+ return fd >= 0 ? new InputChannel(getName(), fd) : NULL;
+}
+
// --- InputPublisher ---
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 06e658d..a630ea1 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -30,6 +30,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
PatchCache.cpp \
PathCache.cpp \
PathTessellator.cpp \
+ PixelBuffer.cpp \
Program.cpp \
ProgramCache.cpp \
RenderBufferCache.cpp \
@@ -58,7 +59,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DGL_GLEXT_PROTOTYPES
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
- LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui libRS libRScpp
+ LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libGLESv2 libskia libui libRS libRScpp
LOCAL_MODULE := libhwui
LOCAL_MODULE_TAGS := optional
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 57d1a4f..a381a68 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -70,6 +70,7 @@ void Caches::init() {
mCurrentPositionPointer = this;
mCurrentPositionStride = 0;
mCurrentTexCoordsPointer = this;
+ mCurrentPixelBuffer = 0;
mTexCoordsArrayEnabled = false;
@@ -366,6 +367,28 @@ bool Caches::unbindIndicesBuffer() {
}
///////////////////////////////////////////////////////////////////////////////
+// PBO
+///////////////////////////////////////////////////////////////////////////////
+
+bool Caches::bindPixelBuffer(const GLuint buffer) {
+ if (mCurrentPixelBuffer != buffer) {
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
+ mCurrentPixelBuffer = buffer;
+ return true;
+ }
+ return false;
+}
+
+bool Caches::unbindPixelBuffer() {
+ if (mCurrentPixelBuffer) {
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ mCurrentPixelBuffer = 0;
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
// Meshes and textures
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 63836c1..91b938b 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -176,6 +176,16 @@ public:
bool unbindIndicesBuffer();
/**
+ * Binds the specified buffer as the current GL unpack pixel buffer.
+ */
+ bool bindPixelBuffer(const GLuint buffer);
+
+ /**
+ * Resets the current unpack pixel buffer to 0 (default value.)
+ */
+ bool unbindPixelBuffer();
+
+ /**
* Binds an attrib to the specified float vertex pointer.
* Assumes a stride of gMeshStride and a size of 2.
*/
@@ -307,6 +317,7 @@ private:
GLuint mCurrentBuffer;
GLuint mCurrentIndicesBuffer;
+ GLuint mCurrentPixelBuffer;
void* mCurrentPositionPointer;
GLsizei mCurrentPositionStride;
void* mCurrentTexCoordsPointer;
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 46beb94..790c4f4 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -84,6 +84,9 @@
// Turn on to insert an event marker for each display list op
#define DEBUG_DISPLAY_LIST_OPS_AS_EVENTS 0
+// Turn on to highlight drawing batches and merged batches with different colors
+#define DEBUG_MERGE_BEHAVIOR 0
+
#if DEBUG_INIT
#define INIT_LOGD(...) ALOGD(__VA_ARGS__)
#else
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index fe51bf9..f0084f2 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -21,7 +21,9 @@
#include <utils/Trace.h>
+#include "Caches.h"
#include "Debug.h"
+#include "DeferredDisplayList.h"
#include "DisplayListOp.h"
#include "OpenGLRenderer.h"
@@ -37,15 +39,27 @@ namespace uirenderer {
// Depth of the save stack at the beginning of batch playback at flush time
#define FLUSH_SAVE_STACK_DEPTH 2
+#define DEBUG_COLOR_BARRIER 0x1f000000
+#define DEBUG_COLOR_MERGEDBATCH 0x5f7f7fff
+#define DEBUG_COLOR_MERGEDBATCH_SOLO 0x5f7fff7f
+
/////////////////////////////////////////////////////////////////////////////////
// Operation Batches
/////////////////////////////////////////////////////////////////////////////////
-class DrawOpBatch {
+class Batch {
public:
- DrawOpBatch() { mOps.clear(); }
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) = 0;
+ virtual ~Batch() {}
+};
+
+class DrawBatch : public Batch {
+public:
+ DrawBatch(int batchId, mergeid_t mergeId) : mBatchId(batchId), mMergeId(mergeId) {
+ mOps.clear();
+ }
- virtual ~DrawOpBatch() { mOps.clear(); }
+ virtual ~DrawBatch() { mOps.clear(); }
void add(DrawOp* op) {
// NOTE: ignore empty bounds special case, since we don't merge across those ops
@@ -53,7 +67,7 @@ public:
mOps.add(op);
}
- virtual bool intersects(Rect& rect) {
+ bool intersects(Rect& rect) {
if (!rect.intersects(mBounds)) return false;
for (unsigned int i = 0; i < mOps.size(); i++) {
@@ -70,8 +84,9 @@ public:
return false;
}
- virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
- DEFER_LOGD("replaying draw batch %p", this);
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
+ DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
+ index, this, mOps.size(), mOps[0]->getBatchId(), mOps[0]->getMergeId());
status_t status = DrawGlInfo::kStatusDone;
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
@@ -83,31 +98,127 @@ public:
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
renderer.eventMark(op->name());
#endif
- status |= op->applyDraw(renderer, dirty, 0);
+ status |= op->applyDraw(renderer, dirty);
logBuffer.writeCommand(0, op->name());
+
+#if DEBUG_MERGE_BEHAVIOR
+ Rect& bounds = mOps[i]->state.mBounds;
+ int batchColor = 0x1f000000;
+ if (getBatchId() & 0x1) batchColor |= 0x0000ff;
+ if (getBatchId() & 0x2) batchColor |= 0x00ff00;
+ if (getBatchId() & 0x4) batchColor |= 0xff0000;
+ renderer.drawScreenSpaceColorRect(bounds.left, bounds.top, bounds.right, bounds.bottom,
+ batchColor);
+#endif
}
return status;
}
+ inline int getBatchId() const { return mBatchId; }
+ inline mergeid_t getMergeId() const { return mMergeId; }
inline int count() const { return mOps.size(); }
-private:
+
+protected:
Vector<DrawOp*> mOps;
Rect mBounds;
+private:
+ int mBatchId;
+ mergeid_t mMergeId;
};
-class StateOpBatch : public DrawOpBatch {
+// compare alphas approximately, with a small margin
+#define NEQ_FALPHA(lhs, rhs) \
+ fabs((float)lhs - (float)rhs) > 0.001f
+
+class MergingDrawBatch : public DrawBatch {
public:
- // creates a single operation batch
- StateOpBatch(StateOp* op) : mOp(op) {}
+ MergingDrawBatch(int batchId, mergeid_t mergeId) : DrawBatch(batchId, mergeId) {}
- bool intersects(Rect& rect) {
- // if something checks for intersection, it's trying to go backwards across a state op,
- // something not currently supported - state ops are always barriers
- CRASH();
- return false;
+ /*
+ * Checks if a (mergeable) op can be merged into this batch
+ *
+ * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
+ * important to consider all paint attributes used in the draw calls in deciding both a) if an
+ * op tries to merge at all, and b) if the op
+ *
+ * False positives can lead to information from the paints of subsequent merged operations being
+ * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
+ */
+ bool canMergeWith(DrawOp* op) {
+ if (!op->state.mMatrix.isPureTranslate()) return false;
+
+ bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text ||
+ getBatchId() == DeferredDisplayList::kOpBatch_ColorText;
+
+ // Overlapping other operations is only allowed for text without shadow. For other ops,
+ // multiDraw isn't guaranteed to overdraw correctly
+ if (!isTextBatch || op->state.mDrawModifiers.mHasShadow) {
+ if (intersects(op->state.mBounds)) return false;
+ }
+
+ const DeferredDisplayState& lhs = op->state;
+ const DeferredDisplayState& rhs = mOps[0]->state;
+
+ if (NEQ_FALPHA(lhs.mAlpha, rhs.mAlpha)) return false;
+
+ // if paints are equal, then modifiers + paint attribs don't need to be compared
+ if (op->mPaint == mOps[0]->mPaint) return true;
+
+ if (op->getPaintAlpha() != mOps[0]->getPaintAlpha()) return false;
+
+ /* Draw Modifiers compatibility check
+ *
+ * Shadows are ignored, as only text uses them, and in that case they are drawn
+ * per-DrawTextOp, before the unified text draw. Because of this, it's always safe to merge
+ * text UNLESS a later draw's shadow should overlays a previous draw's text. This is covered
+ * above with the intersection check.
+ *
+ * OverrideLayerAlpha is also ignored, as it's only used for drawing layers, which are never
+ * merged.
+ *
+ * These ignore cases prevent us from simply memcmp'ing the drawModifiers
+ */
+
+ const DrawModifiers& lhsMod = lhs.mDrawModifiers;
+ const DrawModifiers& rhsMod = rhs.mDrawModifiers;
+ if (lhsMod.mShader != rhsMod.mShader) return false;
+ if (lhsMod.mColorFilter != rhsMod.mColorFilter) return false;
+
+ // Draw filter testing expects bit fields to be clear if filter not set.
+ if (lhsMod.mHasDrawFilter != rhsMod.mHasDrawFilter) return false;
+ if (lhsMod.mPaintFilterClearBits != rhsMod.mPaintFilterClearBits) return false;
+ if (lhsMod.mPaintFilterSetBits != rhsMod.mPaintFilterSetBits) return false;
+
+ return true;
}
- virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
+ DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
+ index, this, mOps.size(), getBatchId(), getMergeId());
+ if (mOps.size() == 1) {
+ return DrawBatch::replay(renderer, dirty, false);
+ }
+
+ DrawOp* op = mOps[0];
+ status_t status = op->multiDraw(renderer, dirty, mOps, mBounds);
+ DisplayListLogBuffer& buffer = DisplayListLogBuffer::getInstance();
+ buffer.writeCommand(0, "multiDraw");
+ buffer.writeCommand(1, op->name());
+
+#if DEBUG_MERGE_BEHAVIOR
+ renderer.drawScreenSpaceColorRect(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom,
+ DEBUG_COLOR_MERGEDBATCH);
+#endif
+ return status;
+ }
+};
+
+class StateOpBatch : public Batch {
+public:
+ // creates a single operation batch
+ StateOpBatch(StateOp* op) : mOp(op) {}
+
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
DEFER_LOGD("replaying state op batch %p", this);
renderer.restoreDisplayState(mOp->state);
@@ -123,18 +234,11 @@ private:
const StateOp* mOp;
};
-class RestoreToCountBatch : public DrawOpBatch {
+class RestoreToCountBatch : public Batch {
public:
RestoreToCountBatch(StateOp* op, int restoreCount) : mOp(op), mRestoreCount(restoreCount) {}
- bool intersects(Rect& rect) {
- // if something checks for intersection, it's trying to go backwards across a state op,
- // something not currently supported - state ops are always barriers
- CRASH();
- return false;
- }
-
- virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount);
renderer.restoreDisplayState(mOp->state);
@@ -154,14 +258,30 @@ private:
const int mRestoreCount;
};
+#if DEBUG_MERGE_BEHAVIOR
+class BarrierDebugBatch : public Batch {
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
+ renderer.drawScreenSpaceColorRect(0, 0, 10000, 10000, DEBUG_COLOR_BARRIER);
+ return DrawGlInfo::kStatusDrew;
+ }
+};
+#endif
+
/////////////////////////////////////////////////////////////////////////////////
// DeferredDisplayList
/////////////////////////////////////////////////////////////////////////////////
void DeferredDisplayList::resetBatchingState() {
for (int i = 0; i < kOpBatch_Count; i++) {
- mBatchIndices[i] = -1;
+ mBatchLookup[i] = NULL;
+ mMergingBatches[i].clear();
}
+#if DEBUG_MERGE_BEHAVIOR
+ if (mBatches.size() != 0) {
+ mBatches.add(new BarrierDebugBatch());
+ }
+#endif
+ mEarliestBatchIndex = mBatches.size();
}
void DeferredDisplayList::clear() {
@@ -173,6 +293,7 @@ void DeferredDisplayList::clear() {
}
mBatches.clear();
mSaveStack.clear();
+ mEarliestBatchIndex = 0;
}
/////////////////////////////////////////////////////////////////////////////////
@@ -281,28 +402,35 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
return; // quick rejected
}
- op->onDrawOpDeferred(renderer);
+ int batchId = kOpBatch_None;
+ mergeid_t mergeId = (mergeid_t) -1;
+ bool mergeable = op->onDefer(renderer, &batchId, &mergeId);
+
+ // complex clip has a complex set of expectations on the renderer state - for now, avoid taking
+ // the merge path in those cases
+ mergeable &= !recordingComplexClip();
if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) {
// TODO: elegant way to reuse batches?
- DrawOpBatch* b = new DrawOpBatch();
+ DrawBatch* b = new DrawBatch(batchId, mergeId);
b->add(op);
mBatches.add(b);
return;
}
- // disallowReorder isn't set, so find the latest batch of the new op's type, and try to merge
- // the new op into it
- DrawOpBatch* targetBatch = NULL;
- int batchId = op->getBatchId();
+ // find the latest batch of the new op's type, and try to merge the new op into it
+ DrawBatch* targetBatch = NULL;
+ // insertion point of a new batch, will hopefully be immediately after similar batch
+ // (eventually, should be similar shader)
+ int insertBatchIndex = mBatches.size();
if (!mBatches.isEmpty()) {
if (op->state.mBounds.isEmpty()) {
// don't know the bounds for op, so add to last batch and start from scratch on next op
- mBatches.top()->add(op);
- for (int i = 0; i < kOpBatch_Count; i++) {
- mBatchIndices[i] = -1;
- }
+ DrawBatch* b = new DrawBatch(batchId, mergeId);
+ b->add(op);
+ mBatches.add(b);
+ resetBatchingState();
#if DEBUG_DEFER
DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches");
op->output(2);
@@ -310,13 +438,36 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
return;
}
- if (batchId >= 0 && mBatchIndices[batchId] != -1) {
- int targetIndex = mBatchIndices[batchId];
- targetBatch = mBatches[targetIndex];
+ if (mergeable) {
+ // Try to merge with any existing batch with same mergeId.
+ if (mMergingBatches[batchId].get(mergeId, targetBatch)) {
+ if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op)) {
+ targetBatch = NULL;
+ }
+ }
+ } else {
+ // join with similar, non-merging batch
+ targetBatch = (DrawBatch*)mBatchLookup[batchId];
+ }
+
+ if (targetBatch || mergeable) {
// iterate back toward target to see if anything drawn since should overlap the new op
- for (int i = mBatches.size() - 1; i > targetIndex; i--) {
- DrawOpBatch* overBatch = mBatches[i];
+ // if no target, merging ops still interate to find similar batch to insert after
+ for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
+ DrawBatch* overBatch = (DrawBatch*)mBatches[i];
+
+ if (overBatch == targetBatch) break;
+
+ // TODO: also consider shader shared between batch types
+ if (batchId == overBatch->getBatchId()) {
+ insertBatchIndex = i + 1;
+ if (!targetBatch) break; // found insert position, quit
+ }
+
if (overBatch->intersects(op->state.mBounds)) {
+ // NOTE: it may be possible to optimize for special cases where two operations
+ // of the same batch/paint could swap order, such as with a non-mergeable
+ // (clipped) and a mergeable text operation
targetBatch = NULL;
#if DEBUG_DEFER
DEFER_LOGD("op couldn't join batch %d, was intersected by batch %d",
@@ -328,13 +479,21 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
}
}
}
+
if (!targetBatch) {
- targetBatch = new DrawOpBatch();
- mBatches.add(targetBatch);
- if (batchId >= 0) {
- mBatchIndices[batchId] = mBatches.size() - 1;
+ if (mergeable) {
+ targetBatch = new MergingDrawBatch(batchId, mergeId);
+ mMergingBatches[batchId].put(mergeId, targetBatch);
+ } else {
+ targetBatch = new DrawBatch(batchId, mergeId);
+ mBatchLookup[batchId] = targetBatch;
+ DEFER_LOGD("creating Batch %p, bid %x, at %d",
+ targetBatch, batchId, insertBatchIndex);
}
+
+ mBatches.insertAt(targetBatch, insertBatchIndex);
}
+
targetBatch->add(op);
}
@@ -362,21 +521,21 @@ void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, S
// Replay / flush
/////////////////////////////////////////////////////////////////////////////////
-static status_t replayBatchList(Vector<DrawOpBatch*>& batchList,
+static status_t replayBatchList(const Vector<Batch*>& batchList,
OpenGLRenderer& renderer, Rect& dirty) {
status_t status = DrawGlInfo::kStatusDone;
- int opCount = 0;
for (unsigned int i = 0; i < batchList.size(); i++) {
- status |= batchList[i]->replay(renderer, dirty);
- opCount += batchList[i]->count();
+ status |= batchList[i]->replay(renderer, dirty, i);
}
- DEFER_LOGD("--flushed, drew %d batches (total %d ops)", batchList.size(), opCount);
+ DEFER_LOGD("--flushed, drew %d batches", batchList.size());
return status;
}
status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
ATRACE_NAME("flush drawing commands");
+ Caches::getInstance().fontRenderer->endPrecaching();
+
status_t status = DrawGlInfo::kStatusDone;
if (isEmpty()) return status; // nothing to flush
@@ -397,7 +556,6 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
renderer.setDrawModifiers(restoreDrawModifiers);
DEFER_LOGD("--flush complete, returning %x", status);
-
clear();
return status;
}
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 3e450da..9782c1c 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -22,6 +22,9 @@
#include "Matrix.h"
#include "Rect.h"
+#include "utils/TinyHashMap.h"
+
+class SkBitmap;
namespace android {
namespace uirenderer {
@@ -31,16 +34,21 @@ class DrawOp;
class SaveOp;
class SaveLayerOp;
class StateOp;
-class DrawOpBatch;
class OpenGLRenderer;
+class Batch;
+class DrawBatch;
+class MergingDrawBatch;
+
+typedef void* mergeid_t;
+
class DeferredDisplayList {
public:
DeferredDisplayList() { clear(); }
~DeferredDisplayList() { clear(); }
enum OpBatchId {
- kOpBatch_None = -1, // Don't batch
+ kOpBatch_None = 0, // Don't batch
kOpBatch_Bitmap,
kOpBatch_Patch,
kOpBatch_AlphaVertices,
@@ -52,8 +60,6 @@ public:
kOpBatch_Count, // Add other batch ids before this
};
- void clear();
-
bool isEmpty() { return mBatches.isEmpty(); }
/**
@@ -80,6 +86,8 @@ private:
*/
void resetBatchingState();
+ void clear();
+
void storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op);
void storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op, int newSaveCount);
@@ -96,8 +104,20 @@ private:
Vector<int> mSaveStack;
int mComplexClipStackStart;
- Vector<DrawOpBatch*> mBatches;
- int mBatchIndices[kOpBatch_Count];
+ Vector<Batch*> mBatches;
+
+ // Maps batch ids to the most recent *non-merging* batch of that id
+ Batch* mBatchLookup[kOpBatch_Count];
+
+ // Points to the index after the most recent barrier
+ int mEarliestBatchIndex;
+
+ /**
+ * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
+ * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
+ * collide, which avoids the need to resolve mergeid collisions.
+ */
+ TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
};
}; // namespace uirenderer
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 36c95f9..26abec2 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -485,7 +485,7 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level)
#if DEBUG_DISPLAY_LIST
Rect* clipRect = renderer.getClipRect();
- DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.f, %.0f, %.0f",
+ 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
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index a5dee9f..ad7edb1 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -121,6 +121,7 @@ public:
};
class DrawOp : public DisplayListOp {
+friend class MergingDrawBatch;
public:
DrawOp(SkPaint* paint)
: mPaint(paint), mQuickRejected(false) {}
@@ -145,12 +146,41 @@ public:
return;
}
- replayStruct.mDrawGlStatus |= applyDraw(replayStruct.mRenderer, replayStruct.mDirty, level);
+ replayStruct.mDrawGlStatus |= applyDraw(replayStruct.mRenderer, replayStruct.mDirty);
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) = 0;
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) = 0;
- virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+ /**
+ * Draw multiple instances of an operation, must be overidden for operations that merge
+ *
+ * Currently guarantees certain similarities between ops (see MergingDrawBatch::canMergeWith),
+ * and pure translation transformations. Other guarantees of similarity should be enforced by
+ * reducing which operations are tagged as mergeable.
+ */
+ virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+ const Vector<DrawOp*>& ops, const Rect& bounds) {
+ status_t status = DrawGlInfo::kStatusDone;
+ for (unsigned int i = 0; i < ops.size(); i++) {
+ renderer.restoreDisplayState(ops[i]->state);
+ status |= ops[i]->applyDraw(renderer, dirty);
+ }
+ return status;
+ }
+
+ /*
+ * When this method is invoked the state field is initialized to have the
+ * final rendering state. We can thus use it to process data as it will be
+ * used at draw time.
+ *
+ * Additionally, this method allows subclasses to provide defer-time preferences for batching
+ * and merging.
+ *
+ * Return true if the op can merge with others of its kind (such subclasses should implement
+ * multiDraw)
+ */
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ return false;
}
// returns true if bounds exist
@@ -160,12 +190,11 @@ public:
void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
bool getQuickRejected() { return mQuickRejected; }
- /** Batching disabled by default, turned on for individual ops */
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_None;
+ inline int getPaintAlpha() {
+ return OpenGLRenderer::getAlphaDirect(mPaint);
}
- float strokeWidthOutset() {
+ inline float strokeWidthOutset() {
float width = mPaint->getStrokeWidth();
if (width == 0) return 0.5f; // account for hairline
return width * 0.5f;
@@ -207,6 +236,14 @@ public:
return true;
}
+ bool mergeAllowed() {
+ // checks that we're unclipped, and srcover
+ const Rect& opBounds = state.mBounds;
+ return fabs(opBounds.getWidth() - mLocalBounds.getWidth()) < 0.1 &&
+ fabs(opBounds.getHeight() - mLocalBounds.getHeight()) < 0.1 &&
+ (OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode);
+ }
+
protected:
Rect mLocalBounds; // displayed area in LOCAL coord. doesn't incorporate stroke, so check paint
};
@@ -686,20 +723,58 @@ public:
paint),
mBitmap(bitmap) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mLocalBounds.left, mLocalBounds.top,
getPaint(renderer));
}
+#define SET_TEXTURE(ptr, posRect, offsetRect, texCoordsRect, xDim, yDim) \
+ TextureVertex::set(ptr++, posRect.xDim - offsetRect.left, posRect.yDim - offsetRect.top, \
+ texCoordsRect.xDim, texCoordsRect.yDim)
+
+ virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+ const Vector<DrawOp*>& ops, const Rect& bounds) {
+ renderer.restoreDisplayState(state, true); // restore all but the clip
+ renderer.setFullScreenClip(); // ensure merged ops aren't clipped
+ TextureVertex vertices[6 * ops.size()];
+ TextureVertex* vertex = &vertices[0];
+
+ // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op, and allowing
+ // them to be merged in getBatchId()
+ const Rect texCoords(0, 0, 1, 1);
+
+ const float width = mBitmap->width();
+ const float height = mBitmap->height();
+ for (unsigned int i = 0; i < ops.size(); i++) {
+ const Rect& opBounds = ops[i]->state.mBounds;
+ SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, top);
+ SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
+ SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
+
+ SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
+ SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
+ SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom);
+ }
+
+ return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0], bounds, mPaint);
+ }
+
virtual void output(int level, uint32_t logFlags) {
OP_LOG("Draw bitmap %p at %f %f", mBitmap, mLocalBounds.left, mLocalBounds.top);
}
virtual const char* name() { return "DrawBitmap"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Bitmap;
+
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Bitmap;
+ *mergeId = (mergeid_t)mBitmap;
+
+ // don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
+ // MergingDrawBatch::canMergeWith
+ return mergeAllowed() && (mBitmap->getConfig() != SkBitmap::kA8_Config);
}
+ const SkBitmap* bitmap() { return mBitmap; }
protected:
SkBitmap* mBitmap;
};
@@ -713,7 +788,7 @@ public:
transform.mapRect(mLocalBounds);
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mMatrix, getPaint(renderer));
}
@@ -721,9 +796,11 @@ public:
OP_LOG("Draw bitmap %p matrix " MATRIX_STRING, mBitmap, MATRIX_ARGS(mMatrix));
}
- virtual const char* name() { return "DrawBitmap"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Bitmap;
+ virtual const char* name() { return "DrawBitmapMatrix"; }
+
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Bitmap;
+ return false;
}
private:
@@ -738,7 +815,7 @@ public:
: DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint),
mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mSrc.left, mSrc.top, mSrc.right, mSrc.bottom,
mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom,
getPaint(renderer));
@@ -750,8 +827,10 @@ public:
}
virtual const char* name() { return "DrawBitmapRect"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Bitmap;
+
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Bitmap;
+ return false;
}
private:
@@ -764,7 +843,7 @@ public:
DrawBitmapDataOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
: DrawBitmapOp(bitmap, left, top, paint) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmapData(mBitmap, mLocalBounds.left,
mLocalBounds.top, getPaint(renderer));
}
@@ -774,8 +853,10 @@ public:
}
virtual const char* name() { return "DrawBitmapData"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Bitmap;
+
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Bitmap;
+ return false;
}
};
@@ -787,7 +868,7 @@ public:
mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
mVertices(vertices), mColors(colors) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmapMesh(mBitmap, mMeshWidth, mMeshHeight,
mVertices, mColors, getPaint(renderer));
}
@@ -797,8 +878,10 @@ public:
}
virtual const char* name() { return "DrawBitmapMesh"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Bitmap;
+
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Bitmap;
+ return false;
}
private:
@@ -820,7 +903,7 @@ public:
mColors(colors), mxDivsCount(width), myDivsCount(height),
mNumColors(numColors), mAlpha(alpha), mMode(mode) {};
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
// NOTE: not calling the virtual method, which takes a paint
return renderer.drawPatch(mBitmap, mxDivs, myDivs, mColors,
mxDivsCount, myDivsCount, mNumColors,
@@ -833,8 +916,11 @@ public:
}
virtual const char* name() { return "DrawPatch"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Patch;
+
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Patch;
+ *mergeId = (mergeid_t)mBitmap;
+ return true;
}
private:
@@ -854,7 +940,7 @@ public:
DrawColorOp(int color, SkXfermode::Mode mode)
: DrawOp(0), mColor(color), mMode(mode) {};
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawColor(mColor, mMode);
}
@@ -882,13 +968,15 @@ public:
return true;
}
- virtual DeferredDisplayList::OpBatchId getBatchId() {
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
if (mPaint->getPathEffect()) {
- return DeferredDisplayList::kOpBatch_AlphaMaskTexture;
+ *batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
+ } else {
+ *batchId = mPaint->isAntiAlias() ?
+ DeferredDisplayList::kOpBatch_AlphaVertices :
+ DeferredDisplayList::kOpBatch_Vertices;
}
- return mPaint->isAntiAlias() ?
- DeferredDisplayList::kOpBatch_AlphaVertices :
- DeferredDisplayList::kOpBatch_Vertices;
+ return false;
}
};
@@ -897,7 +985,7 @@ public:
DrawRectOp(float left, float top, float right, float bottom, SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawRect(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
}
@@ -915,7 +1003,7 @@ public:
: DrawBoundedOp(rects, count, paint),
mRects(rects), mCount(count) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawRects(mRects, mCount, getPaint(renderer));
}
@@ -925,8 +1013,9 @@ public:
virtual const char* name() { return "DrawRects"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Vertices;
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Vertices;
+ return false;
}
private:
@@ -940,7 +1029,7 @@ public:
float rx, float ry, SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawRoundRect(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, getPaint(renderer));
}
@@ -962,7 +1051,7 @@ public:
: DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint),
mX(x), mY(y), mRadius(radius) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawCircle(mX, mY, mRadius, getPaint(renderer));
}
@@ -983,7 +1072,7 @@ public:
DrawOvalOp(float left, float top, float right, float bottom, SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawOval(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
}
@@ -1002,7 +1091,7 @@ public:
: DrawStrokableOp(left, top, right, bottom, paint),
mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawArc(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom,
mStartAngle, mSweepAngle, mUseCenter, getPaint(renderer));
@@ -1033,13 +1122,16 @@ public:
mLocalBounds.set(left, top, left + width, top + height);
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawPath(mPath, getPaint(renderer));
}
- virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
SkPaint* paint = getPaint(renderer);
renderer.getCaches().pathCache.precache(mPath, paint);
+
+ *batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
+ return false;
}
virtual void output(int level, uint32_t logFlags) {
@@ -1048,9 +1140,6 @@ public:
virtual const char* name() { return "DrawPath"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_AlphaMaskTexture;
- }
private:
SkPath* mPath;
};
@@ -1063,7 +1152,7 @@ public:
mLocalBounds.outset(strokeWidthOutset());
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawLines(mPoints, mCount, getPaint(renderer));
}
@@ -1073,10 +1162,11 @@ public:
virtual const char* name() { return "DrawLines"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return mPaint->isAntiAlias() ?
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = mPaint->isAntiAlias() ?
DeferredDisplayList::kOpBatch_AlphaVertices :
DeferredDisplayList::kOpBatch_Vertices;
+ return false;
}
protected:
@@ -1089,7 +1179,7 @@ public:
DrawPointsOp(float* points, int count, SkPaint* paint)
: DrawLinesOp(points, count, paint) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawPoints(mPoints, mCount, getPaint(renderer));
}
@@ -1109,17 +1199,18 @@ public:
OP_LOG("Draw some text, %d bytes", mBytesCount);
}
- virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
SkPaint* paint = getPaint(renderer);
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
fontRenderer.precache(paint, mText, mCount, mat4::identity());
- }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return mPaint->getColor() == 0xff000000 ?
+ *batchId = mPaint->getColor() == 0xff000000 ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
+
+ return false;
}
+
protected:
const char* mText;
int mBytesCount;
@@ -1135,7 +1226,7 @@ public:
/* TODO: inherit from DrawBounded and init mLocalBounds */
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawTextOnPath(mText, mBytesCount, mCount, mPath,
mHOffset, mVOffset, getPaint(renderer));
}
@@ -1156,7 +1247,7 @@ public:
/* TODO: inherit from DrawBounded and init mLocalBounds */
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawPosText(mText, mBytesCount, mCount, mPositions, getPaint(renderer));
}
@@ -1189,12 +1280,7 @@ public:
memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float));
}
- /*
- * When this method is invoked the state field is initialized to have the
- * final rendering state. We can thus use it to process data as it will be
- * used at draw time.
- */
- virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
SkPaint* paint = getPaint(renderer);
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
const mat4& transform = renderer.findBestFontTransform(state.mMatrix);
@@ -1202,25 +1288,44 @@ public:
fontRenderer.precache(paint, mText, mCount, transform);
mPrecacheTransform = transform;
}
+ *batchId = mPaint->getColor() == 0xff000000 ?
+ DeferredDisplayList::kOpBatch_Text :
+ DeferredDisplayList::kOpBatch_ColorText;
+
+ *mergeId = (mergeid_t)mPaint->getColor();
+
+ // don't merge decorated text - the decorations won't draw in order
+ bool noDecorations = !(mPaint->getFlags() & (SkPaint::kUnderlineText_Flag |
+ SkPaint::kStrikeThruText_Flag));
+ return mergeAllowed() && noDecorations;
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
mPositions, getPaint(renderer), mLength);
}
+ virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+ const Vector<DrawOp*>& ops, const Rect& bounds) {
+ status_t status = DrawGlInfo::kStatusDone;
+ renderer.setFullScreenClip(); // ensure merged ops aren't clipped
+ for (unsigned int i = 0; i < ops.size(); i++) {
+ DrawOpMode drawOpMode = (i == ops.size() - 1) ? kDrawOpMode_Flush : kDrawOpMode_Defer;
+ renderer.restoreDisplayState(ops[i]->state, true); // restore all but the clip
+
+ DrawTextOp& op = *((DrawTextOp*)ops[i]);
+ status |= renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY,
+ op.mPositions, op.getPaint(renderer), op.mLength, drawOpMode);
+ }
+ return status;
+ }
+
virtual void output(int level, uint32_t logFlags) {
OP_LOG("Draw Text of count %d, bytes %d", mCount, mBytesCount);
}
virtual const char* name() { return "DrawText"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return mPaint->getColor() == 0xff000000 ?
- DeferredDisplayList::kOpBatch_Text :
- DeferredDisplayList::kOpBatch_ColorText;
- }
-
private:
const char* mText;
int mBytesCount;
@@ -1241,7 +1346,7 @@ public:
DrawFunctorOp(Functor* functor)
: DrawOp(0), mFunctor(functor) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
renderer.startMark("GL functor");
status_t ret = renderer.callDrawGLFunction(mFunctor, dirty);
renderer.endMark();
@@ -1269,14 +1374,14 @@ public:
mDisplayList->defer(deferStruct, level + 1);
}
}
-virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) {
+ virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) {
if (mDisplayList && mDisplayList->isRenderable()) {
mDisplayList->replay(replayStruct, level + 1);
}
}
// NOT USED since replay() is overridden
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return DrawGlInfo::kStatusDone;
}
@@ -1299,7 +1404,7 @@ public:
DrawLayerOp(Layer* layer, float x, float y)
: DrawOp(0), mLayer(layer), mX(x), mY(y) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawLayer(mLayer, mX, mY);
}
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 0b8f7e6..876c38a 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -276,6 +276,15 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float
bitmap = refBitmap(bitmap);
paint = refPaint(paint);
+ if (srcLeft == 0 && srcTop == 0 &&
+ srcRight == bitmap->width() && srcBottom == bitmap->height() &&
+ (srcBottom - srcTop == dstBottom - dstTop) &&
+ (srcRight - srcLeft == dstRight - dstLeft)) {
+ // transform simple rect to rect drawing case into position bitmap ops, since they merge
+ addDrawOp(new (alloc()) DrawBitmapOp(bitmap, dstLeft, dstTop, paint));
+ return DrawGlInfo::kStatusDone;
+ }
+
addDrawOp(new (alloc()) DrawBitmapRectOp(bitmap,
srcLeft, srcTop, srcRight, srcBottom,
dstLeft, dstTop, dstRight, dstBottom, paint));
@@ -413,7 +422,9 @@ status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int
}
status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, const float* positions, SkPaint* paint, float length) {
+ float x, float y, const float* positions, SkPaint* paint,
+ float length, DrawOpMode drawOpMode) {
+
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
if (length < 0.0f) length = paint->measureText(text, bytesCount);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 19f7eb6..75abad6 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -121,8 +121,9 @@ public:
float hOffset, float vOffset, SkPaint* paint);
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
- virtual status_t drawText(const char* text, int bytesCount, int count,
- float x, float y, const float* positions, SkPaint* paint, float length);
+ virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
+ const float* positions, SkPaint* paint, float length, DrawOpMode drawOpMode);
+
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
virtual void resetShader();
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index e80b325..19b3849 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -21,38 +21,50 @@ namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Must be a power of two
-#define DITHER_KERNEL_SIZE 4
-
-///////////////////////////////////////////////////////////////////////////////
// Lifecycle
///////////////////////////////////////////////////////////////////////////////
void Dither::bindDitherTexture() {
if (!mInitialized) {
- const uint8_t pattern[] = {
- 0, 8, 2, 10,
- 12, 4, 14, 6,
- 3, 11, 1, 9,
- 15, 7, 13, 5
- };
+ bool useFloatTexture = Extensions::getInstance().getMajorGlVersion() >= 3;
glGenTextures(1, &mDitherTexture);
glBindTexture(GL_TEXTURE_2D, mDitherTexture);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, &pattern);
+ if (useFloatTexture) {
+ // We use a R16F texture, let's remap the alpha channel to the
+ // red channel to avoid changing the shader sampling code on GL ES 3.0+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
+
+ float dither = 1.0f / (255.0f * DITHER_KERNEL_SIZE * DITHER_KERNEL_SIZE);
+ const GLfloat pattern[] = {
+ 0 * dither, 8 * dither, 2 * dither, 10 * dither,
+ 12 * dither, 4 * dither, 14 * dither, 6 * dither,
+ 3 * dither, 11 * dither, 1 * dither, 9 * dither,
+ 15 * dither, 7 * dither, 13 * dither, 5 * dither
+ };
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, sizeof(GLfloat));
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
+ GL_RED, GL_FLOAT, &pattern);
+ } else {
+ const uint8_t pattern[] = {
+ 0, 8, 2, 10,
+ 12, 4, 14, 6,
+ 3, 11, 1, 9,
+ 15, 7, 13, 5
+ };
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, &pattern);
+ }
mInitialized = true;
} else {
@@ -63,6 +75,7 @@ void Dither::bindDitherTexture() {
void Dither::clear() {
if (mInitialized) {
glDeleteTextures(1, &mDitherTexture);
+ mInitialized = false;
}
}
@@ -76,10 +89,7 @@ void Dither::setupProgram(Program* program, GLuint* textureUnit) {
bindDitherTexture();
- float ditherSize = 1.0f / DITHER_KERNEL_SIZE;
glUniform1i(program->getUniform("ditherSampler"), textureSlot);
- glUniform1f(program->getUniform("ditherSize"), ditherSize);
- glUniform1f(program->getUniform("ditherSizeSquared"), ditherSize * ditherSize);
}
}; // namespace uirenderer
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
index 34cf9bf..4d1f921 100644
--- a/libs/hwui/Dither.h
+++ b/libs/hwui/Dither.h
@@ -17,13 +17,23 @@
#ifndef ANDROID_HWUI_DITHER_H
#define ANDROID_HWUI_DITHER_H
-#include <GLES2/gl2.h>
+#include <GLES3/gl3.h>
#include "Program.h"
namespace android {
namespace uirenderer {
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Must be a power of two
+#define DITHER_KERNEL_SIZE 4
+// These must not use the .0f notation as they are used from GLSL
+#define DITHER_KERNEL_SIZE_INV (1.0 / 4.0)
+#define DITHER_KERNEL_SIZE_INV_SQUARE (1.0 / 16.0)
+
/**
* Handles dithering for programs.
*/
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 26c7e5d..543cfa2 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -31,7 +31,9 @@
#include "Caches.h"
#include "Debug.h"
+#include "Extensions.h"
#include "FontRenderer.h"
+#include "PixelBuffer.h"
#include "Rect.h"
namespace android {
@@ -55,7 +57,6 @@ FontRenderer::FontRenderer() :
mGammaTable = NULL;
mInitialized = false;
- mMaxNumberOfQuads = 1024;
mCurrentCacheTexture = NULL;
@@ -132,26 +133,13 @@ void FontRenderer::flushAllAndInvalidate() {
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
mCacheTextures[i]->init();
}
-
-#if DEBUG_FONT_RENDERER
- uint16_t totalGlyphs = 0;
- for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
- totalGlyphs += mCacheTextures[i]->getGlyphCount();
- // Erase caches, just as a debugging facility
- if (mCacheTextures[i]->getTexture()) {
- memset(mCacheTextures[i]->getTexture(), 0,
- mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
- }
- }
- ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
-#endif
}
void FontRenderer::flushLargeCaches() {
// Start from 1; don't deallocate smallest/default texture
for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture->getTexture()) {
+ if (cacheTexture->getPixelBuffer()) {
cacheTexture->init();
LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
while (it.next()) {
@@ -225,7 +213,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
uint32_t cacheWidth = cacheTexture->getWidth();
- if (!cacheTexture->getTexture()) {
+ if (!cacheTexture->getPixelBuffer()) {
Caches::getInstance().activeTexture(0);
// Large-glyph texture memory is allocated only as needed
cacheTexture->allocateTexture();
@@ -238,7 +226,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
// or anti-aliased (8 bits per pixel)
SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
- uint8_t* cacheBuffer = cacheTexture->getTexture();
+ uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
// Copy the glyph image, taking the mask format into account
@@ -304,7 +292,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
}
CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
- CacheTexture* cacheTexture = new CacheTexture(width, height, mMaxNumberOfQuads);
+ CacheTexture* cacheTexture = new CacheTexture(width, height, gMaxNumberOfQuads);
if (allocate) {
Caches::getInstance().activeTexture(0);
@@ -331,12 +319,12 @@ void FontRenderer::initTextTexture() {
// Avoid having to reallocate memory and render quad by quad
void FontRenderer::initVertexArrayBuffers() {
- uint32_t numIndices = mMaxNumberOfQuads * 6;
+ uint32_t numIndices = gMaxNumberOfQuads * 6;
uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
// Four verts, two triangles , six indices per quad
- for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
+ for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
int i6 = i * 6;
int i4 = i * 4;
@@ -375,34 +363,40 @@ void FontRenderer::checkTextureUpdate() {
Caches& caches = Caches::getInstance();
GLuint lastTextureId = 0;
+
+ bool resetPixelStore = false;
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
// Iterate over all the cache textures and see which ones need to be updated
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
- // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
- // of data. So expand the dirty rect to the encompassing horizontal stripe.
- const Rect* dirtyRect = cacheTexture->getDirtyRect();
- uint32_t x = 0;
- uint32_t y = dirtyRect->top;
- uint32_t width = cacheTexture->getWidth();
- uint32_t height = dirtyRect->getHeight();
- void* textureData = cacheTexture->getTexture() + y * width;
-
+ if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
if (cacheTexture->getTextureId() != lastTextureId) {
lastTextureId = cacheTexture->getTextureId();
caches.activeTexture(0);
glBindTexture(GL_TEXTURE_2D, lastTextureId);
}
+
+ if (cacheTexture->upload()) {
+ resetPixelStore = true;
+ }
+
#if DEBUG_FONT_RENDERER
ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
i, x, y, width, height);
#endif
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
- GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
- cacheTexture->setDirty(false);
}
}
+ // Unbind any PBO we might have used to update textures
+ caches.unbindPixelBuffer();
+
+ // Reset to default unpack row length to avoid affecting texture
+ // uploads in other parts of the renderer
+ if (resetPixelStore) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ }
+
mUploadTexture = false;
}
@@ -512,13 +506,14 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
checkInit();
+ DropShadow image;
+ image.width = 0;
+ image.height = 0;
+ image.image = NULL;
+ image.penX = 0;
+ image.penY = 0;
+
if (!mCurrentFont) {
- DropShadow image;
- image.width = 0;
- image.height = 0;
- image.image = NULL;
- image.penX = 0;
- image.penY = 0;
return image;
}
@@ -532,6 +527,11 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
+ uint32_t maxSize = Caches::getInstance().maxTextureSize;
+ if (paddedWidth > maxSize || paddedHeight > maxSize) {
+ return image;
+ }
+
// Align buffers for renderscript usage
if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
@@ -551,10 +551,12 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
+ // Unbind any PBO we might have used
+ Caches::getInstance().unbindPixelBuffer();
+
blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
}
- DropShadow image;
image.width = paddedWidth;
image.height = paddedHeight;
image.image = dataBuffer;
@@ -585,9 +587,13 @@ void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, con
font->precache(paint, text, numGlyphs);
}
+void FontRenderer::endPrecaching() {
+ checkTextureUpdate();
+}
+
bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
- const float* positions, Rect* bounds, Functor* functor) {
+ const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
if (!mCurrentFont) {
ALOGE("No font set");
return false;
@@ -595,7 +601,10 @@ bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *t
initRender(clip, bounds, functor);
mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
- finishRender();
+
+ if (forceFinish) {
+ finishRender();
+ }
return mDrawn;
}
@@ -663,5 +672,16 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int
*image = outImage;
}
+uint32_t FontRenderer::getCacheSize() const {
+ uint32_t size = 0;
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ CacheTexture* cacheTexture = mCacheTextures[i];
+ if (cacheTexture && cacheTexture->getPixelBuffer()) {
+ size += cacheTexture->getPixelBuffer()->getSize();
+ }
+ }
+ return size;
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 1da3b6c..307a1d9 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -61,11 +61,13 @@ public:
void setFont(SkPaint* paint, const mat4& matrix);
void precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix);
+ void endPrecaching();
// bounds is an out parameter
bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds,
- Functor* functor);
+ Functor* functor, bool forceFinish = true);
+
// bounds is an out parameter
bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds);
@@ -95,20 +97,13 @@ public:
mLinearFiltering = linearFiltering;
}
- uint32_t getCacheSize() const {
- uint32_t size = 0;
- for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
- CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture && cacheTexture->getTexture()) {
- size += cacheTexture->getWidth() * cacheTexture->getHeight();
- }
- }
- return size;
- }
+ uint32_t getCacheSize() const;
private:
friend class Font;
+ static const uint32_t gMaxNumberOfQuads = 2048;
+
const uint8_t* mGammaTable;
void allocateTextureMemory(CacheTexture* cacheTexture);
@@ -162,7 +157,6 @@ private:
bool mUploadTexture;
- uint32_t mMaxNumberOfQuads;
uint32_t mIndexBufferID;
Functor* mFunctor;
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
index bd0a4b3..06d2aad 100644
--- a/libs/hwui/GammaFontRenderer.cpp
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -129,6 +129,12 @@ void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description,
}
}
+void ShaderGammaFontRenderer::endPrecaching() {
+ if (mRenderer) {
+ mRenderer->endPrecaching();
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Lookup-based renderer
///////////////////////////////////////////////////////////////////////////////
@@ -146,6 +152,12 @@ LookupGammaFontRenderer::LookupGammaFontRenderer(): GammaFontRenderer() {
mRenderer = NULL;
}
+void LookupGammaFontRenderer::endPrecaching() {
+ if (mRenderer) {
+ mRenderer->endPrecaching();
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Lookup-based renderer, using 3 different correction tables
///////////////////////////////////////////////////////////////////////////////
@@ -177,6 +189,14 @@ Lookup3GammaFontRenderer::~Lookup3GammaFontRenderer() {
}
}
+void Lookup3GammaFontRenderer::endPrecaching() {
+ for (int i = 0; i < kGammaCount; i++) {
+ if (mRenderers[i]) {
+ mRenderers[i]->endPrecaching();
+ }
+ }
+}
+
void Lookup3GammaFontRenderer::clear() {
for (int i = 0; i < kGammaCount; i++) {
delete mRenderers[i];
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index 5c1860e..bbfa66d 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -40,6 +40,8 @@ public:
virtual void describe(ProgramDescription& description, const SkPaint* paint) const = 0;
virtual void setupProgram(ProgramDescription& description, Program* program) const = 0;
+ virtual void endPrecaching() = 0;
+
static GammaFontRenderer* createRenderer();
protected:
@@ -86,6 +88,8 @@ public:
void describe(ProgramDescription& description, const SkPaint* paint) const;
void setupProgram(ProgramDescription& description, Program* program) const;
+ void endPrecaching();
+
private:
ShaderGammaFontRenderer(bool multiGamma);
@@ -134,6 +138,8 @@ public:
void setupProgram(ProgramDescription& description, Program* program) const {
}
+ void endPrecaching();
+
private:
LookupGammaFontRenderer();
@@ -171,6 +177,8 @@ public:
void setupProgram(ProgramDescription& description, Program* program) const {
}
+ void endPrecaching();
+
private:
Lookup3GammaFontRenderer();
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index d681609..507ed95 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -27,13 +27,6 @@ namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#define GRADIENT_TEXTURE_HEIGHT 2
-#define GRADIENT_BYTES_PER_PIXEL 4
-
-///////////////////////////////////////////////////////////////////////////////
// Functions
///////////////////////////////////////////////////////////////////////////////
@@ -83,6 +76,10 @@ GradientCache::GradientCache():
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
mCache.setOnEntryRemovedListener(this);
+
+ const Extensions& extensions = Extensions::getInstance();
+ mUseFloatTexture = extensions.getMajorGlVersion() >= 3;
+ mHasNpot = extensions.hasNPot();
}
GradientCache::GradientCache(uint32_t maxByteSize):
@@ -120,7 +117,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) {
void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
if (texture) {
- const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
+ const uint32_t size = texture->width * texture->height * bytesPerPixel();
mSize -= size;
glDeleteTextures(1, &texture->id);
@@ -151,8 +148,11 @@ void GradientCache::getGradientInfo(const uint32_t* colors, const int count,
GradientInfo& info) {
uint32_t width = 256 * (count - 1);
- if (!Extensions::getInstance().hasNPot()) {
- width = 1 << (31 - __builtin_clz(width));
+ // If the npot extension is not supported we cannot use non-clamp
+ // wrap modes. We therefore find the nearest largest power of 2
+ // unless width is already a power of 2
+ if (!mHasNpot && (width & (width - 1)) != 0) {
+ width = 1 << (32 - __builtin_clz(width));
}
bool hasAlpha = false;
@@ -175,12 +175,12 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
Texture* texture = new Texture;
texture->width = info.width;
- texture->height = GRADIENT_TEXTURE_HEIGHT;
+ texture->height = 2;
texture->blend = info.hasAlpha;
texture->generation = 1;
// Asume the cache is always big enough
- const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
+ const uint32_t size = texture->width * texture->height * bytesPerPixel();
while (getSize() + size > mMaxSize) {
mCache.removeOldest();
}
@@ -193,69 +193,110 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
return texture;
}
+size_t GradientCache::bytesPerPixel() const {
+ // We use 4 channels (RGBA)
+ return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t));
+}
+
+void GradientCache::splitToBytes(uint32_t inColor, GradientColor& outColor) const {
+ outColor.r = (inColor >> 16) & 0xff;
+ outColor.g = (inColor >> 8) & 0xff;
+ outColor.b = (inColor >> 0) & 0xff;
+ outColor.a = (inColor >> 24) & 0xff;
+}
+
+void GradientCache::splitToFloats(uint32_t inColor, GradientColor& outColor) const {
+ outColor.r = ((inColor >> 16) & 0xff) / 255.0f;
+ outColor.g = ((inColor >> 8) & 0xff) / 255.0f;
+ outColor.b = ((inColor >> 0) & 0xff) / 255.0f;
+ outColor.a = ((inColor >> 24) & 0xff) / 255.0f;
+}
+
+void GradientCache::mixBytes(GradientColor& start, GradientColor& end, float amount,
+ uint8_t*& dst) const {
+ float oppAmount = 1.0f - amount;
+ const float alpha = start.a * oppAmount + end.a * amount;
+ const float a = alpha / 255.0f;
+
+ *dst++ = uint8_t(a * (start.r * oppAmount + end.r * amount));
+ *dst++ = uint8_t(a * (start.g * oppAmount + end.g * amount));
+ *dst++ = uint8_t(a * (start.b * oppAmount + end.b * amount));
+ *dst++ = uint8_t(alpha);
+}
+
+void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float amount,
+ uint8_t*& dst) const {
+ float oppAmount = 1.0f - amount;
+ const float a = start.a * oppAmount + end.a * amount;
+
+ float* d = (float*) dst;
+ *d++ = a * (start.r * oppAmount + end.r * amount);
+ *d++ = a * (start.g * oppAmount + end.g * amount);
+ *d++ = a * (start.b * oppAmount + end.b * amount);
+ *d++ = a;
+
+ dst += 4 * sizeof(float);
+}
+
void GradientCache::generateTexture(uint32_t* colors, float* positions,
int count, Texture* texture) {
-
const uint32_t width = texture->width;
- const GLsizei rowBytes = width * GRADIENT_BYTES_PER_PIXEL;
- uint32_t pixels[width * texture->height];
+ const GLsizei rowBytes = width * bytesPerPixel();
+ uint8_t pixels[rowBytes * texture->height];
- int currentPos = 1;
+ static ChannelSplitter gSplitters[] = {
+ &android::uirenderer::GradientCache::splitToBytes,
+ &android::uirenderer::GradientCache::splitToFloats,
+ };
+ ChannelSplitter split = gSplitters[mUseFloatTexture];
- float startA = (colors[0] >> 24) & 0xff;
- float startR = (colors[0] >> 16) & 0xff;
- float startG = (colors[0] >> 8) & 0xff;
- float startB = (colors[0] >> 0) & 0xff;
+ static ChannelMixer gMixers[] = {
+ &android::uirenderer::GradientCache::mixBytes,
+ &android::uirenderer::GradientCache::mixFloats,
+ };
+ ChannelMixer mix = gMixers[mUseFloatTexture];
- float endA = (colors[1] >> 24) & 0xff;
- float endR = (colors[1] >> 16) & 0xff;
- float endG = (colors[1] >> 8) & 0xff;
- float endB = (colors[1] >> 0) & 0xff;
+ GradientColor start;
+ (this->*split)(colors[0], start);
- float start = positions[0];
- float distance = positions[1] - start;
+ GradientColor end;
+ (this->*split)(colors[1], end);
- uint8_t* p = (uint8_t*) pixels;
+ int currentPos = 1;
+ float startPos = positions[0];
+ float distance = positions[1] - startPos;
+
+ uint8_t* dst = pixels;
for (uint32_t x = 0; x < width; x++) {
float pos = x / float(width - 1);
if (pos > positions[currentPos]) {
- startA = endA;
- startR = endR;
- startG = endG;
- startB = endB;
- start = positions[currentPos];
+ start = end;
+ startPos = positions[currentPos];
currentPos++;
- endA = (colors[currentPos] >> 24) & 0xff;
- endR = (colors[currentPos] >> 16) & 0xff;
- endG = (colors[currentPos] >> 8) & 0xff;
- endB = (colors[currentPos] >> 0) & 0xff;
- distance = positions[currentPos] - start;
+ (this->*split)(colors[currentPos], end);
+ distance = positions[currentPos] - startPos;
}
- float amount = (pos - start) / distance;
- float oppAmount = 1.0f - amount;
-
- const float alpha = startA * oppAmount + endA * amount;
- const float a = alpha / 255.0f;
- *p++ = uint8_t(a * (startR * oppAmount + endR * amount));
- *p++ = uint8_t(a * (startG * oppAmount + endG * amount));
- *p++ = uint8_t(a * (startB * oppAmount + endB * amount));
- *p++ = uint8_t(alpha);
+ float amount = (pos - startPos) / distance;
+ (this->*mix)(start, end, amount, dst);
}
- for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) {
- memcpy(pixels + width * i, pixels, rowBytes);
- }
+ memcpy(pixels + rowBytes, pixels, rowBytes);
glGenTextures(1, &texture->id);
-
glBindTexture(GL_TEXTURE_2D, texture->id);
- glPixelStorei(GL_UNPACK_ALIGNMENT, GRADIENT_BYTES_PER_PIXEL);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ if (mUseFloatTexture) {
+ // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, texture->height, 0,
+ GL_RGBA, GL_FLOAT, pixels);
+ } else {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ }
texture->setFilter(GL_LINEAR);
texture->setWrap(GL_CLAMP_TO_EDGE);
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 7dc5b8a..43934d9 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_HWUI_GRADIENT_CACHE_H
#define ANDROID_HWUI_GRADIENT_CACHE_H
-#include <GLES2/gl2.h>
+#include <GLES3/gl3.h>
#include <SkShader.h>
@@ -160,12 +160,35 @@ private:
void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info);
+ size_t bytesPerPixel() const;
+
+ struct GradientColor {
+ float r;
+ float g;
+ float b;
+ float a;
+ };
+
+ typedef void (GradientCache::*ChannelSplitter)(uint32_t inColor,
+ GradientColor& outColor) const;
+
+ void splitToBytes(uint32_t inColor, GradientColor& outColor) const;
+ void splitToFloats(uint32_t inColor, GradientColor& outColor) const;
+
+ typedef void (GradientCache::*ChannelMixer)(GradientColor& start, GradientColor& end,
+ float amount, uint8_t*& dst) const;
+
+ void mixBytes(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const;
+ void mixFloats(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const;
+
LruCache<GradientCacheEntry, Texture*> mCache;
uint32_t mSize;
uint32_t mMaxSize;
GLint mMaxTextureSize;
+ bool mUseFloatTexture;
+ bool mHasNpot;
Vector<SkShader*> mGarbage;
mutable Mutex mLock;
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 63bb73f..a718294 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -90,7 +90,7 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
if (fbo) {
Caches::getInstance().activeTexture(0);
bindTexture();
- allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
+ allocateTexture();
if (glGetError() != GL_NO_ERROR) {
setSize(oldWidth, oldHeight);
@@ -167,7 +167,6 @@ void Layer::defer() {
displayList->defer(deferredState, 0);
deferredUpdateScheduled = false;
- displayList = NULL;
}
void Layer::flush() {
@@ -182,7 +181,7 @@ void Layer::flush() {
renderer = NULL;
dirtyRect.setEmpty();
- deferredList->clear();
+ displayList = NULL;
}
}
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 27e0cf1..715dfa4 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -255,13 +255,14 @@ struct Layer {
texture.id = 0;
}
- inline void allocateTexture(GLenum format, GLenum storage) {
+ inline void allocateTexture() {
#if DEBUG_LAYERS
ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
#endif
if (texture.id) {
- glTexImage2D(renderTarget, 0, format, getWidth(), getHeight(), 0,
- format, storage, NULL);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 8451048..3e55fff 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -256,7 +256,7 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque
// Initialize the texture if needed
if (layer->isEmpty()) {
layer->setEmpty(false);
- layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
+ layer->allocateTexture();
// This should only happen if we run out of memory
if (glGetError() != GL_NO_ERROR) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 1138998..f81b4ff 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -112,11 +112,9 @@ static const Blender gBlendsSwap[] = {
OpenGLRenderer::OpenGLRenderer():
mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
- mDrawModifiers.mShader = NULL;
- mDrawModifiers.mColorFilter = NULL;
+ // *set* draw modifiers to be 0
+ memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
mDrawModifiers.mOverrideLayerAlpha = 1.0f;
- mDrawModifiers.mHasShadow = false;
- mDrawModifiers.mHasDrawFilter = false;
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
@@ -571,8 +569,8 @@ void OpenGLRenderer::updateLayers() {
startMark("Defer Layer Updates");
}
- // Note: it is very important to update the layers in reverse order
- for (int i = count - 1; i >= 0; i--) {
+ // Note: it is very important to update the layers in order
+ for (int i = 0; i < count; i++) {
Layer* layer = mLayerUpdates.itemAt(i);
updateLayer(layer, false);
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
@@ -594,8 +592,8 @@ void OpenGLRenderer::flushLayers() {
startMark("Apply Layer Updates");
char layerName[12];
- // Note: it is very important to update the layers in reverse order
- for (int i = count - 1; i >= 0; i--) {
+ // Note: it is very important to update the layers in order
+ for (int i = 0; i < count; i++) {
sprintf(layerName, "Layer #%d", i);
startMark(layerName);
@@ -922,7 +920,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui
// Initialize the texture if needed
if (layer->isEmpty()) {
- layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
+ layer->allocateTexture();
layer->setEmpty(false);
}
@@ -1330,10 +1328,9 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
}
}
- if (stateDeferFlags & kStateDeferFlag_Clip) {
+ state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip);
+ if (state.mClipValid) {
state.mClip.set(currentClip);
- } else {
- state.mClip.setEmpty();
}
// Transform, drawModifiers, and alpha always deferred, since they are used by state operations
@@ -1344,17 +1341,22 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
return false;
}
-void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state) {
+void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
currentTransform().load(state.mMatrix);
mDrawModifiers = state.mDrawModifiers;
mSnapshot->alpha = state.mAlpha;
- if (!state.mClip.isEmpty()) {
+ if (state.mClipValid && !skipClipRestore) {
mSnapshot->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom);
dirtyClip();
}
}
+void OpenGLRenderer::setFullScreenClip() {
+ mSnapshot->setClip(0, 0, mWidth, mHeight);
+ dirtyClip();
+}
+
///////////////////////////////////////////////////////////////////////////////
// Transforms
///////////////////////////////////////////////////////////////////////////////
@@ -1905,14 +1907,15 @@ void OpenGLRenderer::finishDrawTexture() {
status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty,
int32_t replayFlags) {
+ status_t status;
// All the usual checks and setup operations (quickReject, setupDraw, etc.)
// will be performed by the display list itself
if (displayList && displayList->isRenderable()) {
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
- startFrame();
+ status = startFrame();
ReplayStateStruct replayStruct(*this, dirty, replayFlags);
displayList->replay(replayStruct, 0);
- return replayStruct.mDrawGlStatus;
+ return status | replayStruct.mDrawGlStatus;
}
DeferredDisplayList deferredList;
@@ -1920,9 +1923,9 @@ status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty,
displayList->defer(deferStruct, 0);
flushLayers();
- startFrame();
+ status = startFrame();
- return deferredList.flush(*this, dirty);
+ return status | deferredList.flush(*this, dirty);
}
return DrawGlInfo::kStatusDone;
@@ -1962,6 +1965,42 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk
(GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
}
+status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
+ const Rect& bounds, SkPaint* paint) {
+
+ // merged draw operations don't need scissor, but clip should still be valid
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled);
+
+ mCaches.activeTexture(0);
+ Texture* texture = mCaches.textureCache.get(bitmap);
+ if (!texture) return DrawGlInfo::kStatusDone;
+ const AutoTexture autoCleanup(texture);
+
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ texture->setWrap(GL_CLAMP_TO_EDGE, true);
+ texture->setFilter(GL_NEAREST, true); // merged ops are always pure-translation for now
+
+ const float x = (int) floorf(bounds.left + 0.5f);
+ const float y = (int) floorf(bounds.top + 0.5f);
+ if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
+ int color = paint != NULL ? paint->getColor() : 0;
+ drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
+ texture->id, paint != NULL, color, alpha, mode,
+ &vertices[0].position[0], &vertices[0].texture[0],
+ GL_TRIANGLES, bitmapCount * 6, true, true);
+ } else {
+ drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
+ texture->id, alpha / 255.0f, mode, texture->blend,
+ &vertices[0].position[0], &vertices[0].texture[0],
+ GL_TRIANGLES, bitmapCount * 6, false, true, 0, true);
+ }
+
+ return DrawGlInfo::kStatusDrew;
+}
+
status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
const float right = left + bitmap->width();
const float bottom = top + bitmap->height();
@@ -2643,6 +2682,9 @@ void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesC
mCaches.dropShadowCache.setFontRenderer(fontRenderer);
const ShadowTexture* shadow = mCaches.dropShadowCache.get(
paint, text, bytesCount, count, mDrawModifiers.mShadowRadius, positions);
+ // If the drop shadow exceeds the max texture size or couldn't be
+ // allocated, skip drawing
+ if (!shadow) return;
const AutoTexture autoCleanup(shadow);
const float sx = x - shadow->left + mDrawModifiers.mShadowDx;
@@ -2792,8 +2834,11 @@ mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const {
}
status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, const float* positions, SkPaint* paint, float length) {
- if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
+ float x, float y, const float* positions, SkPaint* paint, float length,
+ DrawOpMode drawOpMode) {
+
+ if (drawOpMode == kDrawOpMode_Immediate &&
+ (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint))) {
return DrawGlInfo::kStatusDone;
}
@@ -2811,8 +2856,13 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
SkPaint::FontMetrics metrics;
paint->getFontMetrics(&metrics, 0.0f);
- if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
- return DrawGlInfo::kStatusDone;
+ if (drawOpMode == kDrawOpMode_Immediate) {
+ if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
+ return DrawGlInfo::kStatusDone;
+ }
+ } else {
+ // merged draw operations don't need scissor, but clip should still be valid
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled);
}
const float oldX = x;
@@ -2864,17 +2914,20 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
bool status;
TextSetupFunctor functor(*this, x, y, pureTranslate, alpha, mode, paint);
+
+ // don't call issuedrawcommand, do it at end of batch
+ bool forceFinish = (drawOpMode != kDrawOpMode_Defer);
if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
SkPaint paintCopy(*paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor);
+ positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
} else {
status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor);
+ positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
}
- if (status && hasActiveLayer) {
+ if ((status || drawOpMode != kDrawOpMode_Immediate) && hasActiveLayer) {
if (!pureTranslate) {
transform.mapRect(bounds);
}
@@ -3089,7 +3142,11 @@ void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::resetPaintFilter() {
+ // when clearing the PaintFilter, the masks should also be cleared for simple DrawModifier
+ // comparison, see MergingDrawBatch::canMergeWith
mDrawModifiers.mHasDrawFilter = false;
+ mDrawModifiers.mPaintFilterClearBits = 0;
+ mDrawModifiers.mPaintFilterSetBits = 0;
}
void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) {
@@ -3361,7 +3418,7 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b
void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom,
GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool ignoreTransform, bool dirty) {
+ bool ignoreTransform, bool ignoreScale, bool dirty) {
setupDraw();
setupDrawWithTexture(true);
@@ -3373,7 +3430,11 @@ void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, f
setupDrawBlending(true, mode);
setupDrawProgram();
if (!dirty) setupDrawDirtyRegionsDisabled();
- setupDrawModelView(left, top, right, bottom, ignoreTransform);
+ if (!ignoreScale) {
+ setupDrawModelView(left, top, right, bottom, ignoreTransform);
+ } else {
+ setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
+ }
setupDrawTexture(texture);
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index dd7a5a2..a0ad888 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -71,10 +71,17 @@ enum StateDeferFlags {
kStateDeferFlag_Clip = 0x2
};
+enum DrawOpMode {
+ kDrawOpMode_Immediate,
+ kDrawOpMode_Defer,
+ kDrawOpMode_Flush
+};
+
struct DeferredDisplayState {
- Rect mBounds; // local bounds, mapped with matrix to be in screen space coordinates, clipped.
+ Rect mBounds; // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped.
// the below are set and used by the OpenGLRenderer at record and deferred playback
+ bool mClipValid;
Rect mClip;
mat4 mMatrix;
DrawModifiers mDrawModifiers;
@@ -232,6 +239,8 @@ public:
virtual void outputDisplayList(DisplayList* displayList);
virtual status_t drawLayer(Layer* layer, float x, float y);
virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
+ status_t drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
+ const Rect& bounds, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
@@ -261,7 +270,8 @@ public:
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length = -1.0f);
+ const float* positions, SkPaint* paint, float length = -1.0f,
+ DrawOpMode drawOpMode = kDrawOpMode_Immediate);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
virtual void resetShader();
@@ -282,7 +292,8 @@ public:
SkPaint* filterPaint(SkPaint* paint);
bool storeDisplayState(DeferredDisplayState& state, int stateDeferFlags);
- void restoreDisplayState(const DeferredDisplayState& state);
+ void restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore = false);
+ void setFullScreenClip();
const DrawModifiers& getDrawModifiers() { return mDrawModifiers; }
void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; }
@@ -336,20 +347,18 @@ public:
* @param mode Where to store the resulting xfermode
*/
static inline void getAlphaAndModeDirect(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
- if (paint) {
- *mode = getXfermode(paint->getXfermode());
-
- // Skia draws using the color's alpha channel if < 255
- // Otherwise, it uses the paint's alpha
- int color = paint->getColor();
- *alpha = (color >> 24) & 0xFF;
- if (*alpha == 255) {
- *alpha = paint->getAlpha();
- }
- } else {
- *mode = SkXfermode::kSrcOver_Mode;
- *alpha = 255;
- }
+ *mode = getXfermodeDirect(paint);
+ *alpha = getAlphaDirect(paint);
+ }
+
+ static inline SkXfermode::Mode getXfermodeDirect(SkPaint* paint) {
+ if (!paint) return SkXfermode::kSrcOver_Mode;
+ return getXfermode(paint->getXfermode());
+ }
+
+ static inline int getAlphaDirect(SkPaint* paint) {
+ if (!paint) return 255;
+ return paint->getAlpha();
}
/**
@@ -358,6 +367,20 @@ public:
*/
mat4 findBestFontTransform(const mat4& transform) const;
+#if DEBUG_MERGE_BEHAVIOR
+ void drawScreenSpaceColorRect(float left, float top, float right, float bottom, int color) {
+ mCaches.setScissorEnabled(false);
+
+ // should only be called outside of other draw ops, so stencil can only be in test state
+ bool stencilWasEnabled = mCaches.stencil.isTestEnabled();
+ mCaches.stencil.disable();
+
+ drawColorRect(left, top, right, bottom, color, SkXfermode::kSrcOver_Mode, true);
+
+ if (stencilWasEnabled) mCaches.stencil.enableTest();
+ }
+#endif
+
protected:
/**
* Computes the projection matrix, initialize the first snapshot
@@ -778,7 +801,7 @@ private:
void drawAlpha8TextureMesh(float left, float top, float right, float bottom,
GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool ignoreTransform, bool dirty = true);
+ bool ignoreTransform, bool ignoreScale = false, bool dirty = true);
/**
* Draws text underline and strike-through if needed.
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
new file mode 100644
index 0000000..8280370
--- /dev/null
+++ b/libs/hwui/PixelBuffer.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/Log.h>
+
+#include "Caches.h"
+#include "Extensions.h"
+#include "PixelBuffer.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// CPU pixel buffer
+///////////////////////////////////////////////////////////////////////////////
+
+class CpuPixelBuffer: public PixelBuffer {
+public:
+ CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height);
+ ~CpuPixelBuffer();
+
+ uint8_t* map(AccessMode mode = kAccessMode_ReadWrite);
+ void unmap();
+
+ uint8_t* getMappedPointer() const;
+
+ void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset);
+
+private:
+ uint8_t* mBuffer;
+};
+
+CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height):
+ PixelBuffer(format, width, height) {
+ mBuffer = new uint8_t[width * height * formatSize(format)];
+}
+
+CpuPixelBuffer::~CpuPixelBuffer() {
+ delete[] mBuffer;
+}
+
+uint8_t* CpuPixelBuffer::map(AccessMode mode) {
+ if (mAccessMode == kAccessMode_None) {
+ mAccessMode = mode;
+ }
+ return mBuffer;
+}
+
+void CpuPixelBuffer::unmap() {
+ mAccessMode = kAccessMode_None;
+}
+
+uint8_t* CpuPixelBuffer::getMappedPointer() const {
+ return mAccessMode == kAccessMode_None ? NULL : mBuffer;
+}
+
+void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
+ mFormat, GL_UNSIGNED_BYTE, mBuffer + offset);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GPU pixel buffer
+///////////////////////////////////////////////////////////////////////////////
+
+class GpuPixelBuffer: public PixelBuffer {
+public:
+ GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height);
+ ~GpuPixelBuffer();
+
+ uint8_t* map(AccessMode mode = kAccessMode_ReadWrite);
+ void unmap();
+
+ uint8_t* getMappedPointer() const;
+
+ void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset);
+
+private:
+ GLuint mBuffer;
+ uint8_t* mMappedPointer;
+ Caches& mCaches;
+};
+
+GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height):
+ PixelBuffer(format, width, height), mMappedPointer(0), mCaches(Caches::getInstance()) {
+ glGenBuffers(1, &mBuffer);
+ mCaches.bindPixelBuffer(mBuffer);
+ glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), NULL, GL_DYNAMIC_DRAW);
+ mCaches.unbindPixelBuffer();
+}
+
+GpuPixelBuffer::~GpuPixelBuffer() {
+ glDeleteBuffers(1, &mBuffer);
+}
+
+uint8_t* GpuPixelBuffer::map(AccessMode mode) {
+ if (mAccessMode == kAccessMode_None) {
+ mCaches.bindPixelBuffer(mBuffer);
+ mMappedPointer = (uint8_t*) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode);
+ mAccessMode = mode;
+ }
+
+ return mMappedPointer;
+}
+
+void GpuPixelBuffer::unmap() {
+ if (mAccessMode != kAccessMode_None) {
+ if (mMappedPointer) {
+ mCaches.bindPixelBuffer(mBuffer);
+ glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+ }
+ mAccessMode = kAccessMode_None;
+ mMappedPointer = NULL;
+ }
+}
+
+uint8_t* GpuPixelBuffer::getMappedPointer() const {
+ return mMappedPointer;
+}
+
+void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
+ // If the buffer is not mapped, unmap() will not bind it
+ mCaches.bindPixelBuffer(mBuffer);
+ unmap();
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat,
+ GL_UNSIGNED_BYTE, (void*) offset);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Factory
+///////////////////////////////////////////////////////////////////////////////
+
+PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) {
+ bool gpuBuffer = type == kBufferType_Auto && Extensions::getInstance().getMajorGlVersion() >= 3;
+ if (gpuBuffer) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, property, "false") > 0) {
+ if (!strcmp(property, "true")) {
+ return new GpuPixelBuffer(format, width, height);
+ }
+ }
+ }
+ return new CpuPixelBuffer(format, width, height);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h
new file mode 100644
index 0000000..32d5417
--- /dev/null
+++ b/libs/hwui/PixelBuffer.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_PIXEL_BUFFER_H
+#define ANDROID_HWUI_PIXEL_BUFFER_H
+
+#include <GLES3/gl3.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Represents a pixel buffer. A pixel buffer will be backed either by a
+ * PBO on OpenGL ES 3.0 and higher or by an array of uint8_t on other
+ * versions. If the buffer is backed by a PBO it will of type
+ * GL_PIXEL_UNPACK_BUFFER.
+ *
+ * To read from or write into a PixelBuffer you must first map the
+ * buffer using the map(AccessMode) method. This method returns a
+ * pointer to the beginning of the buffer.
+ *
+ * Before the buffer can be used by the GPU, for instance to upload
+ * a texture, you must first unmap the buffer. To do so, call the
+ * unmap() method.
+ *
+ * Mapping and unmapping a PixelBuffer can have the side effect of
+ * changing the currently active GL_PIXEL_UNPACK_BUFFER. It is
+ * therefore recommended to call Caches::unbindPixelbuffer() after
+ * using a PixelBuffer to upload to a texture.
+ */
+class PixelBuffer {
+public:
+ enum BufferType {
+ kBufferType_Auto,
+ kBufferType_CPU
+ };
+
+ enum AccessMode {
+ kAccessMode_None = 0,
+ kAccessMode_Read = GL_MAP_READ_BIT,
+ kAccessMode_Write = GL_MAP_WRITE_BIT,
+ kAccessMode_ReadWrite = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT
+ };
+
+ /**
+ * Creates a new PixelBuffer object with the specified format and
+ * dimensions. The buffer is immediately allocated.
+ *
+ * The buffer type specifies how the buffer should be allocated.
+ * By default this method will automatically choose whether to allocate
+ * a CPU or GPU buffer.
+ */
+ static PixelBuffer* create(GLenum format, uint32_t width, uint32_t height,
+ BufferType type = kBufferType_Auto);
+
+ virtual ~PixelBuffer() {
+ }
+
+ /**
+ * Returns the format of this render buffer.
+ */
+ GLenum getFormat() const {
+ return mFormat;
+ }
+
+ /**
+ * Maps this before with the specified access mode. This method
+ * returns a pointer to the region of memory where the buffer was
+ * mapped.
+ *
+ * If the buffer is already mapped when this method is invoked,
+ * this method will return the previously mapped pointer. The
+ * access mode can only be changed by calling unmap() first.
+ *
+ * The specified access mode cannot be kAccessMode_None.
+ */
+ virtual uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) = 0;
+
+ /**
+ * Unmaps this buffer, if needed. After the buffer is unmapped,
+ * the pointer previously returned by map() becomes invalid and
+ * should not be used. After calling this method, getMappedPointer()
+ * will always return NULL.
+ */
+ virtual void unmap() = 0;
+
+ /**
+ * Returns the current access mode for this buffer. If the buffer
+ * is not mapped, this method returns kAccessMode_None.
+ */
+ AccessMode getAccessMode() const {
+ return mAccessMode;
+ }
+
+ /**
+ * Returns the currently mapped pointer. Returns NULL if the buffer
+ * is not mapped.
+ */
+ virtual uint8_t* getMappedPointer() const = 0;
+
+ /**
+ * Upload the specified rectangle of this pixe buffer as a
+ * GL_TEXTURE_2D texture. Calling this method will trigger
+ * an unmap() if necessary.
+ */
+ virtual void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) = 0;
+
+ /**
+ * Returns the width of the render buffer in pixels.
+ */
+ uint32_t getWidth() const {
+ return mWidth;
+ }
+
+ /**
+ * Returns the height of the render buffer in pixels.
+ */
+ uint32_t getHeight() const {
+ return mHeight;
+ }
+
+ /**
+ * Returns the size of this pixel buffer in bytes.
+ */
+ uint32_t getSize() const {
+ return mWidth * mHeight * formatSize(mFormat);
+ }
+
+ /**
+ * Returns the number of bytes per pixel in the specified format.
+ *
+ * Supported formats:
+ * GL_ALPHA
+ * GL_RGBA
+ */
+ static uint32_t formatSize(GLenum format) {
+ switch (format) {
+ case GL_ALPHA:
+ return 1;
+ case GL_RGBA:
+ return 4;
+ }
+ return 0;
+ }
+
+protected:
+ /**
+ * Creates a new render buffer in the specified format and dimensions.
+ * The format must be GL_ALPHA or GL_RGBA.
+ */
+ PixelBuffer(GLenum format, uint32_t width, uint32_t height):
+ mFormat(format), mWidth(width), mHeight(height), mAccessMode(kAccessMode_None) {
+ }
+
+ GLenum mFormat;
+
+ uint32_t mWidth;
+ uint32_t mHeight;
+
+ AccessMode mAccessMode;
+
+}; // class PixelBuffer
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_PIXEL_BUFFER_H
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index f78fb2d..8eb85e5 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -19,6 +19,7 @@
#include <utils/String8.h>
#include "Caches.h"
+#include "Dither.h"
#include "ProgramCache.h"
namespace android {
@@ -32,6 +33,9 @@ namespace uirenderer {
#define MODULATE_OP_MODULATE 1
#define MODULATE_OP_MODULATE_A8 2
+#define STR(x) STR1(x)
+#define STR1(x) #x
+
///////////////////////////////////////////////////////////////////////////////
// Vertex shaders snippets
///////////////////////////////////////////////////////////////////////////////
@@ -51,17 +55,8 @@ const char* gVS_Header_Uniforms =
"uniform mat4 transform;\n";
const char* gVS_Header_Uniforms_IsPoint =
"uniform mediump float pointSize;\n";
-const char* gVS_Header_Uniforms_HasGradient[3] = {
- // Linear
- "uniform mat4 screenSpace;\n"
- "uniform float ditherSize;\n",
- // Circular
- "uniform mat4 screenSpace;\n"
- "uniform float ditherSize;\n",
- // Sweep
- "uniform mat4 screenSpace;\n"
- "uniform float ditherSize;\n"
-};
+const char* gVS_Header_Uniforms_HasGradient =
+ "uniform mat4 screenSpace;\n";
const char* gVS_Header_Uniforms_HasBitmap =
"uniform mat4 textureTransform;\n"
"uniform mediump vec2 textureDimension;\n";
@@ -105,21 +100,21 @@ const char* gVS_Main_OutTransformedTexCoords =
const char* gVS_Main_OutGradient[6] = {
// Linear
" linear = vec2((screenSpace * position).x, 0.5);\n"
- " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
" linear = (screenSpace * position).x;\n"
- " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
// Circular
" circular = (screenSpace * position).xy;\n"
- " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
" circular = (screenSpace * position).xy;\n"
- " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
// Sweep
" sweep = (screenSpace * position).xy;\n"
- " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
" sweep = (screenSpace * position).xy;\n"
- " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
};
const char* gVS_Main_OutBitmapTexCoords =
" outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
@@ -153,24 +148,14 @@ const char* gFS_Uniforms_TextureSampler =
"uniform sampler2D baseSampler;\n";
const char* gFS_Uniforms_ExternalTextureSampler =
"uniform samplerExternalOES baseSampler;\n";
-#define FS_UNIFORMS_DITHER \
- "uniform float ditherSizeSquared;\n" \
- "uniform sampler2D ditherSampler;\n"
-#define FS_UNIFORMS_GRADIENT \
- "uniform vec4 startColor;\n" \
+const char* gFS_Uniforms_Dither =
+ "uniform sampler2D ditherSampler;";
+const char* gFS_Uniforms_GradientSampler[2] = {
+ "%s\n"
+ "uniform sampler2D gradientSampler;\n",
+ "%s\n"
+ "uniform vec4 startColor;\n"
"uniform vec4 endColor;\n"
-const char* gFS_Uniforms_GradientSampler[6] = {
- // Linear
- FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
- FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT,
-
- // Circular
- FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
- FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT,
-
- // Sweep
- FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
- FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT
};
const char* gFS_Uniforms_BitmapSampler =
"uniform sampler2D bitmapSampler;\n";
@@ -197,10 +182,14 @@ const char* gFS_Main_PointBitmapTexCoords =
" highp vec2 outBitmapTexCoords = outPointBitmapTexCoords + "
"((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n";
-#define FS_MAIN_DITHER \
- "texture2D(ditherSampler, ditherTexCoords).a * ditherSizeSquared"
+const char* gFS_Main_Dither[2] = {
+ // ES 2.0
+ "texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE),
+ // ES 3.0
+ "texture2D(ditherSampler, ditherTexCoords).a"
+};
const char* gFS_Main_AddDitherToGradient =
- " gradientColor += " FS_MAIN_DITHER ";\n";
+ " gradientColor += %s;\n";
// Fast cases
const char* gFS_Fast_SingleColor =
@@ -233,18 +222,18 @@ const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma =
"}\n\n";
const char* gFS_Fast_SingleGradient[2] = {
"\nvoid main(void) {\n"
- " gl_FragColor = " FS_MAIN_DITHER " + texture2D(gradientSampler, linear);\n"
+ " gl_FragColor = %s + texture2D(gradientSampler, linear);\n"
"}\n\n",
"\nvoid main(void) {\n"
- " gl_FragColor = " FS_MAIN_DITHER " + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
- "}\n\n"
+ " gl_FragColor = %s + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
+ "}\n\n",
};
const char* gFS_Fast_SingleModulateGradient[2] = {
"\nvoid main(void) {\n"
- " gl_FragColor = " FS_MAIN_DITHER " + color.a * texture2D(gradientSampler, linear);\n"
+ " gl_FragColor = %s + color.a * texture2D(gradientSampler, linear);\n"
"}\n\n",
"\nvoid main(void) {\n"
- " gl_FragColor = " FS_MAIN_DITHER " + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
+ " gl_FragColor = %s + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
"}\n\n"
};
@@ -410,7 +399,7 @@ const char* gBlendOps[18] = {
// Constructors/destructors
///////////////////////////////////////////////////////////////////////////////
-ProgramCache::ProgramCache() {
+ProgramCache::ProgramCache(): mHasES3(Extensions::getInstance().getMajorGlVersion() >= 3) {
}
ProgramCache::~ProgramCache() {
@@ -484,7 +473,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
shader.append(gVS_Header_Uniforms_TextureTransform);
}
if (description.hasGradient) {
- shader.append(gVS_Header_Uniforms_HasGradient[description.gradientType]);
+ shader.append(gVS_Header_Uniforms_HasGradient);
}
if (description.hasBitmap) {
shader.append(gVS_Header_Uniforms_HasBitmap);
@@ -601,7 +590,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.append(gFS_Uniforms_ExternalTextureSampler);
}
if (description.hasGradient) {
- shader.append(gFS_Uniforms_GradientSampler[gradientIndex(description)]);
+ shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
+ gFS_Uniforms_Dither);
}
if (description.hasBitmap && description.isPoint) {
shader.append(gFS_Header_Uniforms_PointHasBitmap);
@@ -652,9 +642,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
fast = true;
} else if (singleGradient) {
if (!description.modulate) {
- shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]);
+ shader.appendFormat(gFS_Fast_SingleGradient[description.isSimpleGradient],
+ gFS_Main_Dither[mHasES3]);
} else {
- shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]);
+ shader.appendFormat(gFS_Fast_SingleModulateGradient[description.isSimpleGradient],
+ gFS_Main_Dither[mHasES3]);
}
fast = true;
}
@@ -708,7 +700,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
}
if (description.hasGradient) {
shader.append(gFS_Main_FetchGradient[gradientIndex(description)]);
- shader.append(gFS_Main_AddDitherToGradient);
+ shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]);
}
if (description.hasBitmap) {
if (description.isPoint) {
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 1ca148d..38f6f99 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -57,6 +57,8 @@ private:
void printLongString(const String8& shader) const;
KeyedVector<programid, Program*> mCache;
+
+ const bool mHasES3;
}; // class ProgramCache
}; // namespace uirenderer
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index e4b4f3c..6eea00c 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -25,6 +25,10 @@
* the OpenGLRenderer.
*/
+///////////////////////////////////////////////////////////////////////////////
+// Compile-time properties
+///////////////////////////////////////////////////////////////////////////////
+
// If turned on, text is interpreted as glyphs instead of UTF-16
#define RENDER_TEXT_AS_GLYPHS 1
@@ -39,6 +43,10 @@
// to properly implement overdraw debugging
#define STENCIL_BUFFER_SIZE 8
+///////////////////////////////////////////////////////////////////////////////
+// Debug properties
+///////////////////////////////////////////////////////////////////////////////
+
/**
* Debug level for app developers. The value is a numeric value defined
* by the DebugLevel enum below.
@@ -82,6 +90,23 @@ enum DebugLevel {
#define PROPERTY_DEBUG_STENCIL_CLIP "debug.hwui.show_non_rect_clip"
/**
+ * Disables draw operation deferral if set to "true", forcing draw
+ * commands to be issued to OpenGL in order, and processed in sequence
+ * with state-manipulation canvas commands.
+ */
+#define PROPERTY_DISABLE_DRAW_DEFER "debug.hwui.disable_draw_defer"
+
+/**
+ * Used to disable draw operation reordering when deferring draw operations
+ * Has no effect if PROPERTY_DISABLE_DRAW_DEFER is set to "true"
+ */
+#define PROPERTY_DISABLE_DRAW_REORDER "debug.hwui.disable_draw_reorder"
+
+///////////////////////////////////////////////////////////////////////////////
+// Runtime configuration properties
+///////////////////////////////////////////////////////////////////////////////
+
+/**
* Used to enable/disable scissor optimization. The accepted values are
* "true" and "false". The default value is "false".
*
@@ -97,17 +122,10 @@ enum DebugLevel {
#define PROPERTY_DISABLE_SCISSOR_OPTIMIZATION "ro.hwui.disable_scissor_opt"
/**
- * Disables draw operation deferral if set to "true", forcing draw
- * commands to be issued to OpenGL in order, and processed in sequence
- * with state-manipulation canvas commands.
- */
-#define PROPERTY_DISABLE_DRAW_DEFER "debug.hwui.disable_draw_defer"
-
-/**
- * Used to disable draw operation reordering when deferring draw operations
- * Has no effect if PROPERTY_DISABLE_DRAW_DEFER is set to "true"
+ * Indicates whether PBOs can be used to back pixel buffers.
+ * Accepted values are "true" and "false".
*/
-#define PROPERTY_DISABLE_DRAW_REORDER "debug.hwui.disable_draw_reorder"
+#define PROPERTY_ENABLE_GPU_PIXEL_BUFFERS "hwui.use_gpu_pixel_buffers"
// These properties are defined in mega-bytes
#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
@@ -152,8 +170,9 @@ enum DebugLevel {
// Lumincance threshold above which white gamma correction is applied. Range: [0..255]
#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "hwui.text_gamma.white_threshold"
-// Converts a number of mega-bytes into bytes
-#define MB(s) s * 1024 * 1024
+///////////////////////////////////////////////////////////////////////////////
+// Default property values
+///////////////////////////////////////////////////////////////////////////////
#define DEFAULT_TEXTURE_CACHE_SIZE 24.0f
#define DEFAULT_LAYER_CACHE_SIZE 16.0f
@@ -170,6 +189,13 @@ enum DebugLevel {
#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192
+///////////////////////////////////////////////////////////////////////////////
+// Misc
+///////////////////////////////////////////////////////////////////////////////
+
+// Converts a number of mega-bytes into bytes
+#define MB(s) s * 1024 * 1024
+
static DebugLevel readDebugLevel() {
char property[PROPERTY_VALUE_MAX];
if (property_get(PROPERTY_DEBUG, property, NULL) > 0) {
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index f1f35bd..6976eaa 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -178,6 +178,10 @@ ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32
FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, text, 0,
len, numGlyphs, radius, positions);
+ if (!shadow.image) {
+ return NULL;
+ }
+
texture = new ShadowTexture;
texture->left = shadow.penX;
texture->top = shadow.penY;
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 1096642..6c5267d 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -15,10 +15,11 @@
*/
#include <SkGlyph.h>
-#include <utils/Log.h>
-#include "Debug.h"
#include "CacheTexture.h"
+#include "../Debug.h"
+#include "../Extensions.h"
+#include "../PixelBuffer.h"
namespace android {
namespace uirenderer {
@@ -112,6 +113,11 @@ CacheTexture::CacheTexture(uint16_t width, uint16_t height, uint32_t maxQuadCoun
mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount) {
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+
+ // OpenGL ES 3.0+ lets us specify the row length for unpack operations such
+ // as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
+ // With OpenGL ES 2.0 we have to upload entire stripes instead.
+ mHasES3 = Extensions::getInstance().getMajorGlVersion() >= 3;
}
CacheTexture::~CacheTexture() {
@@ -144,7 +150,7 @@ void CacheTexture::releaseMesh() {
void CacheTexture::releaseTexture() {
if (mTexture) {
- delete[] mTexture;
+ delete mTexture;
mTexture = NULL;
}
if (mTextureId) {
@@ -155,6 +161,17 @@ void CacheTexture::releaseTexture() {
mCurrentQuad = 0;
}
+void CacheTexture::setLinearFiltering(bool linearFiltering, bool bind) {
+ if (linearFiltering != mLinearFiltering) {
+ mLinearFiltering = linearFiltering;
+
+ const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
+ if (bind) glBindTexture(GL_TEXTURE_2D, getTextureId());
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+ }
+}
+
void CacheTexture::allocateMesh() {
if (!mMesh) {
mMesh = new TextureVertex[mMaxQuadCount * 4];
@@ -163,7 +180,7 @@ void CacheTexture::allocateMesh() {
void CacheTexture::allocateTexture() {
if (!mTexture) {
- mTexture = new uint8_t[mWidth * mHeight];
+ mTexture = PixelBuffer::create(GL_ALPHA, mWidth, mHeight);
}
if (!mTextureId) {
@@ -184,6 +201,34 @@ void CacheTexture::allocateTexture() {
}
}
+bool CacheTexture::upload() {
+ const Rect& dirtyRect = mDirtyRect;
+
+ uint32_t x = mHasES3 ? dirtyRect.left : 0;
+ uint32_t y = dirtyRect.top;
+ uint32_t width = mHasES3 ? dirtyRect.getWidth() : mWidth;
+ uint32_t height = dirtyRect.getHeight();
+
+ // The unpack row length only needs to be specified when a new
+ // texture is bound
+ if (mHasES3) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, mWidth);
+ }
+
+ mTexture->upload(x, y, width, height, y * mWidth + x);
+
+ setDirty(false);
+
+ return mHasES3;
+}
+
+void CacheTexture::setDirty(bool dirty) {
+ mDirty = dirty;
+ if (!dirty) {
+ mDirtyRect.setEmpty();
+ }
+}
+
bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mHeight) {
return false;
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index 5742941..ddcc836 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_HWUI_CACHE_TEXTURE_H
#define ANDROID_HWUI_CACHE_TEXTURE_H
-#include <GLES2/gl2.h>
+#include <GLES3/gl3.h>
#include <SkScalerContext.h>
@@ -30,6 +30,8 @@
namespace android {
namespace uirenderer {
+class PixelBuffer;
+
/**
* CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
* Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
@@ -83,6 +85,10 @@ public:
void allocateTexture();
void allocateMesh();
+ // Returns true if glPixelStorei(GL_UNPACK_ROW_LENGTH) must be reset
+ // This method will also call setDirty(false)
+ bool upload();
+
bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY);
inline uint16_t getWidth() const {
@@ -97,7 +103,7 @@ public:
return &mDirtyRect;
}
- inline uint8_t* getTexture() const {
+ inline PixelBuffer* getPixelBuffer() const {
return mTexture;
}
@@ -110,13 +116,6 @@ public:
return mDirty;
}
- inline void setDirty(bool dirty) {
- mDirty = dirty;
- if (!dirty) {
- mDirtyRect.setEmpty();
- }
- }
-
inline bool getLinearFiltering() const {
return mLinearFiltering;
}
@@ -124,16 +123,7 @@ public:
/**
* This method assumes that the proper texture unit is active.
*/
- void setLinearFiltering(bool linearFiltering, bool bind = true) {
- if (linearFiltering != mLinearFiltering) {
- mLinearFiltering = linearFiltering;
-
- const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
- if (bind) glBindTexture(GL_TEXTURE_2D, getTextureId());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
- }
- }
+ void setLinearFiltering(bool linearFiltering, bool bind = true);
inline uint16_t getGlyphCount() const {
return mNumGlyphs;
@@ -176,7 +166,9 @@ public:
}
private:
- uint8_t* mTexture;
+ void setDirty(bool dirty);
+
+ PixelBuffer* mTexture;
GLuint mTextureId;
uint16_t mWidth;
uint16_t mHeight;
@@ -188,6 +180,7 @@ private:
uint32_t mMaxQuadCount;
CacheBlock* mCacheBlocks;
Rect mDirtyRect;
+ bool mHasES3;
};
}; // namespace uirenderer
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index 02c1aa1..011cfc1 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -25,11 +25,12 @@
#include <SkGlyph.h>
#include <SkUtils.h>
-#include "Debug.h"
#include "FontUtil.h"
#include "Font.h"
-#include "FontRenderer.h"
-#include "Properties.h"
+#include "../Debug.h"
+#include "../FontRenderer.h"
+#include "../PixelBuffer.h"
+#include "../Properties.h"
namespace android {
namespace uirenderer {
@@ -200,25 +201,23 @@ void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y,
p[3].x(), p[3].y(), u1, v1, glyph->mCacheTexture);
}
-void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop;
-
- uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
- uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
+void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap,
+ uint32_t bitmapWidth, uint32_t bitmapHeight, Rect* bounds, const float* pos) {
+ int dstX = x + glyph->mBitmapLeft;
+ int dstY = y + glyph->mBitmapTop;
CacheTexture* cacheTexture = glyph->mCacheTexture;
+
uint32_t cacheWidth = cacheTexture->getWidth();
- const uint8_t* cacheBuffer = cacheTexture->getTexture();
-
- uint32_t cacheX = 0, cacheY = 0;
- int32_t bX = 0, bY = 0;
- for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
- for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
- uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
- bitmap[bY * bitmapW + bX] = tempCol;
- }
+ uint32_t startY = glyph->mStartY * cacheWidth;
+ uint32_t endY = startY + (glyph->mBitmapHeight * cacheWidth);
+
+ PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer();
+ const uint8_t* cacheBuffer = pixelBuffer->map();
+
+ for (uint32_t cacheY = startY, bitmapY = dstY * bitmapWidth; cacheY < endY;
+ cacheY += cacheWidth, bitmapY += bitmapWidth) {
+ memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth);
}
}
diff --git a/libs/hwui/utils/TinyHashMap.h b/libs/hwui/utils/TinyHashMap.h
new file mode 100644
index 0000000..8855140
--- /dev/null
+++ b/libs/hwui/utils/TinyHashMap.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_TINYHASHMAP_H
+#define ANDROID_HWUI_TINYHASHMAP_H
+
+#include <utils/BasicHashtable.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A very simple hash map that doesn't allow duplicate keys, overwriting the older entry.
+ *
+ * Currently, expects simple keys that are handled by hash_t()
+ */
+template <typename TKey, typename TValue>
+class TinyHashMap {
+public:
+ typedef key_value_pair_t<TKey, TValue> TEntry;
+
+ /**
+ * Puts an entry in the hash, removing any existing entry with the same key
+ */
+ void put(TKey key, TValue value) {
+ hash_t hash = hash_t(key);
+
+ ssize_t index = mTable.find(-1, hash, key);
+ if (index != -1) {
+ mTable.removeAt(index);
+ }
+
+ TEntry initEntry(key, value);
+ mTable.add(hash, initEntry);
+ }
+
+ /**
+ * Return true if key is in the map, in which case stores the value in the output ref
+ */
+ bool get(TKey key, TValue& outValue) {
+ hash_t hash = hash_t(key);
+ ssize_t index = mTable.find(-1, hash, key);
+ if (index == -1) {
+ return false;
+ }
+ outValue = mTable.entryAt(index).value;
+ return true;
+ }
+
+ void clear() { mTable.clear(); }
+
+private:
+ BasicHashtable<TKey, TEntry> mTable;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_TINYHASHMAP_H
diff --git a/location/java/android/location/IGeofenceProvider.aidl b/location/java/android/location/IGeofenceProvider.aidl
new file mode 100644
index 0000000..5a5fdc6
--- /dev/null
+++ b/location/java/android/location/IGeofenceProvider.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.hardware.location.IGeofenceHardware;
+
+/**
+ * An interface for location providers implementing the Geofencing service
+ *
+ * {@hide}
+ */
+interface IGeofenceProvider {
+ void setGeofenceHardware(in IGeofenceHardware proxy);
+}
diff --git a/location/java/android/location/IGpsGeofenceHardware.aidl b/location/java/android/location/IGpsGeofenceHardware.aidl
new file mode 100644
index 0000000..764bf8e
--- /dev/null
+++ b/location/java/android/location/IGpsGeofenceHardware.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+/**
+ * GPS hardware geofence
+ *
+ * @hide
+ */
+interface IGpsGeofenceHardware
+{
+ boolean isHardwareGeofenceSupported();
+ boolean addCircularHardwareGeofence(int geofenceId, double latitude, double
+ longitude, double radius, int lastTransition, int monitorTransition,
+ int notificationResponsiveness, int unknownTimer);
+ boolean removeHardwareGeofence(int geofenceId);
+ boolean pauseHardwareGeofence(int geofenceId);
+ boolean resumeHardwareGeofence(int geofenceId, int monitorTransition);
+}
diff --git a/location/lib/java/com/android/location/provider/GeofenceProvider.java b/location/lib/java/com/android/location/provider/GeofenceProvider.java
new file mode 100644
index 0000000..2618f34
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/GeofenceProvider.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.location.provider;
+
+import android.hardware.location.GeofenceHardware;
+import android.hardware.location.IGeofenceHardware;
+import android.os.IBinder;
+
+import android.location.IGeofenceProvider;
+import android.util.Log;
+
+import java.lang.Long;
+
+/**
+ * Base class for geofence providers implemented as unbundled services.
+ *
+ * <p>Geofence providers can be implemented as services and return the result of
+ * {@link com.android.location.provider.GeofenceProvider#getBinder()} in its getBinder() method.
+ *
+ * <p>IMPORTANT: This class is effectively a public API for unbundled
+ * applications, and must remain API stable. See README.txt in the root
+ * of this package for more information.
+ */
+public abstract class GeofenceProvider {
+
+ private GeofenceHardware mGeofenceHardware;
+
+ private IGeofenceProvider.Stub mProvider = new IGeofenceProvider.Stub() {
+ public void setGeofenceHardware(IGeofenceHardware hardwareProxy) {
+ mGeofenceHardware = new GeofenceHardware(hardwareProxy);
+ onGeofenceHardwareChange(mGeofenceHardware);
+ }
+ };
+
+ /**
+ * Returns the Binder interface for the geofence provider.
+ * This is intended to be used for the onBind() method of
+ * a service that implements a geofence service.
+ *
+ * @return the IBinder instance for the provider
+ */
+ public IBinder getBinder() {
+ return mProvider;
+ }
+
+ /**
+ * Called when GeofenceHardware object becomes available.
+ *
+ * @param geofenceHardware Geofence Hardware object. This can be null
+ * when for some reason the service connection gets disconnected.
+ */
+ public abstract void onGeofenceHardwareChange(GeofenceHardware geofenceHardware);
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index b80a166..917a47d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -50,6 +50,7 @@ public class AudioManager {
private long mVolumeKeyUpTime;
private final boolean mUseMasterVolume;
private final boolean mUseVolumeKeySounds;
+ private final Binder mToken = new Binder();
private static String TAG = "AudioManager";
/**
@@ -2075,7 +2076,8 @@ public class AudioManager {
IAudioService service = getService();
try {
// pi != null
- service.registerMediaButtonIntent(pi, eventReceiver);
+ service.registerMediaButtonIntent(pi, eventReceiver,
+ eventReceiver == null ? mToken : null);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index fd71d79..637ac85 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -357,7 +357,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
private static final int SCO_STATE_INACTIVE = 0;
// SCO audio activation request waiting for headset service to connect
private static final int SCO_STATE_ACTIVATE_REQ = 1;
- // SCO audio state is active or starting due to a local request to start a virtual call
+ // SCO audio state is active or starting due to a request from AudioManager API
private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
// SCO audio deactivation request waiting for headset service to connect
private static final int SCO_STATE_DEACTIVATE_REQ = 5;
@@ -530,6 +530,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// Register for package removal intent broadcasts for media button receiver persistence
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
pkgFilter.addDataScheme("package");
context.registerReceiver(mReceiver, pkgFilter);
@@ -2053,8 +2056,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
if (mScoAudioState == SCO_STATE_INACTIVE) {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
- if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice)) {
+ if (mBluetoothHeadset.connectAudio()) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
} else {
broadcastScoConnectionState(
@@ -2076,8 +2078,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
- if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice)) {
+ if (!mBluetoothHeadset.disconnectAudio()) {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -2250,12 +2251,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
switch (mScoAudioState) {
case SCO_STATE_ACTIVATE_REQ:
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
+ status = mBluetoothHeadset.connectAudio();
break;
case SCO_STATE_DEACTIVATE_REQ:
- status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
+ status = mBluetoothHeadset.disconnectAudio();
break;
case SCO_STATE_DEACTIVATE_EXT_REQ:
status = mBluetoothHeadset.stopVoiceRecognition(
@@ -3535,6 +3534,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
onNewPlaybackStateForRcc(msg.arg1 /* rccId */, msg.arg2 /* state */,
(RccPlaybackState)msg.obj /* newState */);
break;
+ case MSG_RCC_SEEK_REQUEST:
+ onSetRemoteControlClientPlaybackPosition(msg.arg1 /* generationId */,
+ ((Long)msg.obj).longValue() /* timeMs */);
case MSG_SET_RSX_CONNECTION_STATE:
onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
@@ -4037,14 +4039,21 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
0,
null,
SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
- } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
+ } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
+ || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
// a package is being removed, not replaced
String packageName = intent.getData().getSchemeSpecificPart();
if (packageName != null) {
- removeMediaButtonReceiverForPackage(packageName);
+ cleanupMediaButtonReceiverForPackage(packageName, true);
}
}
+ } else if (action.equals(Intent.ACTION_PACKAGE_ADDED)
+ || action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
+ String packageName = intent.getData().getSchemeSpecificPart();
+ if (packageName != null) {
+ cleanupMediaButtonReceiverForPackage(packageName, false);
+ }
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
AudioSystem.setParameters("screen_state=on");
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
@@ -4851,8 +4860,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
- private static class RemoteControlStackEntry {
+ private static class RemoteControlStackEntry implements DeathRecipient {
public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+ final public AudioService mService;
/**
* The target for the ACTION_MEDIA_BUTTON events.
* Always non null.
@@ -4863,6 +4873,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* Always non null.
*/
final public ComponentName mReceiverComponent;
+ public IBinder mToken;
public String mCallingPackageName;
public int mCallingUid;
/**
@@ -4893,9 +4904,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
/** precondition: mediaIntent != null */
- public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
+ public RemoteControlStackEntry(AudioService service, PendingIntent mediaIntent,
+ ComponentName eventReceiver, IBinder token) {
+ mService = service;
mMediaIntent = mediaIntent;
mReceiverComponent = eventReceiver;
+ mToken = token;
mCallingUid = -1;
mRcClient = null;
mRccId = ++sLastRccId;
@@ -4905,6 +4919,17 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
RemoteControlClient.PLAYBACK_SPEED_1X);
resetPlaybackInfo();
+ if (mToken != null) {
+ try {
+ mToken.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ mService.mAudioHandler.post(new Runnable() {
+ @Override public void run() {
+ mService.unregisterMediaButtonIntent(mMediaIntent);
+ }
+ });
+ }
+ }
}
public void unlinkToRcClientDeath() {
@@ -4920,9 +4945,22 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
+ public void destroy() {
+ unlinkToRcClientDeath();
+ if (mToken != null) {
+ mToken.unlinkToDeath(this, 0);
+ mToken = null;
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ mService.unregisterMediaButtonIntent(mMediaIntent);
+ }
+
@Override
protected void finalize() throws Throwable {
- unlinkToRcClientDeath();// unlink exception handled inside method
+ destroy(); // unlink exception handled inside method
super.finalize();
}
}
@@ -5021,11 +5059,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* Remove any entry in the remote control stack that has the same package name as packageName
* Pre-condition: packageName != null
*/
- private void removeMediaButtonReceiverForPackage(String packageName) {
+ private void cleanupMediaButtonReceiverForPackage(String packageName, boolean removeAll) {
synchronized(mRCStack) {
if (mRCStack.empty()) {
return;
} else {
+ final PackageManager pm = mContext.getPackageManager();
RemoteControlStackEntry oldTop = mRCStack.peek();
Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
// iterate over the stack entries
@@ -5033,10 +5072,19 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// evaluated it, traversal order doesn't matter here)
while(stackIterator.hasNext()) {
RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
- if (packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
+ if (removeAll && packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
// a stack entry is from the package being removed, remove it from the stack
stackIterator.remove();
- rcse.unlinkToRcClientDeath();
+ rcse.destroy();
+ } else if (rcse.mReceiverComponent != null) {
+ try {
+ // Check to see if this receiver still exists.
+ pm.getReceiverInfo(rcse.mReceiverComponent, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Not found -- remove it!
+ stackIterator.remove();
+ rcse.destroy();
+ }
}
}
if (mRCStack.empty()) {
@@ -5079,7 +5127,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mediaButtonIntent.setComponent(eventReceiver);
PendingIntent pi = PendingIntent.getBroadcast(mContext,
0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
- registerMediaButtonIntent(pi, eventReceiver);
+ registerMediaButtonIntent(pi, eventReceiver, null);
}
}
@@ -5089,7 +5137,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* Called synchronized on mAudioFocusLock, then mRCStack
* precondition: mediaIntent != null
*/
- private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target) {
+ private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target,
+ IBinder token) {
// already at top of stack?
if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
return;
@@ -5111,7 +5160,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
}
if (!wasInsideStack) {
- rcse = new RemoteControlStackEntry(mediaIntent, target);
+ rcse = new RemoteControlStackEntry(this, mediaIntent, target, token);
}
mRCStack.push(rcse); // rcse is never null
@@ -5133,7 +5182,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
for (int index = mRCStack.size()-1; index >= 0; index--) {
final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
if (rcse.mMediaIntent.equals(pi)) {
- rcse.unlinkToRcClientDeath();
+ rcse.destroy();
// ok to remove element while traversing the stack since we're leaving the loop
mRCStack.removeElementAt(index);
break;
@@ -5421,12 +5470,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
* precondition: mediaIntent != null
*/
- public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
+ public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver,
+ IBinder token) {
Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent);
synchronized(mAudioFocusLock) {
synchronized(mRCStack) {
- pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver);
+ pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token);
// new RC client, assume every type of information shall be queried
checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
}
@@ -5820,7 +5870,16 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_QUEUE, generationId /* arg1 */,
+ // ignore position change requests if invalid generation ID
+ synchronized(mRCStack) {
+ synchronized(mCurrentRcLock) {
+ if (mCurrentRcClientGen != generationId) {
+ return;
+ }
+ }
+ }
+ // discard any unprocessed seek request in the message queue, and replace with latest
+ sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */,
0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */);
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index cd50de4..399eb7b 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -791,7 +791,7 @@ public class AudioTrack
* {@link #ERROR_INVALID_OPERATION}
*/
public int setPlaybackRate(int sampleRateInHz) {
- if (mState == STATE_UNINITIALIZED) {
+ if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
if (sampleRateInHz <= 0) {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 25aae8f..13f6c02 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -120,7 +120,7 @@ interface IAudioService {
oneway void dispatchMediaKeyEvent(in KeyEvent keyEvent);
void dispatchMediaKeyEventUnderWakelock(in KeyEvent keyEvent);
- void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c);
+ void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c, IBinder token);
oneway void unregisterMediaButtonIntent(in PendingIntent pi);
oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index b6b49a2..45a8b99 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -22,6 +22,7 @@ import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.view.Surface;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.Map;
/**
@@ -395,6 +396,27 @@ final public class MediaCodec {
* see {@link #CRYPTO_MODE_UNENCRYPTED} and {@link #CRYPTO_MODE_AES_CTR}.
*/
public int mode;
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(numSubSamples + " subsamples, key [");
+ String hexdigits = "0123456789abcdef";
+ for (int i = 0; i < key.length; i++) {
+ builder.append(hexdigits.charAt((key[i] & 0xf0) >> 4));
+ builder.append(hexdigits.charAt(key[i] & 0x0f));
+ }
+ builder.append("], iv [");
+ for (int i = 0; i < key.length; i++) {
+ builder.append(hexdigits.charAt((iv[i] & 0xf0) >> 4));
+ builder.append(hexdigits.charAt(iv[i] & 0x0f));
+ }
+ builder.append("], clear ");
+ builder.append(Arrays.toString(numBytesOfClearData));
+ builder.append(", encrypted ");
+ builder.append(Arrays.toString(numBytesOfEncryptedData));
+ return builder.toString();
+ }
};
/**
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 3cdf261..6872278 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Bundle;
+import android.os.Parcel;
import android.util.Log;
/**
@@ -136,10 +137,8 @@ public final class MediaDrm {
public static final int MEDIA_DRM_EVENT_KEY_EXPIRED = 3;
public static final int MEDIA_DRM_EVENT_VENDOR_DEFINED = 4;
- /* Do not change these values without updating their counterparts
- * in include/media/mediadrm.h!
- */
private static final int DRM_EVENT = 200;
+
private class EventHandler extends Handler
{
private MediaDrm mMediaDrm;
@@ -161,10 +160,18 @@ public final class MediaDrm {
Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
if (mOnEventListener != null) {
- Bundle bundle = msg.getData();
- byte[] sessionId = bundle.getByteArray("sessionId");
- byte[] data = bundle.getByteArray("data");
- mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
+ if (msg.obj != null && msg.obj instanceof Parcel) {
+ Parcel parcel = (Parcel)msg.obj;
+ byte[] sessionId = parcel.createByteArray();
+ if (sessionId.length == 0) {
+ sessionId = null;
+ }
+ byte[] data = parcel.createByteArray();
+ if (data.length == 0) {
+ data = null;
+ }
+ mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
+ }
}
return;
@@ -183,14 +190,14 @@ public final class MediaDrm {
* the cookie passed to native_setup().)
*/
private static void postEventFromNative(Object mediadrm_ref,
- int what, int arg1, int arg2, Object obj)
+ int eventType, int extra, Object obj)
{
MediaDrm md = (MediaDrm)((WeakReference)mediadrm_ref).get();
if (md == null) {
return;
}
if (md.mEventHandler != null) {
- Message m = md.mEventHandler.obtainMessage(what, arg1, arg2, obj);
+ Message m = md.mEventHandler.obtainMessage(DRM_EVENT, eventType, extra, obj);
md.mEventHandler.sendMessage(m);
}
}
@@ -208,6 +215,7 @@ public final class MediaDrm {
public static final int MEDIA_DRM_KEY_TYPE_STREAMING = 1;
public static final int MEDIA_DRM_KEY_TYPE_OFFLINE = 2;
+ public static final int MEDIA_DRM_KEY_TYPE_RELEASE = 3;
public final class KeyRequest {
public KeyRequest() {}
@@ -216,28 +224,36 @@ public final class MediaDrm {
};
/**
- * A key request/response exchange occurs between the app and a license
- * server to obtain the keys to decrypt encrypted content. getKeyRequest()
- * is used to obtain an opaque key request byte array that is delivered to the
- * license server. The opaque key request byte array is returned in
- * KeyRequest.data. The recommended URL to deliver the key request to is
+ * A key request/response exchange occurs between the app and a license server
+ * to obtain or release keys used to decrypt encrypted content.
+ * getKeyRequest() is used to obtain an opaque key request byte array that is
+ * delivered to the license server. The opaque key request byte array is returned
+ * in KeyRequest.data. The recommended URL to deliver the key request to is
* returned in KeyRequest.defaultUrl.
*
* After the app has received the key request response from the server,
* it should deliver to the response to the DRM engine plugin using the method
* {@link #provideKeyResponse}.
*
- * @param sessonId the session ID for the drm session
+ * @param scope may be a sessionId or a keySetId, depending on the specified keyType.
+ * When the keyType is MEDIA_DRM_KEY_TYPE_STREAMING or MEDIA_DRM_KEY_TYPE_OFFLINE,
+ * scope should be set to the sessionId the keys will be provided to. When the keyType
+ * is MEDIA_DRM_KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys
+ * being released. Releasing keys from a device invalidates them for all sessions.
* @param init container-specific data, its meaning is interpreted based on the
* mime type provided in the mimeType parameter. It could contain, for example,
* the content ID, key ID or other data obtained from the content metadata that is
- * required in generating the key request.
+ * required in generating the key request. init may be null when keyType is
+ * MEDIA_DRM_KEY_TYPE_RELEASE.
* @param mimeType identifies the mime type of the content
- * @param keyType specifes if the request is for streaming or offline content
+ * @param keyType specifes the type of the request. The request may be to acquire
+ * keys for streaming or offline content, or to release previously acquired
+ * keys, which are identified by a keySetId.
+
* @param optionalParameters are included in the key request message to
* allow a client application to provide additional message parameters to the server.
*/
- public native KeyRequest getKeyRequest(byte[] sessionId, byte[] init,
+ public native KeyRequest getKeyRequest(byte[] scope, byte[] init,
String mimeType, int keyType,
HashMap<String, String> optionalParameters)
throws MediaDrmException;
@@ -265,13 +281,11 @@ public final class MediaDrm {
throws MediaDrmException;
/**
- * Remove the persisted keys associated with an offline license. Keys are persisted
- * when {@link provideKeyResponse} is called with keys obtained from the method
- * {@link getKeyRequest} using keyType = MEDIA_DRM_KEY_TYPE_OFFLINE.
+ * Remove the current keys from a session.
*
- * @param keySetId identifies the saved key set to remove
+ * @param sessionId the session ID for the DRM session
*/
- public native void removeKeys(byte[] keySetId) throws MediaDrmException;
+ public native void removeKeys(byte[] sessionId) throws MediaDrmException;
/**
* Request an informative description of the key status for the session. The status is
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 749ef12..cf159f0 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -26,7 +26,10 @@ import android.net.Uri;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.HashMap;
import java.util.Map;
+import java.util.UUID;
/**
* MediaExtractor facilitates extraction of demuxed, typically encoded, media data
@@ -64,7 +67,7 @@ final public class MediaExtractor {
* Sets the DataSource object to be used as the data source for this extractor
* {@hide}
*/
- public native final void setDataSource(DataSource source);
+ public native final void setDataSource(DataSource source) throws IOException;
/**
* Sets the data source as a content Uri.
@@ -118,7 +121,8 @@ final public class MediaExtractor {
* @param path the path of the file, or the http URL
* @param headers the headers associated with the http request for the stream you want to play
*/
- public final void setDataSource(String path, Map<String, String> headers) {
+ public final void setDataSource(String path, Map<String, String> headers)
+ throws IOException {
String[] keys = null;
String[] values = null;
@@ -137,7 +141,7 @@ final public class MediaExtractor {
}
private native final void setDataSource(
- String path, String[] keys, String[] values);
+ String path, String[] keys, String[] values) throws IOException;
/**
* Sets the data source (file-path or http URL) to use.
@@ -151,7 +155,7 @@ final public class MediaExtractor {
* As an alternative, the application could first open the file for reading,
* and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
*/
- public final void setDataSource(String path) {
+ public final void setDataSource(String path) throws IOException {
setDataSource(path, null, null);
}
@@ -161,7 +165,7 @@ final public class MediaExtractor {
*
* @param fd the FileDescriptor for the file you want to extract from.
*/
- public final void setDataSource(FileDescriptor fd) {
+ public final void setDataSource(FileDescriptor fd) throws IOException {
setDataSource(fd, 0, 0x7ffffffffffffffL);
}
@@ -175,7 +179,7 @@ final public class MediaExtractor {
* @param length the length in bytes of the data to be extracted
*/
public native final void setDataSource(
- FileDescriptor fd, long offset, long length);
+ FileDescriptor fd, long offset, long length) throws IOException;
@Override
protected void finalize() {
@@ -195,6 +199,38 @@ final public class MediaExtractor {
public native final int getTrackCount();
/**
+ * Get the PSSH info if present. This returns a map of uuid-to-bytes, with the uuid specifying
+ * the crypto scheme, and the bytes being the data specific to that scheme.
+ * {@hide}
+ */
+ public Map<UUID, byte[]> getPsshInfo() {
+ Map<UUID, byte[]> psshMap = null;
+ Map<String, Object> formatMap = getFileFormatNative();
+ if (formatMap != null && formatMap.containsKey("pssh")) {
+ ByteBuffer rawpssh = (ByteBuffer) formatMap.get("pssh");
+ rawpssh.order(ByteOrder.nativeOrder());
+ rawpssh.rewind();
+ formatMap.remove("pssh");
+ // parse the flat pssh bytebuffer into something more manageable
+ psshMap = new HashMap<UUID, byte[]>();
+ while (rawpssh.remaining() > 0) {
+ rawpssh.order(ByteOrder.BIG_ENDIAN);
+ long msb = rawpssh.getLong();
+ long lsb = rawpssh.getLong();
+ UUID uuid = new UUID(msb, lsb);
+ rawpssh.order(ByteOrder.nativeOrder());
+ int datalen = rawpssh.getInt();
+ byte [] psshdata = new byte[datalen];
+ rawpssh.get(psshdata);
+ psshMap.put(uuid, psshdata);
+ }
+ }
+ return psshMap;
+ }
+
+ private native Map<String, Object> getFileFormatNative();
+
+ /**
* Get the track format at the specified index.
* More detail on the representation can be found at {@link android.media.MediaCodec}
*/
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index 1f5ca35..c0fbd2e 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -257,8 +257,10 @@ final public class MediaMuxer {
}
/**
- * Writes an encoded sample into the muxer. The application needs to make
- * sure that the samples are written into the right tracks.
+ * Writes an encoded sample into the muxer.
+ * <p>The application needs to make sure that the samples are written into
+ * the right tracks. Also, it needs to make sure the samples for each track
+ * are written in chronological order.</p>
* @param byteBuf The encoded sample.
* @param trackIndex The track index for this sample.
* @param bufferInfo The buffer information related to this sample.
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 85a32ca..f745163 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1181,7 +1181,8 @@ public class MediaPlayer
/**
* Gets the duration of the file.
*
- * @return the duration in milliseconds
+ * @return the duration in milliseconds, if no duration is available
+ * (for example, if streaming live content), -1 is returned.
*/
public native int getDuration();
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 795c3c2..61c55a5 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -767,10 +767,19 @@ public class MediaRouter {
boolean wantScan = false;
boolean blockScan = false;
WifiDisplay[] oldDisplays = oldStatus != null ?
- oldStatus.getRememberedDisplays() : new WifiDisplay[0];
- WifiDisplay[] newDisplays = newStatus.getRememberedDisplays();
- WifiDisplay[] availableDisplays = newStatus.getAvailableDisplays();
- WifiDisplay activeDisplay = newStatus.getActiveDisplay();
+ oldStatus.getRememberedDisplays() : WifiDisplay.EMPTY_ARRAY;
+ WifiDisplay[] newDisplays;
+ WifiDisplay[] availableDisplays;
+ WifiDisplay activeDisplay;
+
+ if (newStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
+ newDisplays = newStatus.getRememberedDisplays();
+ availableDisplays = newStatus.getAvailableDisplays();
+ activeDisplay = newStatus.getActiveDisplay();
+ } else {
+ newDisplays = availableDisplays = WifiDisplay.EMPTY_ARRAY;
+ activeDisplay = null;
+ }
for (int i = 0; i < newDisplays.length; i++) {
final WifiDisplay d = newDisplays[i];
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index e076ef0..4a5e82e 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -277,14 +277,12 @@ public class RemoteControlClient
*/
public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
/**
- * @hide
- * TODO un-hide and add in javadoc of setTransportControlFlags(int)
* Flag indicating a RemoteControlClient can receive changes in the media playback position
- * through the {@link #OnPlaybackPositionUpdateListener} interface. This flag must be set
+ * through the {@link OnPlaybackPositionUpdateListener} interface. This flag must be set
* in order for components that display the RemoteControlClient information, to display and
* let the user control media playback position.
* @see #setTransportControlFlags(int)
- * @see #setPlaybackPositionProvider(PlaybackPositionProvider)
+ * @see #setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener)
* @see #setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener)
*/
public final static int FLAG_KEY_MEDIA_POSITION_UPDATE = 1 << 8;
@@ -607,8 +605,6 @@ public class RemoteControlClient
}
/**
- * @hide
- * TODO un-hide
* Sets the current playback state and the matching media position for the current playback
* speed.
* @param state The current playback state, one of the following values:
@@ -660,7 +656,8 @@ public class RemoteControlClient
* {@link #FLAG_KEY_MEDIA_PAUSE},
* {@link #FLAG_KEY_MEDIA_STOP},
* {@link #FLAG_KEY_MEDIA_FAST_FORWARD},
- * {@link #FLAG_KEY_MEDIA_NEXT}
+ * {@link #FLAG_KEY_MEDIA_NEXT},
+ * {@link #FLAG_KEY_MEDIA_POSITION_UPDATE}
*/
public void setTransportControlFlags(int transportControlFlags) {
synchronized(mCacheLock) {
@@ -673,8 +670,6 @@ public class RemoteControlClient
}
/**
- * @hide
- * TODO un-hide
* Interface definition for a callback to be invoked when the media playback position is
* requested to be updated.
* @see RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE
@@ -683,39 +678,38 @@ public class RemoteControlClient
/**
* Called on the implementer to notify it that the playback head should be set at the given
* position. If the position can be changed from its current value, the implementor of
- * the interface should also update the playback position using
- * {@link RemoteControlClient#setPlaybackState(int, long, int)} to reflect the actual new
+ * the interface must also update the playback position using
+ * {@link #setPlaybackState(int, long, float)} to reflect the actual new
* position being used, regardless of whether it differs from the requested position.
+ * Failure to do so would cause the system to not know the new actual playback position,
+ * and user interface components would fail to show the user where playback resumed after
+ * the position was updated.
* @param newPositionMs the new requested position in the current media, expressed in ms.
*/
void onPlaybackPositionUpdate(long newPositionMs);
}
/**
- * @hide
- * TODO un-hide
* Interface definition for a callback to be invoked when the media playback position is
* queried.
* @see RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE
*/
- public interface PlaybackPositionProvider {
+ public interface OnGetPlaybackPositionListener {
/**
* Called on the implementer of the interface to query the current playback position.
* @return a negative value if the current playback position (or the last valid playback
* position) is not known, or a zero or positive value expressed in ms indicating the
* current position, or the last valid known position.
*/
- long getPlaybackPosition();
+ long onGetPlaybackPosition();
}
/**
- * @hide
- * TODO un-hide
* Sets the listener to be called whenever the media playback position is requested
* to be updated.
* Notifications will be received in the same thread as the one in which RemoteControlClient
* was created.
- * @param l
+ * @param l the position update listener to be called
*/
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l) {
synchronized(mCacheLock) {
@@ -734,14 +728,12 @@ public class RemoteControlClient
}
/**
- * @hide
- * TODO un-hide
* Sets the listener to be called whenever the media current playback position is needed.
* Queries will be received in the same thread as the one in which RemoteControlClient
* was created.
- * @param l
+ * @param l the listener to be called to retrieve the playback position
*/
- public void setPlaybackPositionProvider(PlaybackPositionProvider l) {
+ public void setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener l) {
synchronized(mCacheLock) {
int oldCapa = mPlaybackPositionCapabilities;
if (l != null) {
@@ -939,7 +931,7 @@ public class RemoteControlClient
/**
* Provider registered by user of RemoteControlClient to provide the current playback position.
*/
- private PlaybackPositionProvider mPositionProvider;
+ private OnGetPlaybackPositionListener mPositionProvider;
/**
* The current remote control client generation ID across the system, as known by this object
*/
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 6873060..416a2a1 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -28,6 +28,7 @@ LOCAL_SHARED_LIBRARIES := \
libmedia \
libskia \
libui \
+ liblog \
libcutils \
libgui \
libstagefright \
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 3a42db2..cd1d9ce 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -372,7 +372,7 @@ static jint throwExceptionAsNecessary(
default:
{
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ jniThrowException(env, "java/lang/IllegalStateException", msg);
break;
}
}
@@ -455,13 +455,13 @@ static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ jniThrowException(env, "java/lang/IllegalStateException", "no codec found");
return;
}
status_t err = codec->start();
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, "start failed");
}
static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 1618edf..c32ba9d 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -21,10 +21,12 @@
#include "android_media_MediaDrm.h"
#include "android_runtime/AndroidRuntime.h"
+#include "android_os_Parcel.h"
#include "jni.h"
#include "JNIHelp.h"
#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
#include <media/IDrm.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -43,6 +45,15 @@ namespace android {
var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \
LOG_FATAL_IF(! var, "Unable to find method " fieldName);
+#define GET_STATIC_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetStaticFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+#define GET_STATIC_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetStaticMethodID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find static method " fieldName);
+
+
struct RequestFields {
jfieldID data;
jfieldID defaultUrl;
@@ -74,8 +85,16 @@ struct EntryFields {
jmethodID getValue;
};
+struct EventTypes {
+ int kEventProvisionRequired;
+ int kEventKeyRequired;
+ int kEventKeyExpired;
+ int kEventVendorDefined;
+} gEventTypes;
+
struct fields_t {
jfieldID context;
+ jmethodID post_event;
RequestFields keyRequest;
RequestFields provisionRequest;
ArrayListFields arraylist;
@@ -87,6 +106,88 @@ struct fields_t {
static fields_t gFields;
+// ----------------------------------------------------------------------------
+// ref-counted object for callbacks
+class JNIDrmListener: public DrmListener
+{
+public:
+ JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
+ ~JNIDrmListener();
+ virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj = NULL);
+private:
+ JNIDrmListener();
+ jclass mClass; // Reference to MediaDrm class
+ jobject mObject; // Weak ref to MediaDrm Java object to call on
+};
+
+JNIDrmListener::JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
+{
+ // Hold onto the MediaDrm class for use in calling the static method
+ // that posts events to the application thread.
+ jclass clazz = env->GetObjectClass(thiz);
+ if (clazz == NULL) {
+ ALOGE("Can't find android/media/MediaDrm");
+ jniThrowException(env, "java/lang/Exception", NULL);
+ return;
+ }
+ mClass = (jclass)env->NewGlobalRef(clazz);
+
+ // We use a weak reference so the MediaDrm object can be garbage collected.
+ // The reference is only used as a proxy for callbacks.
+ mObject = env->NewGlobalRef(weak_thiz);
+}
+
+JNIDrmListener::~JNIDrmListener()
+{
+ // remove global references
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->DeleteGlobalRef(mObject);
+ env->DeleteGlobalRef(mClass);
+}
+
+void JNIDrmListener::notify(DrmPlugin::EventType eventType, int extra,
+ const Parcel *obj)
+{
+ jint jeventType;
+
+ // translate DrmPlugin event types into their java equivalents
+ switch(eventType) {
+ case DrmPlugin::kDrmPluginEventProvisionRequired:
+ jeventType = gEventTypes.kEventProvisionRequired;
+ break;
+ case DrmPlugin::kDrmPluginEventKeyNeeded:
+ jeventType = gEventTypes.kEventKeyRequired;
+ break;
+ case DrmPlugin::kDrmPluginEventKeyExpired:
+ jeventType = gEventTypes.kEventKeyExpired;
+ break;
+ case DrmPlugin::kDrmPluginEventVendorDefined:
+ jeventType = gEventTypes.kEventVendorDefined;
+ break;
+ default:
+ ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
+ return;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (obj && obj->dataSize() > 0) {
+ jobject jParcel = createJavaParcelObject(env);
+ if (jParcel != NULL) {
+ Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
+ nativeParcel->setData(obj->data(), obj->dataSize());
+ env->CallStaticVoidMethod(mClass, gFields.post_event, mObject,
+ jeventType, extra, jParcel);
+ }
+ }
+
+ if (env->ExceptionCheck()) {
+ ALOGW("An exception occurred while notifying an event.");
+ LOGW_EX(env);
+ env->ExceptionClear();
+ }
+}
+
+
static bool throwExceptionAsNecessary(
JNIEnv *env, status_t err, const char *msg = NULL) {
@@ -109,6 +210,9 @@ JDrm::JDrm(
JNIEnv *env, jobject thiz, const uint8_t uuid[16]) {
mObject = env->NewWeakGlobalRef(thiz);
mDrm = MakeDrm(uuid);
+ if (mDrm != NULL) {
+ mDrm->setListener(this);
+ }
}
JDrm::~JDrm() {
@@ -160,6 +264,25 @@ sp<IDrm> JDrm::MakeDrm(const uint8_t uuid[16]) {
return drm;
}
+status_t JDrm::setListener(const sp<DrmListener>& listener) {
+ Mutex::Autolock lock(mLock);
+ mListener = listener;
+ return OK;
+}
+
+void JDrm::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
+ sp<DrmListener> listener;
+ mLock.lock();
+ listener = mListener;
+ mLock.unlock();
+
+ if (listener != NULL) {
+ Mutex::Autolock lock(mNotifyLock);
+ listener->notify(eventType, extra, obj);
+ }
+}
+
+
// static
bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16]) {
sp<IDrm> drm = MakeDrm();
@@ -194,10 +317,9 @@ static jbyteArray VectorToJByteArray(JNIEnv *env, Vector<uint8_t> const &vector)
}
static String8 JStringToString8(JNIEnv *env, jstring const &jstr) {
- jboolean isCopy;
String8 result;
- const char *s = env->GetStringUTFChars(jstr, &isCopy);
+ const char *s = env->GetStringUTFChars(jstr, NULL);
if (s) {
result = s;
env->ReleaseStringUTFChars(jstr, s);
@@ -322,13 +444,28 @@ static bool CheckSession(JNIEnv *env, const sp<IDrm> &drm, jbyteArray const &jse
}
static void android_media_MediaDrm_release(JNIEnv *env, jobject thiz) {
- setDrm(env, thiz, NULL);
+ sp<JDrm> drm = setDrm(env, thiz, NULL);
+ if (drm != NULL) {
+ drm->setListener(NULL);
+ }
}
static void android_media_MediaDrm_native_init(JNIEnv *env) {
jclass clazz;
FIND_CLASS(clazz, "android/media/MediaDrm");
GET_FIELD_ID(gFields.context, clazz, "mNativeContext", "I");
+ GET_STATIC_METHOD_ID(gFields.post_event, clazz, "postEventFromNative",
+ "(Ljava/lang/Object;IILjava/lang/Object;)V");
+
+ jfieldID field;
+ GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_PROVISION_REQUIRED", "I");
+ gEventTypes.kEventProvisionRequired = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_KEY_REQUIRED", "I");
+ gEventTypes.kEventKeyRequired = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_KEY_EXPIRED", "I");
+ gEventTypes.kEventKeyExpired = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_VENDOR_DEFINED", "I");
+ gEventTypes.kEventVendorDefined = env->GetStaticIntField(clazz, field);
FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
GET_FIELD_ID(gFields.keyRequest.data, clazz, "data", "[B");
@@ -389,6 +526,8 @@ static void android_media_MediaDrm_native_setup(
return;
}
+ sp<JNIDrmListener> listener = new JNIDrmListener(env, thiz, weak_this);
+ drm->setListener(listener);
setDrm(env, thiz, drm);
}
diff --git a/media/jni/android_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h
index 01067c4..9b3917f 100644
--- a/media/jni/android_media_MediaDrm.h
+++ b/media/jni/android_media_MediaDrm.h
@@ -20,6 +20,8 @@
#include "jni.h"
#include <media/stagefright/foundation/ABase.h>
+#include <media/IDrm.h>
+#include <media/IDrmClient.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -27,15 +29,24 @@ namespace android {
struct IDrm;
-struct JDrm : public RefBase {
+class DrmListener: virtual public RefBase
+{
+public:
+ virtual void notify(DrmPlugin::EventType eventType, int extra,
+ const Parcel *obj) = 0;
+};
+
+struct JDrm : public BnDrmClient {
static bool IsCryptoSchemeSupported(const uint8_t uuid[16]);
JDrm(JNIEnv *env, jobject thiz, const uint8_t uuid[16]);
status_t initCheck() const;
-
sp<IDrm> getDrm() { return mDrm; }
+ void notify(DrmPlugin::EventType, int extra, const Parcel *obj);
+ status_t setListener(const sp<DrmListener>& listener);
+
protected:
virtual ~JDrm();
@@ -43,6 +54,10 @@ private:
jweak mObject;
sp<IDrm> mDrm;
+ sp<DrmListener> mListener;
+ Mutex mNotifyLock;
+ Mutex mLock;
+
static sp<IDrm> MakeDrm();
static sp<IDrm> MakeDrm(const uint8_t uuid[16]);
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 23949fa..1704d5c 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -162,6 +162,18 @@ status_t JMediaExtractor::getTrackFormat(size_t index, jobject *format) const {
return ConvertMessageToMap(env, msg, format);
}
+status_t JMediaExtractor::getFileFormat(jobject *format) const {
+ sp<AMessage> msg;
+ status_t err;
+ if ((err = mImpl->getFileFormat(&msg)) != OK) {
+ return err;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ return ConvertMessageToMap(env, msg, format);
+}
+
status_t JMediaExtractor::selectTrack(size_t index) {
return mImpl->selectTrack(index);
}
@@ -339,6 +351,26 @@ static jobject android_media_MediaExtractor_getTrackFormatNative(
return format;
}
+static jobject android_media_MediaExtractor_getFileFormatNative(
+ JNIEnv *env, jobject thiz) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return NULL;
+ }
+
+ jobject format;
+ status_t err = extractor->getFileFormat(&format);
+
+ if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return NULL;
+ }
+
+ return format;
+}
+
static void android_media_MediaExtractor_selectTrack(
JNIEnv *env, jobject thiz, jint index) {
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
@@ -768,6 +800,9 @@ static JNINativeMethod gMethods[] = {
{ "getTrackCount", "()I", (void *)android_media_MediaExtractor_getTrackCount },
+ { "getFileFormatNative", "()Ljava/util/Map;",
+ (void *)android_media_MediaExtractor_getFileFormatNative },
+
{ "getTrackFormatNative", "(I)Ljava/util/Map;",
(void *)android_media_MediaExtractor_getTrackFormatNative },
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index 03900db..ccbad8c 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -45,6 +45,8 @@ struct JMediaExtractor : public RefBase {
size_t countTracks() const;
status_t getTrackFormat(size_t index, jobject *format) const;
+ status_t getFileFormat(jobject *format) const;
+
status_t selectTrack(size_t index);
status_t unselectTrack(size_t index);
diff --git a/media/jni/audioeffect/Android.mk b/media/jni/audioeffect/Android.mk
index b5d8b7b..3b1fb19 100644
--- a/media/jni/audioeffect/Android.mk
+++ b/media/jni/audioeffect/Android.mk
@@ -6,6 +6,7 @@ LOCAL_SRC_FILES:= \
android_media_Visualizer.cpp
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libcutils \
libutils \
libandroid_runtime \
diff --git a/media/jni/mediaeditor/Android.mk b/media/jni/mediaeditor/Android.mk
index 040d2ab..6be7fdd 100644
--- a/media/jni/mediaeditor/Android.mk
+++ b/media/jni/mediaeditor/Android.mk
@@ -52,6 +52,7 @@ LOCAL_SHARED_LIBRARIES := \
libaudioutils \
libbinder \
libcutils \
+ liblog \
libdl \
libgui \
libmedia \
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 9b11bfa..5835b9f 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \
android_media_SoundPool.cpp
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libcutils \
libutils \
libandroid_runtime \
diff --git a/media/libdrm/mobile1/Android.mk b/media/libdrm/mobile1/Android.mk
index b07d91c..7356f46 100644
--- a/media/libdrm/mobile1/Android.mk
+++ b/media/libdrm/mobile1/Android.mk
@@ -44,6 +44,7 @@ LOCAL_CFLAGS := $(LOCAL_DRM_CFLAG)
LOCAL_SHARED_LIBRARIES := \
libutils \
libcutils \
+ liblog \
libcrypto
LOCAL_MODULE := libdrm1
@@ -69,12 +70,13 @@ LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include/parser \
$(JNI_H_INCLUDE) \
$(call include-path-for, system-core)/cutils
-
+
LOCAL_SHARED_LIBRARIES := libdrm1 \
libnativehelper \
libutils \
- libcutils
+ libcutils \
+ liblog
LOCAL_MODULE := libdrm1_jni
diff --git a/media/mca/filterfw/Android.mk b/media/mca/filterfw/Android.mk
index 1d69799..2a9448d 100644
--- a/media/mca/filterfw/Android.mk
+++ b/media/mca/filterfw/Android.mk
@@ -37,6 +37,7 @@ LOCAL_SHARED_LIBRARIES := libstlport \
libdl \
libcutils \
libutils \
+ liblog \
libandroid \
libjnigraphics \
libmedia
@@ -48,5 +49,3 @@ LOCAL_SHARED_LIBRARIES := libstlport \
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
-
-
diff --git a/media/mca/filterpacks/Android.mk b/media/mca/filterpacks/Android.mk
index 6166b1e..6e54f60 100644
--- a/media/mca/filterpacks/Android.mk
+++ b/media/mca/filterpacks/Android.mk
@@ -46,10 +46,8 @@ LOCAL_SRC_FILES += native/imageproc/brightness.c \
native/imageproc/invert.c \
native/imageproc/to_rgba.c
-LOCAL_SHARED_LIBRARIES := libutils libfilterfw
+LOCAL_SHARED_LIBRARIES := liblog libutils libfilterfw
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
-
-
diff --git a/media/tests/omxjpegdecoder/Android.mk b/media/tests/omxjpegdecoder/Android.mk
index 9dcc7ba..ad874c8 100644
--- a/media/tests/omxjpegdecoder/Android.mk
+++ b/media/tests/omxjpegdecoder/Android.mk
@@ -29,6 +29,7 @@ LOCAL_SHARED_LIBRARIES := \
libstagefright_foundation \
libbinder \
libutils \
+ liblog \
libjpeg
LOCAL_C_INCLUDES := \
diff --git a/media/tests/players/Android.mk b/media/tests/players/Android.mk
index c655ae6..adf0d30 100644
--- a/media/tests/players/Android.mk
+++ b/media/tests/players/Android.mk
@@ -20,7 +20,8 @@ LOCAL_SRC_FILES:= invoke_mock_media_player.cpp
LOCAL_SHARED_LIBRARIES:= \
libbinder \
- libutils
+ libutils \
+ liblog
LOCAL_MODULE:= invoke_mock_media_player
LOCAL_MODULE_TAGS := tests eng
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 00d11da..207cc4b 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \
storage_manager.cpp
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libcutils \
libandroidfw \
libutils \
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 5a2e261..ab7ceb6 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -18,7 +18,6 @@ package android.opengl;
import java.io.Writer;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
@@ -30,10 +29,13 @@ import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
-import android.content.pm.ConfigurationInfo;
-import android.os.SystemProperties;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.Choreographer;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@@ -164,11 +166,26 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
private final static String TAG = "GLSurfaceView";
private final static boolean LOG_ATTACH_DETACH = false;
private final static boolean LOG_THREADS = false;
- private final static boolean LOG_PAUSE_RESUME = false;
private final static boolean LOG_SURFACE = false;
private final static boolean LOG_RENDERER = false;
private final static boolean LOG_RENDERER_DRAW_FRAME = false;
private final static boolean LOG_EGL = false;
+ private final static boolean TRACE_ENABLED = false;
+
+ private final WeakReference<GLSurfaceView> mThisWeakRef =
+ new WeakReference<GLSurfaceView>(this);
+ private GLThread mGLThread;
+ private Renderer mRenderer;
+ private boolean mDetached;
+ private EGLConfigChooser mEGLConfigChooser;
+ private EGLContextFactory mEGLContextFactory;
+ private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
+ private GLWrapper mGLWrapper;
+ private int mDebugFlags;
+ private int mEGLContextClientVersion;
+ private boolean mPreserveEGLContextOnPause;
+ private int mUserRenderMode;
+
/**
* The renderer only renders
* when the surface is created, or when {@link #requestRender} is called.
@@ -241,13 +258,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
// underlying surface is created and destroyed
SurfaceHolder holder = getHolder();
holder.addCallback(this);
- // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment
- // this statement if back-porting to 2.2 or older:
- // holder.setFormat(PixelFormat.RGB_565);
- //
- // setType is not needed for SDK 2.0 or newer. Uncomment this
- // statement if back-porting this code to older SDKs.
- // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+ mUserRenderMode = RENDERMODE_CONTINUOUSLY;
}
/**
@@ -346,15 +357,16 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
public void setRenderer(Renderer renderer) {
checkRenderThreadState();
if (mEGLConfigChooser == null) {
- mEGLConfigChooser = new SimpleEGLConfigChooser(true);
+ mEGLConfigChooser = new SimpleEGLConfigChooser(true, mEGLContextClientVersion);
}
if (mEGLContextFactory == null) {
- mEGLContextFactory = new DefaultContextFactory();
+ mEGLContextFactory = new DefaultContextFactory(mEGLContextClientVersion);
}
if (mEGLWindowSurfaceFactory == null) {
mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
}
mRenderer = renderer;
+
mGLThread = new GLThread(mThisWeakRef);
mGLThread.start();
}
@@ -420,7 +432,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* @param needDepth
*/
public void setEGLConfigChooser(boolean needDepth) {
- setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
+ setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth, mEGLContextClientVersion));
}
/**
@@ -439,7 +451,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
int alphaSize, int depthSize, int stencilSize) {
setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
- blueSize, alphaSize, depthSize, stencilSize));
+ blueSize, alphaSize, depthSize, stencilSize, mEGLContextClientVersion));
}
/**
@@ -466,6 +478,13 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* If
* {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
* EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
+ *
+ * This method must be called before:
+ * <ul>
+ * <li>{@link #setEGLConfigChooser(boolean)}
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+ * </ul>
+ *
* @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
*/
public void setEGLContextClientVersion(int version) {
@@ -490,6 +509,14 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* @see #RENDERMODE_WHEN_DIRTY
*/
public void setRenderMode(int renderMode) {
+ switch (renderMode) {
+ case RENDERMODE_WHEN_DIRTY:
+ case RENDERMODE_CONTINUOUSLY:
+ break;
+ default:
+ throw new IllegalArgumentException("renderMode");
+ }
+ mUserRenderMode = renderMode;
mGLThread.setRenderMode(renderMode);
}
@@ -501,7 +528,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* @see #RENDERMODE_WHEN_DIRTY
*/
public int getRenderMode() {
- return mGLThread.getRenderMode();
+ return mUserRenderMode;
}
/**
@@ -582,14 +609,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
}
if (mDetached && (mRenderer != null)) {
- int renderMode = RENDERMODE_CONTINUOUSLY;
- if (mGLThread != null) {
- renderMode = mGLThread.getRenderMode();
- }
mGLThread = new GLThread(mThisWeakRef);
- if (renderMode != RENDERMODE_CONTINUOUSLY) {
- mGLThread.setRenderMode(renderMode);
- }
+ mGLThread.setRenderMode(mUserRenderMode);
mGLThread.start();
}
mDetached = false;
@@ -761,11 +782,15 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
}
- private class DefaultContextFactory implements EGLContextFactory {
- private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ private static class DefaultContextFactory implements EGLContextFactory {
+ private final int mEGLContextClientVersion;
+
+ public DefaultContextFactory(int version) {
+ mEGLContextClientVersion = version;
+ }
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
- int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
+ int[] attrib_list = {EGL14.EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
EGL10.EGL_NONE };
return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
@@ -775,9 +800,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
public void destroyContext(EGL10 egl, EGLDisplay display,
EGLContext context) {
if (!egl.eglDestroyContext(display, context)) {
- Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
+ Log.e(TAG, "display:" + display + " context: " + context);
if (LOG_THREADS) {
- Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "tid=" + Thread.currentThread().getId());
}
EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
}
@@ -807,8 +832,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
try {
result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
} catch (IllegalArgumentException e) {
- // This exception indicates that the surface flinger surface
- // is not valid. This can happen if the surface flinger surface has
+ // This exception indicates that the surfaceflinger surface
+ // is not valid. This can happen if the surfaceflinger surface has
// been torn down, but the application has not yet been
// notified via SurfaceHolder.Callback.surfaceDestroyed.
// In theory the application should be notified first,
@@ -844,10 +869,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
}
- private abstract class BaseConfigChooser
+ private static abstract class BaseConfigChooser
implements EGLConfigChooser {
- public BaseConfigChooser(int[] configSpec) {
- mConfigSpec = filterConfigSpec(configSpec);
+
+ public BaseConfigChooser(int[] configSpec, int version) {
+ mConfigSpec = filterConfigSpec(configSpec, version);
}
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
@@ -881,8 +907,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
protected int[] mConfigSpec;
- private int[] filterConfigSpec(int[] configSpec) {
- if (mEGLContextClientVersion != 2) {
+ private int[] filterConfigSpec(int[] configSpec, int version) {
+ if (version != 2) {
return configSpec;
}
/* We know none of the subclasses define EGL_RENDERABLE_TYPE.
@@ -892,7 +918,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
int[] newConfigSpec = new int[len + 2];
System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
- newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
+ newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT;
newConfigSpec[len+1] = EGL10.EGL_NONE;
return newConfigSpec;
}
@@ -902,9 +928,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* Choose a configuration with exactly the specified r,g,b,a sizes,
* and at least the specified depth and stencil sizes.
*/
- private class ComponentSizeChooser extends BaseConfigChooser {
+ private static class ComponentSizeChooser extends BaseConfigChooser {
public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
- int alphaSize, int depthSize, int stencilSize) {
+ int alphaSize, int depthSize, int stencilSize, int version) {
super(new int[] {
EGL10.EGL_RED_SIZE, redSize,
EGL10.EGL_GREEN_SIZE, greenSize,
@@ -912,7 +938,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
EGL10.EGL_ALPHA_SIZE, alphaSize,
EGL10.EGL_DEPTH_SIZE, depthSize,
EGL10.EGL_STENCIL_SIZE, stencilSize,
- EGL10.EGL_NONE});
+ EGL10.EGL_NONE}, version);
mValue = new int[1];
mRedSize = redSize;
mGreenSize = greenSize;
@@ -920,7 +946,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
mAlphaSize = alphaSize;
mDepthSize = depthSize;
mStencilSize = stencilSize;
- }
+ }
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
@@ -931,14 +957,10 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
int s = findConfigAttrib(egl, display, config,
EGL10.EGL_STENCIL_SIZE, 0);
if ((d >= mDepthSize) && (s >= mStencilSize)) {
- int r = findConfigAttrib(egl, display, config,
- EGL10.EGL_RED_SIZE, 0);
- int g = findConfigAttrib(egl, display, config,
- EGL10.EGL_GREEN_SIZE, 0);
- int b = findConfigAttrib(egl, display, config,
- EGL10.EGL_BLUE_SIZE, 0);
- int a = findConfigAttrib(egl, display, config,
- EGL10.EGL_ALPHA_SIZE, 0);
+ int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
if ((r == mRedSize) && (g == mGreenSize)
&& (b == mBlueSize) && (a == mAlphaSize)) {
return config;
@@ -965,16 +987,16 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
protected int mAlphaSize;
protected int mDepthSize;
protected int mStencilSize;
- }
+ }
/**
* This class will choose a RGB_888 surface with
* or without a depth buffer.
*
*/
- private class SimpleEGLConfigChooser extends ComponentSizeChooser {
- public SimpleEGLConfigChooser(boolean withDepthBuffer) {
- super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
+ private static class SimpleEGLConfigChooser extends ComponentSizeChooser {
+ public SimpleEGLConfigChooser(boolean withDepthBuffer, int version) {
+ super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0, version);
}
}
@@ -991,9 +1013,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* Initialize EGL for a given configuration spec.
* @param configSpec
*/
- public void start() {
+ public void initialize() {
if (LOG_EGL) {
- Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "initialize() tid=" + Thread.currentThread().getId());
}
/*
* Get an EGL instance
@@ -1034,7 +1056,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
throwEglException("createContext");
}
if (LOG_EGL) {
- Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
}
mEglSurface = null;
@@ -1048,7 +1070,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
*/
public boolean createSurface() {
if (LOG_EGL) {
- Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "createSurface() tid=" + Thread.currentThread().getId());
}
/*
* Check preconditions.
@@ -1083,7 +1105,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
- Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+ Log.e(TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
}
return false;
}
@@ -1097,8 +1119,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* Could not make the context current, probably because the underlying
* SurfaceView surface has been destroyed.
*/
- logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
- return false;
+ logEglErrorAsWarning(TAG, "eglMakeCurrent", mEgl.eglGetError());
+ // we fall-through to "true" here because we do have a
+ // valid EGLSurface at this point.
}
return true;
@@ -1108,8 +1131,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* Create a GL object for the current EGL context.
* @return
*/
- GL createGL() {
-
+ public GL createGL() {
GL gl = mEglContext.getGL();
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
@@ -1145,7 +1167,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
public void destroySurface() {
if (LOG_EGL) {
- Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "destroySurface() tid=" + Thread.currentThread().getId());
}
destroySurfaceImp();
}
@@ -1163,9 +1185,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
}
}
- public void finish() {
+ public void terminate() {
if (LOG_EGL) {
- Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "terminate() tid=" + Thread.currentThread().getId());
}
if (mEglContext != null) {
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
@@ -1187,7 +1209,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
public static void throwEglException(String function, int error) {
String message = formatEglError(function, error);
if (LOG_THREADS) {
- Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " "
+ Log.e(TAG, "throwEglException tid=" + Thread.currentThread().getId() + " "
+ message);
}
throw new RuntimeException(message);
@@ -1207,584 +1229,411 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
EGLSurface mEglSurface;
EGLConfig mEglConfig;
EGLContext mEglContext;
-
}
/**
* A generic GL Thread. Takes care of initializing EGL and GL. Delegates
* to a Renderer instance to do the actual drawing. Can be configured to
* render continuously or on request.
- *
- * All potentially blocking synchronization is done through the
- * sGLThreadManager object. This avoids multiple-lock ordering issues.
- *
*/
- static class GLThread extends Thread {
- GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
- super();
+
+ static class GLThread extends HandlerThread {
+ // only accessed from GLThread
+ private GL10 mGLContext;
+ private int mWidth;
+ private int mHeight;
+ private boolean mSizeChanged;
+ // current render mode
+ private int mRenderMode;
+ // the EGLSurface exists but isn't working for some reason
+ private boolean mEglSurfaceIsBad;
+ // we have an EGLContext
+ private boolean mHaveEglContext;
+ // we have an EGLSurface
+ private boolean mHaveEglSurface;
+ // we have a Surface (i.e.: EGLNativeWindowType)
+ private boolean mHasSurface;
+ // activity is paused
+ private boolean mPaused;
+
+ // constants
+ private EglHelper mEglHelper;
+ private Handler mGLHandler;
+ private Choreographer mChoreographer;
+
+ /*
+ * Set once at thread construction time, nulled out when the parent view is garbage
+ * called. This weak reference allows the GLSurfaceView to be garbage collected while
+ * the GLThread is still alive.
+ */
+ private final WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
+
+ private final Runnable mExecuteDrawAction = new Runnable() {
+ private int mTraceVsyncCounter = 0;
+ @Override
+ public void run() {
+ if (TRACE_ENABLED) {
+ Trace.traceCounter(Trace.TRACE_TAG_GRAPHICS,
+ "GLSurfaceView VSYNC counter", (mTraceVsyncCounter++) & 0xf);
+ }
+ executeDraw();
+ }
+ };
+
+ public GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
+ super("GLThread", android.os.Process.THREAD_PRIORITY_DISPLAY);
+ if (LOG_THREADS) {
+ Log.d(TAG, "*** Starting GLThread ***");
+ }
mWidth = 0;
mHeight = 0;
- mRequestRender = true;
mRenderMode = RENDERMODE_CONTINUOUSLY;
mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
}
+ private void readyToRun() {
+ mChoreographer = Choreographer.getInstance();
+ mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
+ }
+
@Override
- public void run() {
- setName("GLThread " + getId());
- if (LOG_THREADS) {
- Log.i("GLThread", "starting tid=" + getId());
- }
+ public void start() {
+ super.start();
+ // getLooper() blocks until the thread is running
+ Looper looper = getLooper();
+ mGLHandler = new Handler(looper);
+ // don't return until the GLThread state has been initialized
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ readyToRun();
+ }
+ }, 0);
+ }
+ @Override
+ public void run() {
try {
- guardedRun();
- } catch (InterruptedException e) {
- // fall thru and exit normally
+ super.run();
} finally {
- sGLThreadManager.threadExiting(this);
+ // by definition the GLThread is not running anymore here
+ stopEglContext();
+ stopEglSurface();
}
}
- /*
- * This private method should only be called inside a
- * synchronized(sGLThreadManager) block.
- */
- private void stopEglSurfaceLocked() {
+ // only call from the GLThread
+ private void stopEglSurface() {
if (mHaveEglSurface) {
+ if (LOG_SURFACE) {
+ Log.d(TAG, "releasing EGL surface because paused tid=" + getId());
+ }
mHaveEglSurface = false;
mEglHelper.destroySurface();
}
}
- /*
- * This private method should only be called inside a
- * synchronized(sGLThreadManager) block.
- */
- private void stopEglContextLocked() {
+ // only call from the GLThread
+ private void stopEglContext() {
if (mHaveEglContext) {
- mEglHelper.finish();
+ mEglHelper.terminate();
mHaveEglContext = false;
- sGLThreadManager.releaseEglContextLocked(this);
+ if (LOG_SURFACE) {
+ Log.d(TAG, "releasing EGL context because paused tid=" + getId());
+ }
}
}
- private void guardedRun() throws InterruptedException {
- mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
- mHaveEglContext = false;
- mHaveEglSurface = false;
- try {
- GL10 gl = null;
- boolean createEglContext = false;
- boolean createEglSurface = false;
- boolean createGlInterface = false;
- boolean lostEglContext = false;
- boolean sizeChanged = false;
- boolean wantRenderNotification = false;
- boolean doRenderNotification = false;
- boolean askedToReleaseEglContext = false;
- int w = 0;
- int h = 0;
- Runnable event = null;
-
- while (true) {
- synchronized (sGLThreadManager) {
- while (true) {
- if (mShouldExit) {
- return;
- }
-
- if (! mEventQueue.isEmpty()) {
- event = mEventQueue.remove(0);
- break;
- }
-
- // Update the pause state.
- boolean pausing = false;
- if (mPaused != mRequestPaused) {
- pausing = mRequestPaused;
- mPaused = mRequestPaused;
- sGLThreadManager.notifyAll();
- if (LOG_PAUSE_RESUME) {
- Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());
- }
- }
-
- // Do we need to give up the EGL context?
- if (mShouldReleaseEglContext) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "releasing EGL context because asked to tid=" + getId());
- }
- stopEglSurfaceLocked();
- stopEglContextLocked();
- mShouldReleaseEglContext = false;
- askedToReleaseEglContext = true;
- }
-
- // Have we lost the EGL context?
- if (lostEglContext) {
- stopEglSurfaceLocked();
- stopEglContextLocked();
- lostEglContext = false;
- }
- // When pausing, release the EGL surface:
- if (pausing && mHaveEglSurface) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
- }
- stopEglSurfaceLocked();
- }
-
- // When pausing, optionally release the EGL Context:
- if (pausing && mHaveEglContext) {
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- boolean preserveEglContextOnPause = view == null ?
- false : view.mPreserveEGLContextOnPause;
- if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) {
- stopEglContextLocked();
- if (LOG_SURFACE) {
- Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
- }
- }
- }
-
- // When pausing, optionally terminate EGL:
- if (pausing) {
- if (sGLThreadManager.shouldTerminateEGLWhenPausing()) {
- mEglHelper.finish();
- if (LOG_SURFACE) {
- Log.i("GLThread", "terminating EGL because paused tid=" + getId());
- }
- }
- }
-
- // Have we lost the SurfaceView surface?
- if ((! mHasSurface) && (! mWaitingForSurface)) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
- }
- if (mHaveEglSurface) {
- stopEglSurfaceLocked();
- }
- mWaitingForSurface = true;
- mSurfaceIsBad = false;
- sGLThreadManager.notifyAll();
- }
+ private void updateState() {
+ final boolean wasAbleToDraw = isAbleToDraw();
+ if (!isReadyToDraw()) {
+ return;
+ }
- // Have we acquired the surface view surface?
- if (mHasSurface && mWaitingForSurface) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());
- }
- mWaitingForSurface = false;
- sGLThreadManager.notifyAll();
- }
+ if (!mHaveEglSurface || mSizeChanged) {
+ // create EGL context if needed
+ boolean reportSurfaceCreated = false;
+ if (!mHaveEglContext) {
+ mEglHelper.initialize();
+ mHaveEglContext = true;
+ reportSurfaceCreated = true;
+ }
- if (doRenderNotification) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "sending render notification tid=" + getId());
- }
- wantRenderNotification = false;
- doRenderNotification = false;
- mRenderComplete = true;
- sGLThreadManager.notifyAll();
- }
+ // get the GL interface for the active EGLContext
+ mGLContext = (GL10)mEglHelper.createGL();
- // Ready to draw?
- if (readyToDraw()) {
-
- // If we don't have an EGL context, try to acquire one.
- if (! mHaveEglContext) {
- if (askedToReleaseEglContext) {
- askedToReleaseEglContext = false;
- } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) {
- try {
- mEglHelper.start();
- } catch (RuntimeException t) {
- sGLThreadManager.releaseEglContextLocked(this);
- throw t;
- }
- mHaveEglContext = true;
- createEglContext = true;
-
- sGLThreadManager.notifyAll();
- }
- }
-
- if (mHaveEglContext && !mHaveEglSurface) {
- mHaveEglSurface = true;
- createEglSurface = true;
- createGlInterface = true;
- sizeChanged = true;
- }
-
- if (mHaveEglSurface) {
- if (mSizeChanged) {
- sizeChanged = true;
- w = mWidth;
- h = mHeight;
- wantRenderNotification = true;
- if (LOG_SURFACE) {
- Log.i("GLThread",
- "noticing that we want render notification tid="
- + getId());
- }
-
- // Destroy and recreate the EGL surface.
- createEglSurface = true;
-
- mSizeChanged = false;
- }
- mRequestRender = false;
- sGLThreadManager.notifyAll();
- break;
- }
- }
+ // create EGL Surface
+ mHaveEglSurface = mEglHelper.createSurface();
+ mEglSurfaceIsBad = !mHaveEglSurface;
+ mSizeChanged = false;
- // By design, this is the only place in a GLThread thread where we wait().
- if (LOG_THREADS) {
- Log.i("GLThread", "waiting tid=" + getId()
- + " mHaveEglContext: " + mHaveEglContext
- + " mHaveEglSurface: " + mHaveEglSurface
- + " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface
- + " mPaused: " + mPaused
- + " mHasSurface: " + mHasSurface
- + " mSurfaceIsBad: " + mSurfaceIsBad
- + " mWaitingForSurface: " + mWaitingForSurface
- + " mWidth: " + mWidth
- + " mHeight: " + mHeight
- + " mRequestRender: " + mRequestRender
- + " mRenderMode: " + mRenderMode);
- }
- sGLThreadManager.wait();
+ // notify use of surface size change
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ if (reportSurfaceCreated) {
+ if (LOG_RENDERER) {
+ Log.d(TAG, "onSurfaceCreated");
}
- } // end of synchronized(sGLThreadManager)
-
- if (event != null) {
- event.run();
- event = null;
- continue;
+ view.mRenderer.onSurfaceCreated(mGLContext, mEglHelper.mEglConfig);
}
- if (createEglSurface) {
- if (LOG_SURFACE) {
- Log.w("GLThread", "egl createSurface");
- }
- if (mEglHelper.createSurface()) {
- synchronized(sGLThreadManager) {
- mFinishedCreatingEglSurface = true;
- sGLThreadManager.notifyAll();
- }
- } else {
- synchronized(sGLThreadManager) {
- mFinishedCreatingEglSurface = true;
- mSurfaceIsBad = true;
- sGLThreadManager.notifyAll();
- }
- continue;
- }
- createEglSurface = false;
+ if (LOG_RENDERER) {
+ Log.d(TAG, "onSurfaceChanged(" + mWidth + ", " + mHeight + ")");
}
+ view.mRenderer.onSurfaceChanged(mGLContext, mWidth, mHeight);
+ }
+ }
- if (createGlInterface) {
- gl = (GL10) mEglHelper.createGL();
+ // see if we should kick the rendering loop
+ if (!wasAbleToDraw && isAbleToDraw()) {
+ // we're now able to draw
+ if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
+ requestRender();
+ }
+ }
- sGLThreadManager.checkGLDriver(gl);
- createGlInterface = false;
- }
+ // By design, this is the only place in a GLThread thread where we wait().
+ if (LOG_THREADS) {
+ Log.d(TAG, "waiting tid=" + getId()
+ + " mHaveEglContext: " + mHaveEglContext
+ + " mHaveEglSurface: " + mHaveEglSurface
+ + " mPaused: " + mPaused
+ + " mHasSurface: " + mHasSurface
+ + " mSurfaceIsBad: " + mEglSurfaceIsBad
+ + " mWidth: " + mWidth
+ + " mHeight: " + mHeight
+ + " mRenderMode: " + mRenderMode);
+ }
+ }
- if (createEglContext) {
- if (LOG_RENDERER) {
- Log.w("GLThread", "onSurfaceCreated");
- }
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- if (view != null) {
- view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
- }
- createEglContext = false;
- }
+ private void executeDraw() {
+ if (TRACE_ENABLED) {
+ Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "executeDraw");
+ }
- if (sizeChanged) {
- if (LOG_RENDERER) {
- Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
- }
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- if (view != null) {
- view.mRenderer.onSurfaceChanged(gl, w, h);
- }
- sizeChanged = false;
- }
+ if (isAbleToDraw()) {
+ if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
+ requestRender();
+ }
- if (LOG_RENDERER_DRAW_FRAME) {
- Log.w("GLThread", "onDrawFrame tid=" + getId());
- }
- {
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- if (view != null) {
- view.mRenderer.onDrawFrame(gl);
- }
- }
+ if (LOG_RENDERER_DRAW_FRAME) {
+ Log.d(TAG, "onDrawFrame tid=" + getId());
+ }
+
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mRenderer.onDrawFrame(mGLContext);
int swapError = mEglHelper.swap();
switch (swapError) {
case EGL10.EGL_SUCCESS:
break;
case EGL11.EGL_CONTEXT_LOST:
if (LOG_SURFACE) {
- Log.i("GLThread", "egl context lost tid=" + getId());
+ Log.d(TAG, "egl context lost tid=" + getId());
}
- lostEglContext = true;
+ stopEglSurface();
+ stopEglContext();
break;
default:
// Other errors typically mean that the current surface is bad,
// probably because the SurfaceView surface has been destroyed,
// but we haven't been notified yet.
// Log the error to help developers understand why rendering stopped.
- EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);
-
- synchronized(sGLThreadManager) {
- mSurfaceIsBad = true;
- sGLThreadManager.notifyAll();
- }
+ EglHelper.logEglErrorAsWarning(TAG, "eglSwapBuffers", swapError);
+ mEglSurfaceIsBad = true;
break;
}
-
- if (wantRenderNotification) {
- doRenderNotification = true;
- }
}
+ }
- } finally {
- /*
- * clean-up everything...
- */
- synchronized (sGLThreadManager) {
- stopEglSurfaceLocked();
- stopEglContextLocked();
- }
+ if (TRACE_ENABLED) {
+ Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
}
}
- public boolean ableToDraw() {
- return mHaveEglContext && mHaveEglSurface && readyToDraw();
+ private boolean isAbleToDraw() {
+ return mHaveEglContext && mHaveEglSurface && isReadyToDraw();
}
- private boolean readyToDraw() {
- return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
- && (mWidth > 0) && (mHeight > 0)
- && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
+ private boolean isReadyToDraw() {
+ return (!mPaused) && mHasSurface && (!mEglSurfaceIsBad)
+ && (mWidth > 0) && (mHeight > 0);
}
- public void setRenderMode(int renderMode) {
- if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
- throw new IllegalArgumentException("renderMode");
+ private boolean isEglContextReleasedWhenPausing() {
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ return (view != null) ? !view.mPreserveEGLContextOnPause : false;
+ }
+
+ public void queueEvent(Runnable r) {
+ if (r == null) {
+ throw new IllegalArgumentException("Runnable r must not be null");
}
- synchronized(sGLThreadManager) {
- mRenderMode = renderMode;
- sGLThreadManager.notifyAll();
+ mGLHandler.post(r);
+ }
+
+ /*
+ * the call-backs below all run on the GLThread and implement state
+ * changes of the GLSurfaceView and Activity life cycle.
+ */
+
+ private void doSurfaceCreated() {
+ mHasSurface = true;
+ updateState();
+ }
+
+ private void doSurfaceDestroyed() {
+ if (mHasSurface) {
+ if (LOG_SURFACE) {
+ Log.d(TAG, "noticed surfaceView surface lost tid=" + getId());
+ }
+ stopEglSurface();
}
+ mHasSurface = false;
}
- public int getRenderMode() {
- synchronized(sGLThreadManager) {
- return mRenderMode;
+ private void doPause() {
+ if (mPaused == false) {
+ mPaused = true;
+ stopEglSurface();
+ // When pausing, optionally release the EGL Context:
+ if (mHaveEglContext && isEglContextReleasedWhenPausing()) {
+ stopEglContext();
+ }
}
}
- public void requestRender() {
- synchronized(sGLThreadManager) {
- mRequestRender = true;
- sGLThreadManager.notifyAll();
+ private void doResume() {
+ mPaused = false;
+ updateState();
+ if (mRenderMode == RENDERMODE_WHEN_DIRTY) {
+ requestRender();
}
}
+ private void doWindowResize(final int width, final int height) {
+ // we were not drawing yet. Update the window size and
+ // state and attempt to draw a frame.
+ mSizeChanged = (mWidth != width || mHeight != height);
+ mWidth = width;
+ mHeight = height;
+ updateState();
+ // we always (attempt to) draw a frame before returning
+ executeDraw();
+ }
+
+ private void doSetRenderMode(final int renderMode) {
+ mRenderMode = renderMode;
+ requestRender();
+ }
+
+ /*
+ * the call-backs below run on the main UI thread, they just
+ * wait while executing work on the GLThread.
+ */
+
public void surfaceCreated() {
- synchronized(sGLThreadManager) {
- if (LOG_THREADS) {
- Log.i("GLThread", "surfaceCreated tid=" + getId());
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doSurfaceCreated();
}
- mHasSurface = true;
- mFinishedCreatingEglSurface = false;
- sGLThreadManager.notifyAll();
- while (mWaitingForSurface
- && !mFinishedCreatingEglSurface
- && !mExited) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- }
+ }, 0);
}
public void surfaceDestroyed() {
- synchronized(sGLThreadManager) {
- if (LOG_THREADS) {
- Log.i("GLThread", "surfaceDestroyed tid=" + getId());
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doSurfaceDestroyed();
}
- mHasSurface = false;
- sGLThreadManager.notifyAll();
- while((!mWaitingForSurface) && (!mExited)) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- }
+ }, 0);
}
public void onPause() {
- synchronized (sGLThreadManager) {
- if (LOG_PAUSE_RESUME) {
- Log.i("GLThread", "onPause tid=" + getId());
- }
- mRequestPaused = true;
- sGLThreadManager.notifyAll();
- while ((! mExited) && (! mPaused)) {
- if (LOG_PAUSE_RESUME) {
- Log.i("Main thread", "onPause waiting for mPaused.");
- }
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doPause();
}
- }
+ }, 0);
}
public void onResume() {
- synchronized (sGLThreadManager) {
- if (LOG_PAUSE_RESUME) {
- Log.i("GLThread", "onResume tid=" + getId());
- }
- mRequestPaused = false;
- mRequestRender = true;
- mRenderComplete = false;
- sGLThreadManager.notifyAll();
- while ((! mExited) && mPaused && (!mRenderComplete)) {
- if (LOG_PAUSE_RESUME) {
- Log.i("Main thread", "onResume waiting for !mPaused.");
- }
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doResume();
}
- }
+ }, 0);
}
public void onWindowResize(int w, int h) {
- synchronized (sGLThreadManager) {
- mWidth = w;
- mHeight = h;
- mSizeChanged = true;
- mRequestRender = true;
- mRenderComplete = false;
- sGLThreadManager.notifyAll();
-
- // Wait for thread to react to resize and render a frame
- while (! mExited && !mPaused && !mRenderComplete
- && ableToDraw()) {
- if (LOG_SURFACE) {
- Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId());
- }
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
+ final int width = w;
+ final int height = h;
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doWindowResize(width, height);
}
- }
+ }, 0);
}
- public void requestExitAndWait() {
- // don't call this from GLThread thread or it is a guaranteed
- // deadlock!
- synchronized(sGLThreadManager) {
- mShouldExit = true;
- sGLThreadManager.notifyAll();
- while (! mExited) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
- }
+ /*
+ * the methods below can be called from any thread
+ */
+
+ public void requestRender() {
+ if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
+ mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
+ mExecuteDrawAction, null);
+ } else {
+ /*
+ * in RENDERMODE_WHEN_DIRTY we schedule the draw callback
+ * immediately because the developer is manager her
+ * timing loop manually -- in particular she could be
+ * using the Choreographer already.
+ */
+ mGLHandler.post(mExecuteDrawAction);
}
}
- public void requestReleaseEglContextLocked() {
- mShouldReleaseEglContext = true;
- sGLThreadManager.notifyAll();
+ public void setRenderMode(final int renderMode) {
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doSetRenderMode(renderMode);
+ }
+ }, 0);
}
- /**
- * Queue an "event" to be run on the GL rendering thread.
- * @param r the runnable to be run on the GL rendering thread.
- */
- public void queueEvent(Runnable r) {
- if (r == null) {
- throw new IllegalArgumentException("r must not be null");
- }
- synchronized(sGLThreadManager) {
- mEventQueue.add(r);
- sGLThreadManager.notifyAll();
+ public void requestExitAndWait() {
+ getLooper().quit();
+ try {
+ this.join();
+ } catch (InterruptedException e) {
}
}
-
- // Once the thread is started, all accesses to the following member
- // variables are protected by the sGLThreadManager monitor
- private boolean mShouldExit;
- private boolean mExited;
- private boolean mRequestPaused;
- private boolean mPaused;
- private boolean mHasSurface;
- private boolean mSurfaceIsBad;
- private boolean mWaitingForSurface;
- private boolean mHaveEglContext;
- private boolean mHaveEglSurface;
- private boolean mFinishedCreatingEglSurface;
- private boolean mShouldReleaseEglContext;
- private int mWidth;
- private int mHeight;
- private int mRenderMode;
- private boolean mRequestRender;
- private boolean mRenderComplete;
- private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
- private boolean mSizeChanged = true;
-
- // End of member variables protected by the sGLThreadManager monitor.
-
- private EglHelper mEglHelper;
-
- /**
- * Set once at thread construction time, nulled out when the parent view is garbage
- * called. This weak reference allows the GLSurfaceView to be garbage collected while
- * the GLThread is still alive.
- */
- private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
-
- }
+ } // class GLThread
static class LogWriter extends Writer {
-
- @Override public void close() {
+ @Override
+ public void close() {
flushBuilder();
}
- @Override public void flush() {
+ @Override
+ public void flush() {
flushBuilder();
}
- @Override public void write(char[] buf, int offset, int count) {
- for(int i = 0; i < count; i++) {
+ @Override
+ public void write(char[] buf, int offset, int count) {
+ for (int i = 0; i < count; i++) {
char c = buf[offset + i];
- if ( c == '\n') {
+ if (c == '\n') {
flushBuilder();
- }
- else {
+ } else {
mBuilder.append(c);
}
}
@@ -1792,7 +1641,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
private void flushBuilder() {
if (mBuilder.length() > 0) {
- Log.v("GLSurfaceView", mBuilder.toString());
+ Log.v(TAG, mBuilder.toString());
mBuilder.delete(0, mBuilder.length());
}
}
@@ -1800,141 +1649,10 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
private StringBuilder mBuilder = new StringBuilder();
}
-
private void checkRenderThreadState() {
if (mGLThread != null) {
throw new IllegalStateException(
"setRenderer has already been called for this instance.");
}
}
-
- private static class GLThreadManager {
- private static String TAG = "GLThreadManager";
-
- public synchronized void threadExiting(GLThread thread) {
- if (LOG_THREADS) {
- Log.i("GLThread", "exiting tid=" + thread.getId());
- }
- thread.mExited = true;
- if (mEglOwner == thread) {
- mEglOwner = null;
- }
- notifyAll();
- }
-
- /*
- * Tries once to acquire the right to use an EGL
- * context. Does not block. Requires that we are already
- * in the sGLThreadManager monitor when this is called.
- *
- * @return true if the right to use an EGL context was acquired.
- */
- public boolean tryAcquireEglContextLocked(GLThread thread) {
- if (mEglOwner == thread || mEglOwner == null) {
- mEglOwner = thread;
- notifyAll();
- return true;
- }
- checkGLESVersion();
- if (mMultipleGLESContextsAllowed) {
- return true;
- }
- // Notify the owning thread that it should release the context.
- // TODO: implement a fairness policy. Currently
- // if the owning thread is drawing continuously it will just
- // reacquire the EGL context.
- if (mEglOwner != null) {
- mEglOwner.requestReleaseEglContextLocked();
- }
- return false;
- }
-
- /*
- * Releases the EGL context. Requires that we are already in the
- * sGLThreadManager monitor when this is called.
- */
- public void releaseEglContextLocked(GLThread thread) {
- if (mEglOwner == thread) {
- mEglOwner = null;
- }
- notifyAll();
- }
-
- public synchronized boolean shouldReleaseEGLContextWhenPausing() {
- // Release the EGL context when pausing even if
- // the hardware supports multiple EGL contexts.
- // Otherwise the device could run out of EGL contexts.
- return mLimitedGLESContexts;
- }
-
- public synchronized boolean shouldTerminateEGLWhenPausing() {
- checkGLESVersion();
- return !mMultipleGLESContextsAllowed;
- }
-
- public synchronized void checkGLDriver(GL10 gl) {
- if (! mGLESDriverCheckComplete) {
- checkGLESVersion();
- String renderer = gl.glGetString(GL10.GL_RENDERER);
- if (mGLESVersion < kGLES_20) {
- mMultipleGLESContextsAllowed =
- ! renderer.startsWith(kMSM7K_RENDERER_PREFIX);
- notifyAll();
- }
- mLimitedGLESContexts = !mMultipleGLESContextsAllowed;
- if (LOG_SURFACE) {
- Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = "
- + mMultipleGLESContextsAllowed
- + " mLimitedGLESContexts = " + mLimitedGLESContexts);
- }
- mGLESDriverCheckComplete = true;
- }
- }
-
- private void checkGLESVersion() {
- if (! mGLESVersionCheckComplete) {
- mGLESVersion = SystemProperties.getInt(
- "ro.opengles.version",
- ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
- if (mGLESVersion >= kGLES_20) {
- mMultipleGLESContextsAllowed = true;
- }
- if (LOG_SURFACE) {
- Log.w(TAG, "checkGLESVersion mGLESVersion =" +
- " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed);
- }
- mGLESVersionCheckComplete = true;
- }
- }
-
- /**
- * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides
- * support for hardware-accelerated views, therefore multiple EGL contexts are
- * supported on all Android 3.0+ EGL drivers.
- */
- private boolean mGLESVersionCheckComplete;
- private int mGLESVersion;
- private boolean mGLESDriverCheckComplete;
- private boolean mMultipleGLESContextsAllowed;
- private boolean mLimitedGLESContexts;
- private static final int kGLES_20 = 0x20000;
- private static final String kMSM7K_RENDERER_PREFIX =
- "Q3Dimension MSM7500 ";
- private GLThread mEglOwner;
- }
-
- private static final GLThreadManager sGLThreadManager = new GLThreadManager();
-
- private final WeakReference<GLSurfaceView> mThisWeakRef =
- new WeakReference<GLSurfaceView>(this);
- private GLThread mGLThread;
- private Renderer mRenderer;
- private boolean mDetached;
- private EGLConfigChooser mEGLConfigChooser;
- private EGLContextFactory mEGLContextFactory;
- private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
- private GLWrapper mGLWrapper;
- private int mDebugFlags;
- private int mEGLContextClientVersion;
- private boolean mPreserveEGLContextOnPause;
}
diff --git a/packages/DefaultContainerService/jni/Android.mk b/packages/DefaultContainerService/jni/Android.mk
index 79ff451..ef4f699 100644
--- a/packages/DefaultContainerService/jni/Android.mk
+++ b/packages/DefaultContainerService/jni/Android.mk
@@ -28,7 +28,8 @@ LOCAL_C_INCLUDES += \
LOCAL_SHARED_LIBRARIES := \
libnativehelper \
- libutils
+ libutils \
+ liblog
LOCAL_STATIC_LIBRARIES := \
libdiskusage
@@ -36,4 +37,4 @@ LOCAL_STATIC_LIBRARIES := \
LOCAL_MODULE := libdefcontainer_jni
LOCAL_MODULE_TAGS := optional
-include $(BUILD_SHARED_LIBRARY) \ No newline at end of file
+include $(BUILD_SHARED_LIBRARY)
diff --git a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java
index f11b499..36ce7ce 100644
--- a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java
+++ b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java
@@ -86,7 +86,7 @@ public class FakeApp extends Application {
@Override
public void onCreate() {
- String processName = ActivityThread.currentPackageName();
+ String processName = ActivityThread.currentProcessName();
Slog.i("FakeOEMFeatures", "Creating app in process: " + processName);
if (!getApplicationInfo().packageName.equals(processName)) {
// If we are not in the main process of the app, then don't do
diff --git a/packages/SystemUI/src/com/android/systemui/UniverseBackground.java b/packages/SystemUI/src/com/android/systemui/UniverseBackground.java
index 7628754..f859880 100644
--- a/packages/SystemUI/src/com/android/systemui/UniverseBackground.java
+++ b/packages/SystemUI/src/com/android/systemui/UniverseBackground.java
@@ -97,7 +97,7 @@ public class UniverseBackground extends FrameLayout {
public UniverseBackground(Context context) {
super(context);
setBackgroundColor(0xff000000);
- mSession = WindowManagerGlobal.getWindowSession(context.getMainLooper());
+ mSession = WindowManagerGlobal.getWindowSession();
mContent = View.inflate(context, R.layout.universe, null);
addView(mContent);
mContent.findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/FirstFrameAnimatorHelper.java b/packages/SystemUI/src/com/android/systemui/recent/FirstFrameAnimatorHelper.java
new file mode 100644
index 0000000..2fc7dfc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/FirstFrameAnimatorHelper.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recent;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.animation.Animator.AnimatorListener;
+import android.util.Log;
+import android.view.ViewTreeObserver;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+
+/*
+ * This is a helper class that listens to updates from the corresponding animation.
+ * For the first two frames, it adjusts the current play time of the animation to
+ * prevent jank at the beginning of the animation
+ */
+public class FirstFrameAnimatorHelper implements ValueAnimator.AnimatorUpdateListener {
+ private static final boolean DEBUG = false;
+ private static final int MAX_DELAY = 1000;
+ private static final int IDEAL_FRAME_DURATION = 16;
+ private View mTarget;
+ private long mStartFrame;
+ private long mStartTime = -1;
+ private boolean mHandlingOnAnimationUpdate;
+ private boolean mAdjustedSecondFrameTime;
+
+ private static ViewTreeObserver.OnDrawListener sGlobalDrawListener;
+ private static long sGlobalFrameCounter;
+
+ public FirstFrameAnimatorHelper(ValueAnimator animator, View target) {
+ mTarget = target;
+ animator.addUpdateListener(this);
+ }
+
+ public FirstFrameAnimatorHelper(ViewPropertyAnimator vpa, View target) {
+ mTarget = target;
+ vpa.setListener(new AnimatorListenerAdapter() {
+ public void onAnimationStart (Animator animation) {
+ final ValueAnimator va = (ValueAnimator) animation;
+ va.addUpdateListener(FirstFrameAnimatorHelper.this);
+ onAnimationUpdate(va);
+ }
+ });
+ }
+
+ public static void initializeDrawListener(View view) {
+ if (sGlobalDrawListener != null) {
+ view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener);
+ }
+ sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() {
+ private long mTime = System.currentTimeMillis();
+ public void onDraw() {
+ sGlobalFrameCounter++;
+ if (DEBUG) {
+ long newTime = System.currentTimeMillis();
+ Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime));
+ mTime = newTime;
+ }
+ }
+ };
+ view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener);
+ }
+
+ public void onAnimationUpdate(final ValueAnimator animation) {
+ final long currentTime = System.currentTimeMillis();
+ if (mStartTime == -1) {
+ mStartFrame = sGlobalFrameCounter;
+ mStartTime = currentTime;
+ }
+
+ if (!mHandlingOnAnimationUpdate) {
+ mHandlingOnAnimationUpdate = true;
+ long frameNum = sGlobalFrameCounter - mStartFrame;
+ // If we haven't drawn our first frame, reset the time to t = 0
+ // (give up after MAX_DELAY ms of waiting though - might happen, for example, if we
+ // are no longer in the foreground and no frames are being rendered ever)
+ if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY) {
+ // The first frame on animations doesn't always trigger an invalidate...
+ // force an invalidate here to make sure the animation continues to advance
+ mTarget.getRootView().invalidate();
+ animation.setCurrentPlayTime(0);
+
+ // For the second frame, if the first frame took more than 16ms,
+ // adjust the start time and pretend it took only 16ms anyway. This
+ // prevents a large jump in the animation due to an expensive first frame
+ } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
+ !mAdjustedSecondFrameTime &&
+ currentTime > mStartTime + IDEAL_FRAME_DURATION) {
+ animation.setCurrentPlayTime(IDEAL_FRAME_DURATION);
+ mAdjustedSecondFrameTime = true;
+ } else {
+ if (frameNum > 1) {
+ mTarget.post(new Runnable() {
+ public void run() {
+ animation.removeUpdateListener(FirstFrameAnimatorHelper.this);
+ }
+ });
+ }
+ if (DEBUG) print(animation);
+ }
+ mHandlingOnAnimationUpdate = false;
+ } else {
+ if (DEBUG) print(animation);
+ }
+ }
+
+ public void print(ValueAnimator animation) {
+ float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration();
+ Log.d("FirstFrameAnimatorHelper", sGlobalFrameCounter +
+ "(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " +
+ mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 9c2bca9..32759de 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -45,8 +45,7 @@ import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.ViewPropertyAnimator;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
@@ -195,50 +194,27 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener
oldHolder.calloutLine.setTranslationY(0f);
}
}
- mItemToAnimateInWhenWindowAnimationIsFinished = null;
-
- final ViewTreeObserver observer = getViewTreeObserver();
- final OnGlobalLayoutListener animateFirstIcon = new OnGlobalLayoutListener() {
- public void onGlobalLayout() {
- ViewHolder oldHolder = mItemToAnimateInWhenWindowAnimationIsFinished;
- if (oldHolder != null) {
- oldHolder.iconView.setAlpha(1f);
- oldHolder.iconView.setTranslationX(0f);
- oldHolder.iconView.setTranslationY(0f);
- oldHolder.labelView.setAlpha(1f);
- oldHolder.labelView.setTranslationX(0f);
- oldHolder.labelView.setTranslationY(0f);
- if (oldHolder.calloutLine != null) {
- oldHolder.calloutLine.setAlpha(1f);
- oldHolder.calloutLine.setTranslationX(0f);
- oldHolder.calloutLine.setTranslationY(0f);
- }
- }
- mItemToAnimateInWhenWindowAnimationIsFinished = holder;
- int translation = -getResources().getDimensionPixelSize(
- R.dimen.status_bar_recents_app_icon_translate_distance);
- final Configuration config = getResources().getConfiguration();
- if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
- if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
- translation = -translation;
- }
- holder.iconView.setAlpha(0f);
- holder.iconView.setTranslationX(translation);
- holder.labelView.setAlpha(0f);
- holder.labelView.setTranslationX(translation);
- holder.calloutLine.setAlpha(0f);
- holder.calloutLine.setTranslationX(translation);
- } else {
- holder.iconView.setAlpha(0f);
- holder.iconView.setTranslationY(translation);
- }
- if (!mWaitingForWindowAnimation) {
- animateInIconOfFirstTask();
- }
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ mItemToAnimateInWhenWindowAnimationIsFinished = holder;
+ int translation = -getResources().getDimensionPixelSize(
+ R.dimen.status_bar_recents_app_icon_translate_distance);
+ final Configuration config = getResources().getConfiguration();
+ if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ translation = -translation;
}
- };
- observer.addOnGlobalLayoutListener(animateFirstIcon);
+ holder.iconView.setAlpha(0f);
+ holder.iconView.setTranslationX(translation);
+ holder.labelView.setAlpha(0f);
+ holder.labelView.setTranslationX(translation);
+ holder.calloutLine.setAlpha(0f);
+ holder.calloutLine.setTranslationX(translation);
+ } else {
+ holder.iconView.setAlpha(0f);
+ holder.iconView.setTranslationY(translation);
+ }
+ if (!mWaitingForWindowAnimation) {
+ animateInIconOfFirstTask();
+ }
}
}
@@ -586,17 +562,20 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener
!mRecentTasksLoader.isFirstScreenful()) {
int timeSinceWindowAnimation =
(int) (System.currentTimeMillis() - mWindowAnimationStartTime);
- final int minStartDelay = 150;
+ final int minStartDelay = 125;
final int startDelay = Math.max(0, Math.min(
minStartDelay - timeSinceWindowAnimation, minStartDelay));
final int duration = 250;
final ViewHolder holder = mItemToAnimateInWhenWindowAnimationIsFinished;
final TimeInterpolator cubic = new DecelerateInterpolator(1.5f);
+ FirstFrameAnimatorHelper.initializeDrawListener(holder.iconView);
for (View v :
new View[] { holder.iconView, holder.labelView, holder.calloutLine }) {
if (v != null) {
- v.animate().translationX(0).translationY(0).alpha(1f).setStartDelay(startDelay)
+ ViewPropertyAnimator vpa = v.animate().translationX(0).translationY(0)
+ .alpha(1f).setStartDelay(startDelay)
.setDuration(duration).setInterpolator(cubic);
+ FirstFrameAnimatorHelper h = new FirstFrameAnimatorHelper(vpa, v);
}
}
mItemToAnimateInWhenWindowAnimationIsFinished = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 7bdcf6e..5b911c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -17,11 +17,11 @@
package com.android.systemui.statusbar;
+import android.service.notification.StatusBarNotification;
import android.content.res.Configuration;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.internal.widget.SizeAdaptiveLayout;
import com.android.systemui.R;
import com.android.systemui.SearchPanelView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 752bb0c..cbbaab3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -20,10 +20,10 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.service.notification.StatusBarNotification;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
-import com.android.internal.statusbar.StatusBarNotification;
/**
* This class takes the functions from IStatusBar that come in on
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index c82f250..886ed77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -16,12 +16,11 @@
package com.android.systemui.statusbar;
-import android.app.Notification;
+import android.service.notification.StatusBarNotification;
import android.os.IBinder;
import android.view.View;
import android.widget.ImageView;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.R;
import java.util.Comparator;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 9f54573..d98f08e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -26,6 +26,7 @@ import android.app.ActivityManagerNative;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.service.notification.StatusBarNotification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -76,7 +77,6 @@ import android.widget.ScrollView;
import android.widget.TextView;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -105,7 +105,7 @@ public class PhoneStatusBar extends BaseStatusBar {
public static final boolean DEBUG = BaseStatusBar.DEBUG;
public static final boolean SPEW = DEBUG;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
- public static final boolean DEBUG_GESTURES = true;
+ public static final boolean DEBUG_GESTURES = false;
public static final boolean DEBUG_CLINGS = false;
@@ -708,6 +708,7 @@ public class PhoneStatusBar extends BaseStatusBar {
private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
public void onClick(View v) {
+ awakenDreams();
toggleRecentApps();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
index ecc70d6..976dd01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.service.notification.StatusBarNotification;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
@@ -23,10 +24,7 @@ import android.os.Handler;
import android.text.StaticLayout;
import android.text.Layout.Alignment;
import android.text.TextPaint;
-import android.text.TextUtils;
-import android.util.Slog;
import android.view.View;
-import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.TextSwitcher;
import android.widget.TextView;
@@ -35,7 +33,6 @@ import android.widget.ImageSwitcher;
import java.util.ArrayList;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.internal.util.CharSequences;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index 0944b40..68d048d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -28,16 +28,11 @@ import android.content.IntentFilter;
import android.location.LocationManager;
import android.os.UserHandle;
import android.provider.Settings;
-import android.util.Slog;
-import android.view.View;
-import android.widget.ImageView;
// private NM API
import android.app.INotificationManager;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
public class LocationController extends BroadcastReceiver {
private static final String TAG = "StatusBar.LocationController";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 3d6bfe7..05bba89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -23,6 +23,7 @@ import android.app.ActivityManagerNative;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.service.notification.StatusBarNotification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -58,7 +59,6 @@ import android.widget.ScrollView;
import android.widget.TextView;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.CommandQueue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
index 0859874..725d9e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
@@ -21,9 +21,9 @@ import java.util.Arrays;
import android.animation.LayoutTransition;
import android.app.Notification;
import android.app.PendingIntent;
+import android.service.notification.StatusBarNotification;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -37,11 +37,9 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.R;
import com.android.systemui.statusbar.StatusBarIconView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 413cc78..dc5de02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.tv;
+import android.service.notification.StatusBarNotification;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.statusbar.BaseStatusBar;
import android.os.IBinder;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 49460de..273ac31 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -59,6 +59,8 @@ import android.os.UEventObserver;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -1801,7 +1803,23 @@ public class PhoneWindowManager implements WindowManagerPolicy {
? com.android.internal.R.anim.lock_screen_wallpaper_behind_enter
: com.android.internal.R.anim.lock_screen_behind_enter);
}
-
+
+ private static void awakenDreams() {
+ IDreamManager dreamManager = getDreamManager();
+ if (dreamManager != null) {
+ try {
+ dreamManager.awaken();
+ } catch (RemoteException e) {
+ // fine, stay asleep then
+ }
+ }
+ }
+
+ static IDreamManager getDreamManager() {
+ return IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE));
+ }
+
static ITelephony getTelephonyService() {
return ITelephony.Stub.asInterface(
ServiceManager.checkService(Context.TELEPHONY_SERVICE));
@@ -4185,9 +4203,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Ignore sensor when plugged into HDMI.
// Note that the dock orientation overrides the HDMI orientation.
preferredRotation = mHdmiRotation;
+ } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+ // Application just wants to remain locked in the last rotation.
+ preferredRotation = lastRotation;
} else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
&& (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
- || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED))
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
@@ -4203,7 +4227,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
if (sensorRotation != Surface.ROTATION_180
|| mAllowAllRotations == 1
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR) {
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
preferredRotation = sensorRotation;
} else {
preferredRotation = lastRotation;
@@ -4251,6 +4276,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return mSeascapeRotation;
case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
// Return either landscape rotation.
if (isLandscapeOrSeascape(preferredRotation)) {
return preferredRotation;
@@ -4261,6 +4287,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return mLandscapeRotation;
case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
// Return either portrait rotation.
if (isAnyPortrait(preferredRotation)) {
return preferredRotation;
@@ -4550,6 +4577,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
void startDockOrHome() {
+ awakenDreams();
// We don't have dock home anymore. Home is home. If you lived here, you'd be home by now.
mContext.startActivityAsUser(mHomeIntent, UserHandle.CURRENT);
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
index 965e378..7315aad 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
@@ -151,7 +151,9 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu
public void onResume(int reason) {
if (DEBUG) Log.d(TAG, "onResume()");
mIsShowing = KeyguardUpdateMonitor.getInstance(mContext).isKeyguardVisible();
- maybeStartBiometricUnlock();
+ if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
+ maybeStartBiometricUnlock();
+ }
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
// Registers a callback which handles stopping the biometric unlock and restarting it in
@@ -269,6 +271,14 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu
}
@Override
+ public void onUserSwitchComplete(int userId) {
+ if (DEBUG) Log.d(TAG, "onUserSwitchComplete(" + userId + ")");
+ if (mBiometricUnlock != null) {
+ maybeStartBiometricUnlock();
+ }
+ }
+
+ @Override
public void onKeyguardVisibilityChanged(boolean showing) {
if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")");
boolean wasShowing = false;
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 4885407..7fcf1d5 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -64,7 +64,7 @@ import java.util.List;
public class KeyguardHostView extends KeyguardViewBase {
private static final String TAG = "KeyguardHostView";
- // transport control states
+ // Transport control states.
static final int TRANSPORT_GONE = 0;
static final int TRANSPORT_INVISIBLE = 1;
static final int TRANSPORT_VISIBLE = 2;
@@ -139,6 +139,9 @@ public class KeyguardHostView extends KeyguardViewBase {
public KeyguardHostView(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ if (DEBUG) Log.e(TAG, "KeyguardHostView()");
+
mLockPatternUtils = new LockPatternUtils(context);
// Note: This depends on KeyguardHostView getting reconstructed every time the
@@ -203,6 +206,9 @@ public class KeyguardHostView extends KeyguardViewBase {
mTransportState = (dcs.clearing ? TRANSPORT_GONE :
(isMusicPlaying(dcs.playbackState) ? TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
mPlaybackState = dcs.playbackState;
+
+ if (DEBUG) Log.v(TAG, "Initial transport state: "
+ + mTransportState + ", pbstate=" + mPlaybackState);
}
private void cleanupAppWidgetIds() {
@@ -1205,7 +1211,7 @@ public class KeyguardHostView extends KeyguardViewBase {
* Create KeyguardTransportControlView on demand.
* @return
*/
- private KeyguardTransportControlView getTransportControlView() {
+ private KeyguardTransportControlView getOrCreateTransportControl() {
if (mTransportControl == null) {
LayoutInflater inflater = LayoutInflater.from(mContext);
mTransportControl = (KeyguardTransportControlView)
@@ -1380,17 +1386,19 @@ public class KeyguardHostView extends KeyguardViewBase {
@Override
public Parcelable onSaveInstanceState() {
- if (DEBUG) Log.d(TAG, "onSaveInstanceState");
+ if (DEBUG) Log.d(TAG, "onSaveInstanceState, tstate=" + mTransportState);
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
- ss.transportState = mTransportState;
+ // If the transport is showing, force it to show it on restore.
+ final boolean showing = mTransportControl != null
+ && mAppWidgetContainer.getWidgetPageIndex(mTransportControl) >= 0;
+ ss.transportState = showing ? TRANSPORT_VISIBLE : mTransportState;
ss.appWidgetToShow = mAppWidgetToShow;
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
- if (DEBUG) Log.d(TAG, "onRestoreInstanceState");
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
@@ -1399,6 +1407,7 @@ public class KeyguardHostView extends KeyguardViewBase {
super.onRestoreInstanceState(ss.getSuperState());
mTransportState = (ss.transportState);
mAppWidgetToShow = ss.appWidgetToShow;
+ if (DEBUG) Log.d(TAG, "onRestoreInstanceState, transport=" + mTransportState);
post(mSwitchPageRunnable);
}
@@ -1424,23 +1433,44 @@ public class KeyguardHostView extends KeyguardViewBase {
mAppWidgetContainer.setCurrentPage(pageToShow);
}
+ /**
+ * Examines the current state and adds the transport to the widget pager when the state changes.
+ *
+ * Showing the initial transport and keeping it around is a bit tricky because the signals
+ * coming from music players aren't always clear. Here's how the states are handled:
+ *
+ * {@link TRANSPORT_GONE} means we have no reason to show the transport - remove it if present.
+ *
+ * {@link TRANSPORT_INVISIBLE} means we have potential to show the transport because a music
+ * player is registered but not currently playing music (or we don't know the state yet). The
+ * code adds it conditionally on play state.
+ *
+ * {@link #TRANSPORT_VISIBLE} means a music player is active and transport should be showing.
+ *
+ * Once the transport is showing, we always show it until keyguard is dismissed. This state is
+ * maintained by onSave/RestoreInstanceState(). This state is cleared in
+ * {@link KeyguardViewManager#hide} when keyguard is dismissed, which causes the transport to be
+ * gone when keyguard is restarted until we get an update with the current state.
+ *
+ * @param state
+ */
private void ensureTransportPresentOrRemoved(int state) {
- int page = getWidgetPosition(R.id.keyguard_transport_control);
- if (state == TRANSPORT_INVISIBLE || state == TRANSPORT_VISIBLE) {
- if (page == -1) {
- if (DEBUGXPORT) Log.v(TAG, "add transport");
- // insert to left of camera if it exists, otherwise after right-most widget
- int lastWidget = mAppWidgetContainer.getChildCount() - 1;
- int position = 0; // handle no widget case
- if (lastWidget >= 0) {
- position = mAppWidgetContainer.isCameraPage(lastWidget) ?
- lastWidget : lastWidget + 1;
- }
- mAppWidgetContainer.addWidget(getTransportControlView(), position);
+ final boolean showing = getWidgetPosition(R.id.keyguard_transport_control) != -1;
+ final boolean visible = state == TRANSPORT_VISIBLE;
+ final boolean shouldBeVisible = state == TRANSPORT_INVISIBLE && isMusicPlaying(state);
+ if (!showing && (visible || shouldBeVisible)) {
+ if (DEBUGXPORT) Log.v(TAG, "add transport");
+ // insert to left of camera if it exists, otherwise after right-most widget
+ int lastWidget = mAppWidgetContainer.getChildCount() - 1;
+ int position = 0; // handle no widget case
+ if (lastWidget >= 0) {
+ position = mAppWidgetContainer.isCameraPage(lastWidget) ?
+ lastWidget : lastWidget + 1;
}
- } else if (page != -1) {
+ mAppWidgetContainer.addWidget(getOrCreateTransportControl(), position);
+ } else if (showing && state == TRANSPORT_GONE) {
if (DEBUGXPORT) Log.v(TAG, "remove transport");
- mAppWidgetContainer.removeWidget(getTransportControlView());
+ mAppWidgetContainer.removeWidget(getOrCreateTransportControl());
mTransportControl = null;
}
}
@@ -1473,7 +1503,7 @@ public class KeyguardHostView extends KeyguardViewBase {
// if music playing, show transport
if (musicTransportState == TRANSPORT_VISIBLE) {
if (DEBUG) Log.d(TAG, "Music playing, show transport");
- return mAppWidgetContainer.getWidgetPageIndex(getTransportControlView());
+ return mAppWidgetContainer.getWidgetPageIndex(getOrCreateTransportControl());
}
// else show the right-most widget (except for camera)
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
index 159a92d..986dc49 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -124,6 +124,8 @@ public class KeyguardUpdateMonitor {
mCallbacks = Lists.newArrayList();
private ContentObserver mDeviceProvisionedObserver;
+ private boolean mSwitchingUser;
+
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -392,6 +394,8 @@ public class KeyguardUpdateMonitor {
mDisplayClientState.clientGeneration = clientGeneration;
mDisplayClientState.clearing = clearing;
mDisplayClientState.intent = p;
+ if (DEBUG)
+ Log.v(TAG, "handleSetGenerationId(g=" + clientGeneration + ", clear=" + clearing + ")");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -401,6 +405,11 @@ public class KeyguardUpdateMonitor {
}
protected void handleSetPlaybackState(int generationId, int playbackState, long eventTime) {
+ if (DEBUG)
+ Log.v(TAG, "handleSetPlaybackState(gen=" + generationId
+ + ", state=" + playbackState + ", t=" + eventTime + ")");
+ mDisplayClientState.playbackState = playbackState;
+ mDisplayClientState.playbackEventTime = eventTime;
if (generationId == mDisplayClientState.clientGeneration) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -454,11 +463,13 @@ public class KeyguardUpdateMonitor {
public void onUserSwitching(int newUserId, IRemoteCallback reply) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING,
newUserId, 0, reply));
+ mSwitchingUser = true;
}
@Override
public void onUserSwitchComplete(int newUserId) throws RemoteException {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCH_COMPLETE,
newUserId));
+ mSwitchingUser = false;
}
});
} catch (RemoteException e) {
@@ -522,7 +533,6 @@ public class KeyguardUpdateMonitor {
cb.onUserSwitching(userId);
}
}
- setAlternateUnlockEnabled(false);
try {
reply.sendResult(null);
} catch (RemoteException e) {
@@ -726,6 +736,10 @@ public class KeyguardUpdateMonitor {
return mKeyguardIsVisible;
}
+ public boolean isSwitchingUser() {
+ return mSwitchingUser;
+ }
+
private static boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
final boolean nowPluggedIn = current.isPluggedIn();
final boolean wasPluggedIn = old.isPluggedIn();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index 8e10528..08a95a6 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -319,8 +319,9 @@ public class KeyguardViewMediator {
mSwitchingUser = true;
resetStateLocked(null);
adjustStatusBarLocked();
- // Disable face unlock when the user switches.
- KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false);
+ // When we switch users we want to bring the new user to the biometric unlock even
+ // if the current user has gone to the backup.
+ KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
}
}
@@ -915,7 +916,7 @@ public class KeyguardViewMediator {
* @see #handleReset()
*/
private void resetStateLocked(Bundle options) {
- if (DEBUG) Log.d(TAG, "resetStateLocked");
+ if (DEBUG) Log.e(TAG, "resetStateLocked");
Message msg = mHandler.obtainMessage(RESET, options);
mHandler.sendMessage(msg);
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index ad5e257..1bcee4e 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -594,13 +594,12 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit
animateOutlinesAndSidePages(false);
}
- public void showInitialPageHints() {
- mShowingInitialHints = true;
+ void updateChildrenContentAlpha(float sidePageAlpha) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
KeyguardWidgetFrame child = getWidgetPageAt(i);
if (i != mCurrentPage) {
- child.setBackgroundAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
+ child.setBackgroundAlpha(sidePageAlpha);
child.setContentAlpha(0f);
} else {
child.setBackgroundAlpha(0f);
@@ -609,9 +608,15 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit
}
}
+ public void showInitialPageHints() {
+ mShowingInitialHints = true;
+ updateChildrenContentAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
+ }
+
@Override
void setCurrentPage(int currentPage) {
super.setCurrentPage(currentPage);
+ updateChildrenContentAlpha(0.0f);
updateWidgetFramesImportantForAccessibility();
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
index 539ec1a..186a013 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
@@ -756,6 +756,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
@Override
public void onChildViewRemoved(View parent, View child) {
mForceScreenScrolled = true;
+ invalidate();
+ invalidateCachedOffsets();
}
protected void invalidateCachedOffsets() {
diff --git a/services/common_time/Android.mk b/services/common_time/Android.mk
index 0606ab4..75eb528 100644
--- a/services/common_time/Android.mk
+++ b/services/common_time/Android.mk
@@ -27,7 +27,8 @@ endif
LOCAL_SHARED_LIBRARIES := \
libbinder \
libcommon_time_client \
- libutils
+ libutils \
+ liblog
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := common_time
diff --git a/services/input/Android.mk b/services/input/Android.mk
index 159800f..5d913f3 100644
--- a/services/input/Android.mk
+++ b/services/input/Android.mk
@@ -29,6 +29,7 @@ LOCAL_SRC_FILES:= \
LOCAL_SHARED_LIBRARIES := \
libcutils \
+ liblog \
libandroidfw \
libutils \
libhardware \
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 0773afb..376de96 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -40,7 +40,6 @@
#include <androidfw/KeyCharacterMap.h>
#include <androidfw/VirtualKeyMap.h>
-#include <sha1.h>
#include <string.h>
#include <stdint.h>
#include <dirent.h>
@@ -49,6 +48,7 @@
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/limits.h>
+#include <sys/sha1.h>
/* this macro is used to tell if "bit" is set in "array"
* it selects a byte from the array, and does a boolean AND
@@ -162,7 +162,8 @@ EventHub::Device::Device(int fd, int32_t id, const String8& path,
next(NULL),
fd(fd), id(id), path(path), identifier(identifier),
classes(0), configuration(NULL), virtualKeyMap(NULL),
- ffEffectPlaying(false), ffEffectId(-1) {
+ ffEffectPlaying(false), ffEffectId(-1),
+ timestampOverrideSec(0), timestampOverrideUsec(0) {
memset(keyBitmask, 0, sizeof(keyBitmask));
memset(absBitmask, 0, sizeof(absBitmask));
memset(relBitmask, 0, sizeof(relBitmask));
@@ -766,12 +767,37 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
- const struct input_event& iev = readBuffer[i];
- ALOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",
+ struct input_event& iev = readBuffer[i];
+ ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
device->path.string(),
(int) iev.time.tv_sec, (int) iev.time.tv_usec,
iev.type, iev.code, iev.value);
+ // Some input devices may have a better concept of the time
+ // when an input event was actually generated than the kernel
+ // which simply timestamps all events on entry to evdev.
+ // This is a custom Android extension of the input protocol
+ // mainly intended for use with uinput based device drivers.
+ if (iev.type == EV_MSC) {
+ if (iev.code == MSC_ANDROID_TIME_SEC) {
+ device->timestampOverrideSec = iev.value;
+ continue;
+ } else if (iev.code == MSC_ANDROID_TIME_USEC) {
+ device->timestampOverrideUsec = iev.value;
+ continue;
+ }
+ }
+ if (device->timestampOverrideSec || device->timestampOverrideUsec) {
+ iev.time.tv_sec = device->timestampOverrideSec;
+ iev.time.tv_usec = device->timestampOverrideUsec;
+ if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
+ device->timestampOverrideSec = 0;
+ device->timestampOverrideUsec = 0;
+ }
+ ALOGV("applied override time %d.%06d",
+ int(iev.time.tv_sec), int(iev.time.tv_usec));
+ }
+
#ifdef HAVE_POSIX_CLOCKS
// Use the time specified in the event instead of the current time
// so that downstream code can get more accurate estimates of
@@ -829,8 +855,8 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
event->code = iev.code;
event->value = iev.value;
event += 1;
+ capacity -= 1;
}
- capacity -= count;
if (capacity == 0) {
// The result buffer is full. Reset the pending event index
// so we will try to read the device again on the next iteration.
@@ -1162,11 +1188,6 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
mBuiltInKeyboardId = device->id;
}
- // 'Q' key support = cheap test of whether this is an alpha-capable kbd
- if (hasKeycodeLocked(device, AKEYCODE_Q)) {
- device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
- }
-
// See if this device has a DPAD.
if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
@@ -1184,6 +1205,14 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
}
}
+ // 'Q' key support = cheap test of whether this is an alpha-capable kbd. Many gamepads will
+ // report a broader set of HID usages than they need, however, so we only want to mark this
+ // device as a keyboard if it is not a gamepad.
+ if (hasKeycodeLocked(device, AKEYCODE_Q) &&
+ !(device->classes & INPUT_DEVICE_CLASS_GAMEPAD)) {
+ device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
+ }
+
// Disable kernel key repeat since we handle it ourselves
unsigned int repeatRate[] = {0,0};
if (ioctl(fd, EVIOCSREP, repeatRate)) {
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index afc12ef..c93fc7a 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -42,6 +42,20 @@
#define BTN_FIRST 0x100 // first button code
#define BTN_LAST 0x15f // last button code
+/*
+ * These constants are used privately in Android to pass raw timestamps
+ * through evdev from uinput device drivers because there is currently no
+ * other way to transfer this information. The evdev driver automatically
+ * timestamps all input events with the time they were posted and clobbers
+ * whatever information was passed in.
+ *
+ * For the purposes of this hack, the timestamp is specified in the
+ * CLOCK_MONOTONIC timebase and is split into two EV_MSC events specifying
+ * seconds and microseconds.
+ */
+#define MSC_ANDROID_TIME_SEC 0x6
+#define MSC_ANDROID_TIME_USEC 0x7
+
namespace android {
enum {
@@ -329,6 +343,9 @@ private:
bool ffEffectPlaying;
int16_t ffEffectId; // initially -1
+ int32_t timestampOverrideSec;
+ int32_t timestampOverrideUsec;
+
Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
~Device();
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 43d76bb..ab38ed2 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -882,8 +882,9 @@ void InputDevice::dump(String8& dump) {
snprintf(name, sizeof(name), "%d", range.axis);
}
dump.appendFormat(INDENT3 "%s: source=0x%08x, "
- "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n",
- name, range.source, range.min, range.max, range.flat, range.fuzz);
+ "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n",
+ name, range.source, range.min, range.max, range.flat, range.fuzz,
+ range.resolution);
}
}
@@ -2247,20 +2248,20 @@ void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
if (mParameters.mode == Parameters::MODE_POINTER) {
float minX, minY, maxX, maxY;
if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
- info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f);
- info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f);
}
} else {
- info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale);
- info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale);
+ info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
}
- info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
if (mCursorScrollAccumulator.haveRelativeVWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
}
if (mCursorScrollAccumulator.haveRelativeHWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
}
}
@@ -2611,10 +2612,12 @@ void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
}
if (mCursorScrollAccumulator.haveRelativeVWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f);
}
if (mCursorScrollAccumulator.haveRelativeHWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f);
}
}
}
@@ -2698,6 +2701,12 @@ void TouchInputMapper::dump(String8& dump) {
mPointerYZoomScale);
dump.appendFormat(INDENT4 "MaxSwipeWidth: %f\n",
mPointerGestureMaxSwipeWidth);
+ } else if (mDeviceMode == DEVICE_MODE_NAVIGATION) {
+ dump.appendFormat(INDENT3 "Navigation Gesture Detector:\n");
+ dump.appendFormat(INDENT4 "AssistStartY: %0.3f\n",
+ mNavigationAssistStartY);
+ dump.appendFormat(INDENT4 "AssistEndY: %0.3f\n",
+ mNavigationAssistEndY);
}
}
@@ -2892,7 +2901,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
}
} else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
- mDeviceMode = DEVICE_MODE_UNSCALED;
+ mDeviceMode = DEVICE_MODE_NAVIGATION;
} else {
mSource = AINPUT_SOURCE_TOUCHPAD;
mDeviceMode = DEVICE_MODE_UNSCALED;
@@ -3063,6 +3072,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
mOrientedRanges.touchMajor.max = diagonalSize;
mOrientedRanges.touchMajor.flat = 0;
mOrientedRanges.touchMajor.fuzz = 0;
+ mOrientedRanges.touchMajor.resolution = 0;
mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
@@ -3073,6 +3083,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
mOrientedRanges.toolMajor.max = diagonalSize;
mOrientedRanges.toolMajor.flat = 0;
mOrientedRanges.toolMajor.fuzz = 0;
+ mOrientedRanges.toolMajor.resolution = 0;
mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
@@ -3083,6 +3094,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
mOrientedRanges.size.max = 1.0;
mOrientedRanges.size.flat = 0;
mOrientedRanges.size.fuzz = 0;
+ mOrientedRanges.size.resolution = 0;
} else {
mSizeScale = 0.0f;
}
@@ -3106,6 +3118,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
mOrientedRanges.pressure.max = 1.0;
mOrientedRanges.pressure.flat = 0;
mOrientedRanges.pressure.fuzz = 0;
+ mOrientedRanges.pressure.resolution = 0;
// Tilt
mTiltXCenter = 0;
@@ -3129,6 +3142,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
mOrientedRanges.tilt.max = M_PI_2;
mOrientedRanges.tilt.flat = 0;
mOrientedRanges.tilt.fuzz = 0;
+ mOrientedRanges.tilt.resolution = 0;
}
// Orientation
@@ -3142,6 +3156,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
mOrientedRanges.orientation.max = M_PI;
mOrientedRanges.orientation.flat = 0;
mOrientedRanges.orientation.fuzz = 0;
+ mOrientedRanges.orientation.resolution = 0;
} else if (mCalibration.orientationCalibration !=
Calibration::ORIENTATION_CALIBRATION_NONE) {
if (mCalibration.orientationCalibration
@@ -3165,6 +3180,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
mOrientedRanges.orientation.max = M_PI_2;
mOrientedRanges.orientation.flat = 0;
mOrientedRanges.orientation.fuzz = 0;
+ mOrientedRanges.orientation.resolution = 0;
}
// Distance
@@ -3190,6 +3206,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
mOrientedRanges.distance.flat = 0;
mOrientedRanges.distance.fuzz =
mRawPointerAxes.distance.fuzz * mDistanceScale;
+ mOrientedRanges.distance.resolution = 0;
}
// Compute oriented precision, scales and ranges.
@@ -3204,12 +3221,14 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
mOrientedRanges.x.min = mYTranslate;
mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1;
mOrientedRanges.x.flat = 0;
- mOrientedRanges.x.fuzz = mYScale;
+ mOrientedRanges.x.fuzz = 0;
+ mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
mOrientedRanges.y.min = mXTranslate;
mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1;
mOrientedRanges.y.flat = 0;
- mOrientedRanges.y.fuzz = mXScale;
+ mOrientedRanges.y.fuzz = 0;
+ mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
break;
default:
@@ -3219,17 +3238,19 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
mOrientedRanges.x.min = mXTranslate;
mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1;
mOrientedRanges.x.flat = 0;
- mOrientedRanges.x.fuzz = mXScale;
+ mOrientedRanges.x.fuzz = 0;
+ mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
mOrientedRanges.y.min = mYTranslate;
mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1;
mOrientedRanges.y.flat = 0;
- mOrientedRanges.y.fuzz = mYScale;
+ mOrientedRanges.y.fuzz = 0;
+ mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
break;
}
- // Compute pointer gesture detection parameters.
if (mDeviceMode == DEVICE_MODE_POINTER) {
+ // Compute pointer gesture detection parameters.
float rawDiagonal = hypotf(rawWidth, rawHeight);
float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);
@@ -3254,10 +3275,14 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
// translated into freeform gestures.
mPointerGestureMaxSwipeWidth =
mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
- }
- // Abort current pointer usages because the state has changed.
- abortPointerUsage(when, 0 /*policyFlags*/);
+ // Abort current pointer usages because the state has changed.
+ abortPointerUsage(when, 0 /*policyFlags*/);
+ } else if (mDeviceMode == DEVICE_MODE_NAVIGATION) {
+ // Compute navigation parameters.
+ mNavigationAssistStartY = mSurfaceHeight * 0.9f;
+ mNavigationAssistEndY = mSurfaceHeight * 0.5f;
+ }
// Inform the dispatcher about the changes.
*outResetNeeded = true;
@@ -3596,6 +3621,7 @@ void TouchInputMapper::reset(nsecs_t when) {
mPointerGesture.reset();
mPointerSimple.reset();
+ mNavigation.reset();
if (mPointerController != NULL) {
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
@@ -3746,6 +3772,8 @@ void TouchInputMapper::sync(nsecs_t when) {
mPointerController->setSpots(mCurrentCookedPointerData.pointerCoords,
mCurrentCookedPointerData.idToIndex,
mCurrentCookedPointerData.touchingIdBits);
+ } else if (mDeviceMode == DEVICE_MODE_NAVIGATION) {
+ dispatchNavigationAssist(when, policyFlags);
}
dispatchHoverExit(when, policyFlags);
@@ -5467,6 +5495,44 @@ void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) {
dispatchPointerSimple(when, policyFlags, false, false);
}
+void TouchInputMapper::dispatchNavigationAssist(nsecs_t when, uint32_t policyFlags) {
+ if (mCurrentCookedPointerData.touchingIdBits.count() == 1) {
+ if (mLastCookedPointerData.touchingIdBits.isEmpty()) {
+ // First pointer down.
+ uint32_t id = mCurrentCookedPointerData.touchingIdBits.firstMarkedBit();
+ const PointerCoords& coords = mCurrentCookedPointerData.pointerCoordsForId(id);
+ if (coords.getY() >= mNavigationAssistStartY) {
+ // Start tracking the possible assist swipe.
+ mNavigation.activeAssistId = id;
+ return;
+ }
+ } else if (mNavigation.activeAssistId >= 0
+ && mCurrentCookedPointerData.touchingIdBits.hasBit(mNavigation.activeAssistId)) {
+ const PointerCoords& coords = mCurrentCookedPointerData.pointerCoordsForId(
+ mNavigation.activeAssistId);
+ if (coords.getY() > mNavigationAssistEndY) {
+ // Swipe is still in progress.
+ return;
+ }
+
+ // Detected assist swipe.
+ int32_t metaState = mContext->getGlobalMetaState();
+ NotifyKeyArgs downArgs(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
+ policyFlags | POLICY_FLAG_VIRTUAL,
+ AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_ASSIST, 0, metaState, when);
+ getListener()->notifyKey(&downArgs);
+
+ NotifyKeyArgs upArgs(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
+ policyFlags | POLICY_FLAG_VIRTUAL,
+ AKEY_EVENT_ACTION_UP, 0, AKEYCODE_ASSIST, 0, metaState, when);
+ getListener()->notifyKey(&upArgs);
+ }
+ }
+
+ // Cancel the assist swipe.
+ mNavigation.activeAssistId = -1;
+}
+
void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
const PointerProperties* properties, const PointerCoords* coords,
@@ -6045,10 +6111,10 @@ void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
for (size_t i = 0; i < mAxes.size(); i++) {
const Axis& axis = mAxes.valueAt(i);
info->addMotionRange(axis.axisInfo.axis, AINPUT_SOURCE_JOYSTICK,
- axis.min, axis.max, axis.flat, axis.fuzz);
+ axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
info->addMotionRange(axis.axisInfo.highAxis, AINPUT_SOURCE_JOYSTICK,
- axis.min, axis.max, axis.flat, axis.fuzz);
+ axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
}
}
}
@@ -6078,8 +6144,8 @@ void JoystickInputMapper::dump(String8& dump) {
dump.append(" (invert)");
}
- dump.appendFormat(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f\n",
- axis.min, axis.max, axis.flat, axis.fuzz);
+ dump.appendFormat(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n",
+ axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
dump.appendFormat(INDENT4 " scale=%0.5f, offset=%0.5f, "
"highScale=%0.5f, highOffset=%0.5f\n",
axis.scale, axis.offset, axis.highScale, axis.highOffset);
@@ -6125,18 +6191,21 @@ void JoystickInputMapper::configure(nsecs_t when,
float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
scale, 0.0f, highScale, 0.0f,
- 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+ 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+ rawAxisInfo.resolution * scale);
} else if (isCenteredAxis(axisInfo.axis)) {
float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
scale, offset, scale, offset,
- -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+ -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+ rawAxisInfo.resolution * scale);
} else {
float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
scale, 0.0f, scale, 0.0f,
- 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+ 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+ rawAxisInfo.resolution * scale);
}
// To eliminate noise while the joystick is at rest, filter out small variations
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index c596b37..312f19b 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -791,6 +791,10 @@ struct CookedPointerData {
void clear();
void copyFrom(const CookedPointerData& other);
+ inline const PointerCoords& pointerCoordsForId(uint32_t id) const {
+ return pointerCoords[idToIndex[id]];
+ }
+
inline bool isHovering(uint32_t pointerIndex) {
return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id);
}
@@ -1180,6 +1184,7 @@ protected:
DEVICE_MODE_DISABLED, // input is disabled
DEVICE_MODE_DIRECT, // direct mapping (touchscreen)
DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad)
+ DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
DEVICE_MODE_POINTER, // pointer mapping (pointer)
};
DeviceMode mDeviceMode;
@@ -1432,6 +1437,10 @@ private:
// The maximum swipe width.
float mPointerGestureMaxSwipeWidth;
+ // The start and end Y thresholds for invoking the assist navigation swipe.
+ float mNavigationAssistStartY;
+ float mNavigationAssistEndY;
+
struct PointerDistanceHeapElement {
uint32_t currentPointerIndex : 8;
uint32_t lastPointerIndex : 8;
@@ -1606,6 +1615,15 @@ private:
}
} mPointerSimple;
+ struct Navigation {
+ // The id of a pointer that is tracking a possible assist swipe.
+ int32_t activeAssistId; // -1 if none
+
+ void reset() {
+ activeAssistId = -1;
+ }
+ } mNavigation;
+
// The pointer and scroll velocity controls.
VelocityControl mPointerVelocityControl;
VelocityControl mWheelXVelocityControl;
@@ -1641,6 +1659,8 @@ private:
bool down, bool hovering);
void abortPointerSimple(nsecs_t when, uint32_t policyFlags);
+ void dispatchNavigationAssist(nsecs_t when, uint32_t policyFlags);
+
// Dispatches a motion event.
// If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
// method will take care of setting the index and transmuting the action to DOWN or UP
@@ -1730,10 +1750,11 @@ private:
float highScale; // scale factor from raw to normalized values of high split
float highOffset; // offset to add after scaling for normalization of high split
- float min; // normalized inclusive minimum
- float max; // normalized inclusive maximum
- float flat; // normalized flat region size
- float fuzz; // normalized error tolerance
+ float min; // normalized inclusive minimum
+ float max; // normalized inclusive maximum
+ float flat; // normalized flat region size
+ float fuzz; // normalized error tolerance
+ float resolution; // normalized resolution in units/mm
float filter; // filter out small variations of this size
float currentValue; // current value
@@ -1744,7 +1765,7 @@ private:
void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
bool explicitlyMapped, float scale, float offset,
float highScale, float highOffset,
- float min, float max, float flat, float fuzz) {
+ float min, float max, float flat, float fuzz, float resolution) {
this->rawAxisInfo = rawAxisInfo;
this->axisInfo = axisInfo;
this->explicitlyMapped = explicitlyMapped;
@@ -1756,6 +1777,7 @@ private:
this->max = max;
this->flat = flat;
this->fuzz = fuzz;
+ this->resolution = resolution;
this->filter = 0;
resetValue();
}
diff --git a/services/input/tests/Android.mk b/services/input/tests/Android.mk
index 8f8c34b..211e64b 100644
--- a/services/input/tests/Android.mk
+++ b/services/input/tests/Android.mk
@@ -9,6 +9,7 @@ test_src_files := \
shared_libraries := \
libcutils \
+ liblog \
libandroidfw \
libutils \
libhardware \
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 9e06db8..01625dd 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -321,12 +321,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// track the current default http proxy - tell the world if we get a new one (real change)
private ProxyProperties mDefaultProxy = null;
- private Object mDefaultProxyLock = new Object();
+ private Object mProxyLock = new Object();
private boolean mDefaultProxyDisabled = false;
// track the global proxy.
private ProxyProperties mGlobalProxy = null;
- private final Object mGlobalProxyLock = new Object();
private SettingsObserver mSettingsObserver;
@@ -1471,8 +1470,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
loge("Error modifying route - no interface name");
return false;
}
-
- if (r.isHostRoute() == false) {
+ if (r.hasGateway()) {
RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getAllRoutes(), r.getGateway());
if (bestRoute != null) {
if (bestRoute.getGateway().equals(r.getGateway())) {
@@ -2348,28 +2346,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
- for (RouteInfo r : routeDiff.added) {
- if (isLinkDefault || ! r.isDefaultRoute()) {
- addRoute(newLp, r, TO_DEFAULT_TABLE);
- } else {
- // add to a secondary route table
- addRoute(newLp, r, TO_SECONDARY_TABLE);
-
- // many radios add a default route even when we don't want one.
- // remove the default route unless somebody else has asked for it
- String ifaceName = newLp.getInterfaceName();
- if (TextUtils.isEmpty(ifaceName) == false && mAddedRoutes.contains(r) == false) {
- if (VDBG) log("Removing " + r + " for interface " + ifaceName);
- try {
- mNetd.removeRoute(ifaceName, r);
- } catch (Exception e) {
- // never crash - catch them all
- if (DBG) loge("Exception trying to remove a route: " + e);
- }
- }
- }
- }
-
if (!isLinkDefault) {
// handle DNS routes
if (routesChanged) {
@@ -2394,6 +2370,29 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
}
+
+ for (RouteInfo r : routeDiff.added) {
+ if (isLinkDefault || ! r.isDefaultRoute()) {
+ addRoute(newLp, r, TO_DEFAULT_TABLE);
+ } else {
+ // add to a secondary route table
+ addRoute(newLp, r, TO_SECONDARY_TABLE);
+
+ // many radios add a default route even when we don't want one.
+ // remove the default route unless somebody else has asked for it
+ String ifaceName = newLp.getInterfaceName();
+ if (TextUtils.isEmpty(ifaceName) == false && mAddedRoutes.contains(r) == false) {
+ if (VDBG) log("Removing " + r + " for interface " + ifaceName);
+ try {
+ mNetd.removeRoute(ifaceName, r);
+ } catch (Exception e) {
+ // never crash - catch them all
+ if (DBG) loge("Exception trying to remove a route: " + e);
+ }
+ }
+ }
+ }
+
return routesChanged;
}
@@ -3039,14 +3038,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// so this API change wouldn't have a benifit. It also breaks the passing
// of proxy info to all the JVMs.
// enforceAccessPermission();
- synchronized (mDefaultProxyLock) {
- return mDefaultProxyDisabled ? null : mDefaultProxy;
+ synchronized (mProxyLock) {
+ if (mGlobalProxy != null) return mGlobalProxy;
+ return (mDefaultProxyDisabled ? null : mDefaultProxy);
}
}
public void setGlobalProxy(ProxyProperties proxyProperties) {
- enforceChangePermission();
- synchronized (mGlobalProxyLock) {
+ enforceConnectivityInternalPermission();
+ synchronized (mProxyLock) {
if (proxyProperties == mGlobalProxy) return;
if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return;
if (mGlobalProxy != null && mGlobalProxy.equals(proxyProperties)) return;
@@ -3063,16 +3063,21 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mGlobalProxy = null;
}
ContentResolver res = mContext.getContentResolver();
- Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, host);
- Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, port);
- Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
- exclList);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, host);
+ Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, port);
+ Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+ exclList);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
if (mGlobalProxy == null) {
proxyProperties = mDefaultProxy;
}
- //sendProxyBroadcast(proxyProperties);
+ sendProxyBroadcast(proxyProperties);
}
private void loadGlobalProxy() {
@@ -3083,7 +3088,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
if (!TextUtils.isEmpty(host)) {
ProxyProperties proxyProperties = new ProxyProperties(host, port, exclList);
- synchronized (mGlobalProxyLock) {
+ synchronized (mProxyLock) {
mGlobalProxy = proxyProperties;
}
}
@@ -3094,7 +3099,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// so this API change wouldn't have a benifit. It also breaks the passing
// of proxy info to all the JVMs.
// enforceAccessPermission();
- synchronized (mGlobalProxyLock) {
+ synchronized (mProxyLock) {
return mGlobalProxy;
}
}
@@ -3103,11 +3108,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (proxy != null && TextUtils.isEmpty(proxy.getHost())) {
proxy = null;
}
- synchronized (mDefaultProxyLock) {
+ synchronized (mProxyLock) {
if (mDefaultProxy != null && mDefaultProxy.equals(proxy)) return;
- if (mDefaultProxy == proxy) return;
+ if (mDefaultProxy == proxy) return; // catches repeated nulls
mDefaultProxy = proxy;
+ if (mGlobalProxy != null) return;
if (!mDefaultProxyDisabled) {
sendProxyBroadcast(proxy);
}
@@ -3350,10 +3356,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mDnsOverridden = true;
}
- // Temporarily disable the default proxy.
- synchronized (mDefaultProxyLock) {
+ // Temporarily disable the default proxy (not global).
+ synchronized (mProxyLock) {
mDefaultProxyDisabled = true;
- if (mDefaultProxy != null) {
+ if (mGlobalProxy == null && mDefaultProxy != null) {
sendProxyBroadcast(null);
}
}
@@ -3368,9 +3374,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mHandler.sendEmptyMessage(EVENT_RESTORE_DNS);
}
}
- synchronized (mDefaultProxyLock) {
+ synchronized (mProxyLock) {
mDefaultProxyDisabled = false;
- if (mDefaultProxy != null) {
+ if (mGlobalProxy == null && mDefaultProxy != null) {
sendProxyBroadcast(mDefaultProxy);
}
}
diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags
index 8bc2da2..59577ad 100644
--- a/services/java/com/android/server/EventLogTags.logtags
+++ b/services/java/com/android/server/EventLogTags.logtags
@@ -157,3 +157,8 @@ option java_package com.android.server
# ConfigUpdateInstallReceiver.java
# ---------------------------
51300 config_install_failed (dir|3)
+
+# ---------------------------
+# IntentFirewall.java
+# ---------------------------
+51400 ifw_intent_matched (Intent Type|1|5),(Component Name|3),(Caller Uid|1|5),(Caller Pkg Count|1|1),(Caller Pkgs|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5)
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 3b541ec..a28c387 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1070,7 +1070,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(getAppShowFlags(), null);
}
- return new InputBindResult(session.session, session.channel, mCurId, mCurSeq);
+ return new InputBindResult(session.session,
+ session.channel != null ? session.channel.dup() : null, mCurId, mCurSeq);
}
InputBindResult startInputLocked(IInputMethodClient client,
@@ -2357,13 +2358,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return true;
case MSG_CREATE_SESSION: {
args = (SomeArgs)msg.obj;
+ IInputMethod method = (IInputMethod)args.arg1;
InputChannel channel = (InputChannel)args.arg2;
try {
- ((IInputMethod)args.arg1).createSession(channel,
- (IInputSessionCallback)args.arg3);
+ method.createSession(channel, (IInputSessionCallback)args.arg3);
} catch (RemoteException e) {
} finally {
- if (channel != null) {
+ // Dispose the channel if the input method is not local to this process
+ // because the remote proxy will get its own copy when unparceled.
+ if (channel != null && Binder.isProxy(method)) {
channel.dispose();
}
}
@@ -2404,16 +2407,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// There is nothing interesting about the last client dying.
}
return true;
- case MSG_BIND_METHOD:
+ case MSG_BIND_METHOD: {
args = (SomeArgs)msg.obj;
+ IInputMethodClient client = (IInputMethodClient)args.arg1;
+ InputBindResult res = (InputBindResult)args.arg2;
try {
- ((IInputMethodClient)args.arg1).onBindMethod(
- (InputBindResult)args.arg2);
+ client.onBindMethod(res);
} catch (RemoteException e) {
Slog.w(TAG, "Client died receiving input method " + args.arg2);
+ } finally {
+ // Dispose the channel if the input method is not local to this process
+ // because the remote proxy will get its own copy when unparceled.
+ if (res.channel != null && Binder.isProxy(client)) {
+ res.channel.dispose();
+ }
}
args.recycle();
return true;
+ }
case MSG_SET_ACTIVE:
try {
((ClientState)msg.obj).client.setActive(msg.arg1 != 0);
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index bcb7cb7..b47e8a0 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -54,19 +54,17 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
-
-import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.server.location.GeocoderProxy;
+import com.android.server.location.GeofenceProxy;
import com.android.server.location.GeofenceManager;
import com.android.server.location.GpsLocationProvider;
import com.android.server.location.LocationBlacklist;
@@ -118,6 +116,8 @@ public class LocationManagerService extends ILocationManager.Stub {
private static final int MSG_LOCATION_CHANGED = 1;
+ private static final long NANOS_PER_MILLI = 1000000L;
+
// Location Providers may sometimes deliver location updates
// slightly faster that requested - provide grace period so
// we don't unnecessarily filter events that are otherwise on
@@ -181,6 +181,11 @@ public class LocationManagerService extends ILocationManager.Stub {
// mapping from provider name to last known location
private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
+ // same as mLastLocation, but is not updated faster than LocationFudger.FASTEST_INTERVAL_MS.
+ // locations stored here are not fudged for coarse permissions.
+ private final HashMap<String, Location> mLastLocationCoarseInterval =
+ new HashMap<String, Location>();
+
// all providers that operate over proxy, for authorizing incoming location
private final ArrayList<LocationProviderProxy> mProxyProviders =
new ArrayList<LocationProviderProxy>();
@@ -338,11 +343,11 @@ public class LocationManagerService extends ILocationManager.Stub {
addProviderLocked(passiveProvider);
mEnabledProviders.add(passiveProvider.getName());
mPassiveProvider = passiveProvider;
+ // Create a gps location provider
+ GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this,
+ mLocationHandler.getLooper());
if (GpsLocationProvider.isSupported()) {
- // Create a gps location provider
- GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this,
- mLocationHandler.getLooper());
mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
addProviderLocked(gpsProvider);
@@ -406,6 +411,14 @@ public class LocationManagerService extends ILocationManager.Stub {
if (mGeocodeProvider == null) {
Slog.e(TAG, "no geocoder provider found");
}
+
+ // bind to geofence provider
+ GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, providerPackageNames,
+ mLocationHandler, gpsProvider.getGpsGeofenceProxy());
+ if (provider == null) {
+ Slog.e(TAG, "no geofence provider found");
+ }
+
}
/**
@@ -417,6 +430,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mLocationHandler.removeMessages(MSG_LOCATION_CHANGED);
synchronized (mLock) {
mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
for (LocationProviderInterface p : mProviders) {
updateProviderListenersLocked(p.getName(), false, mCurrentUserId);
}
@@ -1401,7 +1415,14 @@ public class LocationManagerService extends ILocationManager.Stub {
if (!isAllowedByUserSettingsLocked(name, uid)) return null;
- Location location = mLastLocation.get(name);
+ Location location;
+ if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+ // Make sure that an app with coarse permissions can't get frequent location
+ // updates by calling LocationManager.getLastKnownLocation repeatedly.
+ location = mLastLocationCoarseInterval.get(name);
+ } else {
+ location = mLastLocation.get(name);
+ }
if (location == null) {
return null;
}
@@ -1667,7 +1688,8 @@ public class LocationManagerService extends ILocationManager.Stub {
// Check whether sufficient time has passed
long minTime = record.mRequest.getFastestInterval();
- long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos()) / 1000000L;
+ long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos())
+ / NANOS_PER_MILLI;
if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
return false;
}
@@ -1720,13 +1742,30 @@ public class LocationManagerService extends ILocationManager.Stub {
}
lastLocation.set(location);
+ // Update last known coarse interval location if enough time has passed.
+ Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
+ if (lastLocationCoarseInterval == null) {
+ lastLocationCoarseInterval = new Location(location);
+ mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval);
+ }
+ long timeDiffNanos = location.getElapsedRealtimeNanos()
+ - lastLocationCoarseInterval.getElapsedRealtimeNanos();
+ if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
+ lastLocationCoarseInterval.set(location);
+ }
+ // Don't ever return a coarse location that is more recent than the allowed update
+ // interval (i.e. don't allow an app to keep registering and unregistering for
+ // location updates to overcome the minimum interval).
+ noGPSLocation =
+ lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
+
// Skip if there are no UpdateRecords for this provider.
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records == null || records.size() == 0) return;
// Fetch coarse location
Location coarseLocation = null;
- if (noGPSLocation != null && !noGPSLocation.equals(lastNoGPSLocation)) {
+ if (noGPSLocation != null) {
coarseLocation = mLocationFudger.getOrCreate(noGPSLocation);
}
@@ -2015,6 +2054,7 @@ public class LocationManagerService extends ILocationManager.Stub {
addProviderLocked(provider);
mMockProviders.put(name, provider);
mLastLocation.put(name, null);
+ mLastLocationCoarseInterval.put(name, null);
updateProvidersLocked();
}
Binder.restoreCallingIdentity(identity);
@@ -2037,6 +2077,7 @@ public class LocationManagerService extends ILocationManager.Stub {
addProviderLocked(realProvider);
}
mLastLocation.put(provider, null);
+ mLastLocationCoarseInterval.put(provider, null);
updateProvidersLocked();
Binder.restoreCallingIdentity(identity);
}
@@ -2168,6 +2209,13 @@ public class LocationManagerService extends ILocationManager.Stub {
pw.println(" " + provider + ": " + location);
}
+ pw.println(" Last Known Locations Coarse Intervals:");
+ for (Map.Entry<String, Location> entry : mLastLocationCoarseInterval.entrySet()) {
+ String provider = entry.getKey();
+ Location location = entry.getValue();
+ pw.println(" " + provider + ": " + location);
+ }
+
mGeofenceManager.dump(pw);
if (mEnabledProviders.size() > 0) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 44d730c..cfb892f 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -26,16 +26,17 @@ import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
-import android.app.INotificationListener;
import android.app.ITransientNotification;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -57,6 +58,9 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
+import android.service.notification.INotificationListener;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.AtomicFile;
@@ -68,8 +72,6 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
-import com.android.internal.statusbar.StatusBarNotification;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -121,6 +123,8 @@ public class NotificationManagerService extends INotificationManager.Stub
private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
private static final boolean ENABLE_BLOCKED_TOASTS = true;
+ private static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":";
+
final Context mContext;
final IActivityManager mAm;
final UserManager mUserManager;
@@ -163,8 +167,18 @@ public class NotificationManagerService extends INotificationManager.Stub
private final AppOpsManager mAppOps;
- private ArrayList<NotificationListenerInfo> mListeners = new ArrayList<NotificationListenerInfo>();
- private ArrayList<String> mEnabledListenersForCurrentUser = new ArrayList<String>();
+ // contains connections to all connected listeners, including app services
+ // and system listeners
+ private ArrayList<NotificationListenerInfo> mListeners
+ = new ArrayList<NotificationListenerInfo>();
+ // things that will be put into mListeners as soon as they're ready
+ private ArrayList<String> mServicesBinding = new ArrayList<String>();
+ // lists the component names of all enabled (and therefore connected) listener
+ // app services for the current user only
+ private HashSet<ComponentName> mEnabledListenersForCurrentUser
+ = new HashSet<ComponentName>();
+ // Just the packages from mEnabledListenersForCurrentUser
+ private HashSet<String> mEnabledListenerPackageNames = new HashSet<String>();
// Notification control database. For now just contains disabled packages.
private AtomicFile mPolicyFile;
@@ -181,27 +195,42 @@ public class NotificationManagerService extends INotificationManager.Stub
private class NotificationListenerInfo implements DeathRecipient {
INotificationListener listener;
- String pkg;
+ ComponentName component;
int userid;
boolean isSystem;
+ ServiceConnection connection;
- public NotificationListenerInfo(INotificationListener listener, String pkg, int userid,
- boolean isSystem) {
+ public NotificationListenerInfo(INotificationListener listener, ComponentName component,
+ int userid, boolean isSystem) {
this.listener = listener;
- this.pkg = pkg;
+ this.component = component;
this.userid = userid;
this.isSystem = isSystem;
+ this.connection = null;
+ }
+
+ public NotificationListenerInfo(INotificationListener listener, ComponentName component,
+ int userid, ServiceConnection connection) {
+ this.listener = listener;
+ this.component = component;
+ this.userid = userid;
+ this.isSystem = false;
+ this.connection = connection;
}
boolean enabledAndUserMatches(StatusBarNotification sbn) {
final int nid = sbn.getUserId();
- if (!(isSystem || isEnabledForUser(nid))) return false;
+ if (!isEnabledForCurrentUser()) {
+ return false;
+ }
if (this.userid == UserHandle.USER_ALL) return true;
return (nid == UserHandle.USER_ALL || nid == this.userid);
}
public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
- if (!enabledAndUserMatches(sbn)) return;
+ if (!enabledAndUserMatches(sbn)) {
+ return;
+ }
try {
listener.onNotificationPosted(sbn);
} catch (RemoteException ex) {
@@ -220,15 +249,17 @@ public class NotificationManagerService extends INotificationManager.Stub
@Override
public void binderDied() {
- unregisterListener(this.listener, this.userid);
+ if (connection == null) {
+ // This is not a service; it won't be recreated. We can give up this connection.
+ unregisterListener(this.listener, this.userid);
+ }
}
/** convenience method for looking in mEnabledListenersForCurrentUser */
- public boolean isEnabledForUser(int userid) {
- for (int i=0; i<mEnabledListenersForCurrentUser.size(); i++) {
- if (this.pkg.equals(mEnabledListenersForCurrentUser.get(i))) return true;
- }
- return false;
+ public boolean isEnabledForCurrentUser() {
+ if (this.isSystem) return true;
+ if (this.connection == null) return false;
+ return mEnabledListenersForCurrentUser.contains(this.component);
}
}
@@ -434,6 +465,12 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
+ /**
+ * System-only API for getting a list of current (i.e. not cleared) notifications.
+ *
+ * Requires ACCESS_NOTIFICATIONS which is signature|system.
+ */
+ @Override
public StatusBarNotification[] getActiveNotifications(String callingPkg) {
// enforce() will ensure the calling uid has the correct permission
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -456,6 +493,12 @@ public class NotificationManagerService extends INotificationManager.Stub
return tmp;
}
+ /**
+ * System-only API for getting a list of recent (cleared, no longer shown) notifications.
+ *
+ * Requires ACCESS_NOTIFICATIONS which is signature|system.
+ */
+ @Override
public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
// enforce() will ensure the calling uid has the correct permission
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -474,27 +517,76 @@ public class NotificationManagerService extends INotificationManager.Stub
return tmp;
}
- boolean packageCanTapNotificationsForUser(final int uid, final String pkg) {
- // Make sure the package and uid match, and that the package is allowed access
- return (AppOpsManager.MODE_ALLOWED
- == mAppOps.checkOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, pkg));
+ /**
+ * Called whenever packages change, the user switches, or ENABLED_NOTIFICATION_LISTENERS
+ * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
+ */
+ void rebindListenerServices() {
+ String flat = Settings.Secure.getString(
+ mContext.getContentResolver(),
+ Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+
+ NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()];
+ final ArrayList<ComponentName> toAdd;
+ final int currentUser = ActivityManager.getCurrentUser();
+
+ synchronized (mNotificationList) {
+ // unbind and remove all existing listeners
+ toRemove = mListeners.toArray(toRemove);
+
+ toAdd = new ArrayList<ComponentName>();
+ final HashSet<ComponentName> newEnabled = new HashSet<ComponentName>();
+ final HashSet<String> newPackages = new HashSet<String>();
+
+ // decode the list of components
+ if (flat != null) {
+ String[] components = flat.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
+ for (int i=0; i<components.length; i++) {
+ final ComponentName component
+ = ComponentName.unflattenFromString(components[i]);
+ if (component != null) {
+ newEnabled.add(component);
+ toAdd.add(component);
+ newPackages.add(component.getPackageName());
+ }
+ }
+
+ mEnabledListenersForCurrentUser = newEnabled;
+ mEnabledListenerPackageNames = newPackages;
+ }
+ }
+
+ for (NotificationListenerInfo info : toRemove) {
+ final ComponentName component = info.component;
+ final int oldUser = info.userid;
+ Slog.v(TAG, "disabling notification listener for user " + oldUser + ": " + component);
+ unregisterListenerService(component, info.userid);
+ }
+
+ final int N = toAdd.size();
+ for (int i=0; i<N; i++) {
+ final ComponentName component = toAdd.get(i);
+ Slog.v(TAG, "enabling notification listener for user " + currentUser + ": "
+ + component);
+ registerListenerService(component, currentUser);
+ }
}
+ /**
+ * Register a listener binder directly with the notification manager.
+ *
+ * Only works with system callers. Apps should extend
+ * {@link android.service.notification.NotificationListenerService}.
+ */
@Override
public void registerListener(final INotificationListener listener,
- final String pkg, final int userid) {
- // ensure system or allowed pkg
- int uid = Binder.getCallingUid();
- boolean isSystem = (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0);
- if (!(isSystem || packageCanTapNotificationsForUser(uid, pkg))) {
- throw new SecurityException("Package " + pkg
- + " may not listen for notifications");
- }
+ final ComponentName component, final int userid) {
+ checkCallerIsSystem();
synchronized (mNotificationList) {
try {
NotificationListenerInfo info
- = new NotificationListenerInfo(listener, pkg, userid, isSystem);
+ = new NotificationListenerInfo(listener, component, userid, true);
listener.asBinder().linkToDeath(info, 0);
mListeners.add(info);
} catch (RemoteException e) {
@@ -503,6 +595,90 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
+ /**
+ * Version of registerListener that takes the name of a
+ * {@link android.service.notification.NotificationListenerService} to bind to.
+ *
+ * This is the mechanism by which third parties may subscribe to notifications.
+ */
+ private void registerListenerService(final ComponentName name, final int userid) {
+ checkCallerIsSystem();
+
+ if (DBG) Slog.v(TAG, "registerListenerService: " + name + " u=" + userid);
+
+ synchronized (mNotificationList) {
+ final String servicesBindingTag = name.toString() + "/" + userid;
+ if (mServicesBinding.contains(servicesBindingTag)) {
+ // stop registering this thing already! we're working on it
+ return;
+ }
+ mServicesBinding.add(servicesBindingTag);
+
+ final int N = mListeners.size();
+ for (int i=N-1; i>=0; i--) {
+ final NotificationListenerInfo info = mListeners.get(i);
+ if (name.equals(info.component)
+ && info.userid == userid) {
+ // cut old connections
+ if (DBG) Slog.v(TAG, " disconnecting old listener: " + info.listener);
+ mListeners.remove(i);
+ if (info.connection != null) {
+ mContext.unbindService(info.connection);
+ }
+ }
+ }
+
+ Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
+ intent.setComponent(name);
+
+ intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+ com.android.internal.R.string.notification_listener_binding_label);
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0));
+
+ try {
+ if (DBG) Slog.v(TAG, "binding: " + intent);
+ if (!mContext.bindServiceAsUser(intent,
+ new ServiceConnection() {
+ INotificationListener mListener;
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mNotificationList) {
+ mServicesBinding.remove(servicesBindingTag);
+ try {
+ mListener = INotificationListener.Stub.asInterface(service);
+ NotificationListenerInfo info = new NotificationListenerInfo(
+ mListener, name, userid, this);
+ service.linkToDeath(info, 0);
+ mListeners.add(info);
+ } catch (RemoteException e) {
+ // already dead
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Slog.v(TAG, "notification listener connection lost: " + name);
+ }
+ },
+ Context.BIND_AUTO_CREATE,
+ new UserHandle(userid)))
+ {
+ mServicesBinding.remove(servicesBindingTag);
+ Slog.w(TAG, "Unable to bind listener service: " + intent);
+ return;
+ }
+ } catch (SecurityException ex) {
+ Slog.e(TAG, "Unable to bind listener service: " + intent, ex);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Remove a listener binder directly
+ */
@Override
public void unregisterListener(INotificationListener listener, int userid) {
// no need to check permissions; if your listener binder is in the list,
@@ -513,12 +689,39 @@ public class NotificationManagerService extends INotificationManager.Stub
for (int i=N-1; i>=0; i--) {
final NotificationListenerInfo info = mListeners.get(i);
if (info.listener == listener && info.userid == userid) {
- mListeners.remove(listener);
+ mListeners.remove(i);
+ if (info.connection != null) {
+ mContext.unbindService(info.connection);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove a listener service for the given user by ComponentName
+ */
+ private void unregisterListenerService(ComponentName name, int userid) {
+ checkCallerIsSystem();
+
+ synchronized (mNotificationList) {
+ final int N = mListeners.size();
+ for (int i=N-1; i>=0; i--) {
+ final NotificationListenerInfo info = mListeners.get(i);
+ if (name.equals(info.component)
+ && info.userid == userid) {
+ mListeners.remove(i);
+ if (info.connection != null) {
+ mContext.unbindService(info.connection);
+ }
}
}
}
}
+ /**
+ * asynchronously notify all listeners about a new notification
+ */
private void notifyPostedLocked(NotificationRecord n) {
final StatusBarNotification sbn = n.sbn;
for (final NotificationListenerInfo info : mListeners) {
@@ -530,6 +733,9 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
+ /**
+ * asynchronously notify all listeners about a removed notification
+ */
private void notifyRemovedLocked(NotificationRecord n) {
final StatusBarNotification sbn = n.sbn;
for (final NotificationListenerInfo info : mListeners) {
@@ -541,6 +747,57 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
+ // -- APIs to support listeners clicking/clearing notifications --
+
+ private NotificationListenerInfo checkListenerToken(INotificationListener listener) {
+ final IBinder token = listener.asBinder();
+ final int N = mListeners.size();
+ for (int i=0; i<N; i++) {
+ final NotificationListenerInfo info = mListeners.get(i);
+ if (info.listener.asBinder() == token) return info;
+ }
+ throw new SecurityException("Disallowed call from unknown listener: " + listener);
+ }
+
+ /**
+ * Allow an INotificationListener to simulate a "clear all" operation.
+ *
+ * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
+ *
+ * @param token The binder for the listener, to check that the caller is allowed
+ */
+ public void clearAllNotificationsFromListener(INotificationListener token) {
+ NotificationListenerInfo info = checkListenerToken(token);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ cancelAll(info.userid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
+ *
+ * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
+ *
+ * @param token The binder for the listener, to check that the caller is allowed
+ */
+ public void clearNotificationFromListener(INotificationListener token, String pkg, String tag, int id) {
+ NotificationListenerInfo info = checkListenerToken(token);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ cancelNotification(pkg, tag, id, 0,
+ Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
+ true,
+ info.userid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ // -- end of listener APIs --
+
public static final class NotificationRecord
{
final StatusBarNotification sbn;
@@ -759,12 +1016,23 @@ public class NotificationManagerService extends INotificationManager.Stub
}
pkgList = new String[]{pkgName};
}
+
+ boolean anyListenersInvolved = false;
if (pkgList != null && (pkgList.length > 0)) {
for (String pkgName : pkgList) {
cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
UserHandle.USER_ALL);
+ if (mEnabledListenerPackageNames.contains(pkgName)) {
+ anyListenersInvolved = true;
+ }
}
}
+
+ if (anyListenersInvolved) {
+ // make sure we're still bound to any of our
+ // listeners who may have just upgraded
+ rebindListenerServices();
+ }
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
// Keep track of screen on/off state, but do not turn off the notification light
// until user passes through the lock screen or views the notification.
@@ -795,7 +1063,7 @@ public class NotificationManagerService extends INotificationManager.Stub
= Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
- = Settings.System.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+ = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
SettingsObserver(Handler handler) {
super(handler);
@@ -804,9 +1072,9 @@ public class NotificationManagerService extends INotificationManager.Stub
void observe() {
ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
- false, this);
+ false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
- false, this);
+ false, this, UserHandle.USER_ALL);
update(null);
}
@@ -825,19 +1093,7 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
- String pkglist = Settings.Secure.getString(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
- mEnabledListenersForCurrentUser.clear();
- if (pkglist != null) {
- String[] pkgs = pkglist.split(";");
- for (int i=0; i<pkgs.length; i++) {
- final String pkg = pkgs[i];
- if (pkg != null && ! "".equals(pkg)) {
- mEnabledListenersForCurrentUser.add(pkgs[i]);
- }
- }
- }
+ rebindListenerServices();
}
}
}
@@ -956,6 +1212,9 @@ public class NotificationManagerService extends INotificationManager.Stub
// no beeping until we're basically done booting
mSystemReady = true;
+
+ // make sure our listener services are properly bound
+ rebindListenerServices();
}
// Toasts
@@ -1781,16 +2040,17 @@ public class NotificationManagerService extends INotificationManager.Stub
pw.println("Current Notification Manager state:");
- pw.print(" Enabled listeners: [");
- for (String pkg : mEnabledListenersForCurrentUser) {
- pw.print(" " + pkg);
+ pw.println(" Listeners (" + mEnabledListenersForCurrentUser.size()
+ + ") enabled for current user:");
+ for (ComponentName cmpt : mEnabledListenersForCurrentUser) {
+ pw.println(" " + cmpt);
}
- pw.println(" ]");
- pw.println(" Live listeners:");
+ pw.println(" Live listeners (" + mListeners.size() + "):");
for (NotificationListenerInfo info : mListeners) {
- pw.println(" " + info.pkg + " (user " + info.userid + "): " + info.listener
- + (info.isSystem?" SYSTEM":""));
+ pw.println(" " + info.component
+ + " (user " + info.userid + "): " + info.listener
+ + (info.isSystem?" SYSTEM":""));
}
int N;
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 1fe98af..c21d8c6 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.app.StatusBarManager;
+import android.service.notification.StatusBarNotification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -33,7 +34,6 @@ import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.server.wm.WindowManagerService;
import java.io.FileDescriptor;
diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 179db12..0d8a571 100644
--- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -25,6 +25,7 @@ import android.view.Display;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputFilter;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
@@ -80,7 +81,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private final Choreographer mChoreographer;
- private int mCurrentDeviceId;
+ private int mCurrentTouchDeviceId;
private boolean mInstalled;
@@ -98,6 +99,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private boolean mHoverEventSequenceStarted;
+ private boolean mKeyEventSequenceStarted;
+
AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
super(context.getMainLooper());
mContext = context;
@@ -133,11 +136,21 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
}
- if (mEventHandler == null) {
+ if (event instanceof MotionEvent
+ && event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
+ MotionEvent motionEvent = (MotionEvent) event;
+ onMotionEvent(motionEvent, policyFlags);
+ } else if (event instanceof KeyEvent
+ && event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
+ KeyEvent keyEvent = (KeyEvent) event;
+ onKeyEvent(keyEvent, policyFlags);
+ } else {
super.onInputEvent(event, policyFlags);
- return;
}
- if (event.getSource() != InputDevice.SOURCE_TOUCHSCREEN) {
+ }
+
+ private void onMotionEvent(MotionEvent event, int policyFlags) {
+ if (mEventHandler == null) {
super.onInputEvent(event, policyFlags);
return;
}
@@ -149,26 +162,25 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
return;
}
final int deviceId = event.getDeviceId();
- if (mCurrentDeviceId != deviceId) {
+ if (mCurrentTouchDeviceId != deviceId) {
+ mCurrentTouchDeviceId = deviceId;
mMotionEventSequenceStarted = false;
mHoverEventSequenceStarted = false;
mEventHandler.clear();
- mCurrentDeviceId = deviceId;
}
- if (mCurrentDeviceId < 0) {
+ if (mCurrentTouchDeviceId < 0) {
super.onInputEvent(event, policyFlags);
return;
}
// We do not handle scroll events.
- MotionEvent motionEvent = (MotionEvent) event;
- if (motionEvent.getActionMasked() == MotionEvent.ACTION_SCROLL) {
+ if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
super.onInputEvent(event, policyFlags);
return;
}
// Wait for a down touch event to start processing.
- if (motionEvent.isTouchEvent()) {
+ if (event.isTouchEvent()) {
if (!mMotionEventSequenceStarted) {
- if (motionEvent.getActionMasked() != MotionEvent.ACTION_DOWN) {
+ if (event.getActionMasked() != MotionEvent.ACTION_DOWN) {
return;
}
mMotionEventSequenceStarted = true;
@@ -176,7 +188,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
} else {
// Wait for an enter hover event to start processing.
if (!mHoverEventSequenceStarted) {
- if (motionEvent.getActionMasked() != MotionEvent.ACTION_HOVER_ENTER) {
+ if (event.getActionMasked() != MotionEvent.ACTION_HOVER_ENTER) {
return;
}
mHoverEventSequenceStarted = true;
@@ -185,6 +197,22 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
batchMotionEvent((MotionEvent) event, policyFlags);
}
+ private void onKeyEvent(KeyEvent event, int policyFlags) {
+ if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
+ mKeyEventSequenceStarted = false;
+ super.onInputEvent(event, policyFlags);
+ return;
+ }
+ // Wait for a down key event to start processing.
+ if (!mKeyEventSequenceStarted) {
+ if (event.getAction() != KeyEvent.ACTION_DOWN) {
+ return;
+ }
+ mKeyEventSequenceStarted = true;
+ }
+ mAms.notifyKeyEvent(event, policyFlags);
+ }
+
private void scheduleProcessBatchedEvents() {
mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
mProcessBatchedEventsRunnable, null);
@@ -286,6 +314,13 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
}
+ void reset() {
+ setEnabledFeatures(0);
+ mKeyEventSequenceStarted = false;
+ mMotionEventSequenceStarted = false;
+ mHoverEventSequenceStarted = false;
+ }
+
private void enableFeatures() {
mMotionEventSequenceStarted = false;
mHoverEventSequenceStarted = false;
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 527e891..128a49f 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -61,16 +61,20 @@ import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
+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.WindowManager;
+import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityManager;
@@ -132,6 +136,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private static final int OWN_PROCESS_ID = android.os.Process.myPid();
+ private static final int MAX_POOL_SIZE = 10;
+
private static int sIdCounter = 0;
private static int sNextWindowId;
@@ -140,6 +146,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private final Object mLock = new Object();
+ private final Pool<PendingEvent> mPendingEventPool =
+ new SimplePool<PendingEvent>(MAX_POOL_SIZE);
+
private final SimpleStringSplitter mStringColonSplitter =
new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
@@ -633,6 +642,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
+ boolean notifyKeyEvent(KeyEvent event, int policyFlags) {
+ synchronized (mLock) {
+ KeyEvent localClone = KeyEvent.obtain(event);
+ boolean handled = notifyKeyEventLocked(localClone, policyFlags, false);
+ if (!handled) {
+ handled = notifyKeyEventLocked(localClone, policyFlags, true);
+ }
+ return handled;
+ }
+ }
+
/**
* Gets the bounds of the accessibility focus in the active window.
*
@@ -798,6 +818,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return false;
}
+ private boolean notifyKeyEventLocked(KeyEvent event, int policyFlags, boolean isDefault) {
+ // TODO: Now we are giving the key events to the last enabled
+ // service that can handle them which is the last one
+ // in our list since we write the last enabled as the
+ // last record in the enabled services setting. Ideally,
+ // the user should make the call which service handles
+ // key events. However, only one service should handle
+ // key events to avoid user frustration when different
+ // behavior is observed from different combinations of
+ // enabled accessibility services.
+ UserState state = getCurrentUserStateLocked();
+ for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+ Service service = state.mBoundServices.get(i);
+ if (service.mIsDefault == isDefault) {
+ service.notifyKeyEvent(event, policyFlags);
+ return true;
+ }
+ }
+ return false;
+ }
+
private void notifyClearAccessibilityNodeInfoCacheLocked() {
UserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
@@ -1119,8 +1160,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
boolean setInputFilter = false;
AccessibilityInputFilter inputFilter = null;
synchronized (mLock) {
- if ((userState.mIsAccessibilityEnabled && userState.mIsTouchExplorationEnabled)
- || userState.mIsDisplayMagnificationEnabled) {
+ if (userState.mIsAccessibilityEnabled) {
if (!mHasInputFilter) {
mHasInputFilter = true;
if (mInputFilter == null) {
@@ -1141,7 +1181,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} else {
if (mHasInputFilter) {
mHasInputFilter = false;
- mInputFilter.setEnabledFeatures(0);
+ mInputFilter.reset();
inputFilter = null;
setInputFilter = true;
}
@@ -1446,6 +1486,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public static final int MSG_ANNOUNCE_NEW_USER_IF_NEEDED = 5;
public static final int MSG_UPDATE_INPUT_FILTER = 6;
public static final int MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG = 7;
+ public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8;
public MainHandler(Looper looper) {
super(looper);
@@ -1464,6 +1505,16 @@ 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;
+ synchronized (mLock) {
+ if (mHasInputFilter && mInputFilter != null) {
+ mInputFilter.sendInputEvent(event, policyFlags);
+ }
+ }
+ event.recycle();
+ } break;
case MSG_SEND_STATE_TO_CLIENTS: {
final int clientState = msg.arg1;
final int userId = msg.arg2;
@@ -1536,6 +1587,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
+ private PendingEvent obtainPendingEventLocked(KeyEvent event, int policyFlags, int sequence) {
+ PendingEvent pendingEvent = mPendingEventPool.acquire();
+ if (pendingEvent == null) {
+ pendingEvent = new PendingEvent();
+ }
+ pendingEvent.event = event;
+ pendingEvent.policyFlags = policyFlags;
+ pendingEvent.sequence = sequence;
+ return pendingEvent;
+ }
+
+ private void recyclePendingEventLocked(PendingEvent pendingEvent) {
+ pendingEvent.clear();
+ mPendingEventPool.release(pendingEvent);
+ }
+
/**
* This class represents an accessibility service. It stores all per service
* data required for the service management, provides API for starting/stopping the
@@ -1545,12 +1612,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
* connection for the service.
*/
class Service extends IAccessibilityServiceConnection.Stub
- implements ServiceConnection, DeathRecipient {
-
- // We pick the MSBs to avoid collision since accessibility event types are
- // used as message types allowing us to remove messages per event type.
- private static final int MSG_ON_GESTURE = 0x80000000;
- private static final int MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 0x40000000;
+ implements ServiceConnection, DeathRecipient {;
final int mUserId;
@@ -1594,29 +1656,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final SparseArray<AccessibilityEvent> mPendingEvents =
new SparseArray<AccessibilityEvent>();
- /**
- * Handler for delayed event dispatch.
- */
- public Handler mHandler = new Handler(mMainHandler.getLooper()) {
+ final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher();
+
+ // Handler only for dispatching accessibility events since we use event
+ // types as message types allowing us to remove messages per event type.
+ public Handler mEventDispatchHandler = new Handler(mMainHandler.getLooper()) {
@Override
public void handleMessage(Message message) {
- final int type = message.what;
- switch (type) {
- case MSG_ON_GESTURE: {
- final int gestureId = message.arg1;
- notifyGestureInternal(gestureId);
- } break;
- case MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: {
- notifyClearAccessibilityNodeInfoCacheInternal();
- } break;
- default: {
- final int eventType = type;
- notifyAccessibilityEventInternal(eventType);
- } break;
- }
+ final int eventType = message.what;
+ notifyAccessibilityEventInternal(eventType);
}
};
+ // Handler for scheduling method invocations on the main thread.
+ public InvocationHandler mInvocationHandler = new InvocationHandler(
+ mMainHandler.getLooper());
+
public Service(int userId, ComponentName componentName,
AccessibilityServiceInfo accessibilityServiceInfo) {
mUserId = userId;
@@ -1703,6 +1758,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return false;
}
UserState userState = getUserStateLocked(mUserId);
+ mKeyEventDispatcher.flush();
if (!mIsAutomation) {
mContext.unbindService(this);
} else {
@@ -1718,6 +1774,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
@Override
+ public void setOnKeyEventResult(boolean handled, int sequence) {
+ mKeyEventDispatcher.setOnKeyEventResult(handled, sequence);
+ }
+
+ @Override
public AccessibilityServiceInfo getServiceInfo() {
synchronized (mLock) {
return mAccessibilityServiceInfo;
@@ -1756,11 +1817,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
addServiceLocked(this, userState);
if (userState.mBindingServices.contains(mComponentName)) {
userState.mBindingServices.remove(mComponentName);
- onUserStateChangedLocked(userState);
try {
- mServiceInterface.setConnection(this, mId);
+ mServiceInterface.setConnection(this, mId);
+ onUserStateChangedLocked(userState);
} catch (RemoteException re) {
- Slog.w(LOG_TAG, "Error while setting connection for service: " + service, re);
+ Slog.w(LOG_TAG, "Error while setting connection for service: "
+ + service, re);
+ binderDied();
}
} else {
binderDied();
@@ -2109,6 +2172,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public void binderDied() {
synchronized (mLock) {
+ mKeyEventDispatcher.flush();
UserState userState = getUserStateLocked(mUserId);
// The death recipient is unregistered in removeServiceLocked
removeServiceLocked(this, userState);
@@ -2141,12 +2205,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int what = eventType;
if (oldEvent != null) {
- mHandler.removeMessages(what);
+ mEventDispatchHandler.removeMessages(what);
oldEvent.recycle();
}
- Message message = mHandler.obtainMessage(what);
- mHandler.sendMessageDelayed(message, mNotificationTimeout);
+ Message message = mEventDispatchHandler.obtainMessage(what);
+ mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout);
}
}
@@ -2211,11 +2275,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
public void notifyGesture(int gestureId) {
- mHandler.obtainMessage(MSG_ON_GESTURE, gestureId, 0).sendToTarget();
+ mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE,
+ gestureId, 0).sendToTarget();
+ }
+
+ public void notifyKeyEvent(KeyEvent event, int policyFlags) {
+ mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_KEY_EVENT,
+ policyFlags, 0, event).sendToTarget();
}
public void notifyClearAccessibilityNodeInfoCache() {
- mHandler.sendEmptyMessage(MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE);
+ mInvocationHandler.sendEmptyMessage(
+ InvocationHandler.MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE);
}
private void notifyGestureInternal(int gestureId) {
@@ -2230,6 +2301,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
+ private void notifyKeyEventInternal(KeyEvent event, int policyFlags) {
+ mKeyEventDispatcher.notifyKeyEvent(event, policyFlags);
+ }
+
private void notifyClearAccessibilityNodeInfoCacheInternal() {
IAccessibilityServiceClient listener = mServiceInterface;
if (listener != null) {
@@ -2339,6 +2414,179 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
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_ON_KEY_EVENT_TIMEOUT = 4;
+
+ public InvocationHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ final int type = message.what;
+ switch (type) {
+ case MSG_ON_GESTURE: {
+ 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();
+ } break;
+ case MSG_ON_KEY_EVENT_TIMEOUT: {
+ PendingEvent eventState = (PendingEvent) message.obj;
+ setOnKeyEventResult(false, eventState.sequence);
+ } break;
+ default: {
+ throw new IllegalArgumentException("Unknown message: " + type);
+ }
+ }
+ }
+ }
+
+ private final class KeyEventDispatcher {
+
+ private static final long ON_KEY_EVENT_TIMEOUT_MILLIS = 500;
+
+ private PendingEvent mPendingEvents;
+
+ private final InputEventConsistencyVerifier mSentEventsVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled()
+ ? new InputEventConsistencyVerifier(
+ this, 0, KeyEventDispatcher.class.getSimpleName()) : null;
+
+ public void notifyKeyEvent(KeyEvent event, int policyFlags) {
+ final PendingEvent pendingEvent;
+
+ synchronized (mLock) {
+ pendingEvent = addPendingEventLocked(event, policyFlags);
+ }
+
+ Message message = mInvocationHandler.obtainMessage(
+ InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT, pendingEvent);
+ mInvocationHandler.sendMessageDelayed(message, ON_KEY_EVENT_TIMEOUT_MILLIS);
+
+ try {
+ // Accessibility services are exclusively not in the system
+ // process, therefore no need to clone the motion event to
+ // prevent tampering. It will be cloned in the IPC call.
+ mServiceInterface.onKeyEvent(pendingEvent.event, pendingEvent.sequence);
+ } catch (RemoteException re) {
+ setOnKeyEventResult(false, pendingEvent.sequence);
+ }
+ }
+
+ public void setOnKeyEventResult(boolean handled, int sequence) {
+ synchronized (mLock) {
+ PendingEvent pendingEvent = removePendingEventLocked(sequence);
+ if (pendingEvent != null) {
+ mInvocationHandler.removeMessages(
+ InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT,
+ pendingEvent);
+ pendingEvent.handled = handled;
+ finishPendingEventLocked(pendingEvent);
+ }
+ }
+ }
+
+ public void flush() {
+ synchronized (mLock) {
+ cancelAllPendingEventsLocked();
+ if (mSentEventsVerifier != null) {
+ mSentEventsVerifier.reset();
+ }
+ }
+ }
+
+ private PendingEvent addPendingEventLocked(KeyEvent event, int policyFlags) {
+ final int sequence = event.getSequenceNumber();
+ PendingEvent pendingEvent = obtainPendingEventLocked(event, policyFlags, sequence);
+ pendingEvent.next = mPendingEvents;
+ mPendingEvents = pendingEvent;
+ return pendingEvent;
+ }
+
+ private PendingEvent removePendingEventLocked(int sequence) {
+ PendingEvent previous = null;
+ PendingEvent current = mPendingEvents;
+
+ while (current != null) {
+ if (current.sequence == sequence) {
+ if (previous != null) {
+ previous.next = current.next;
+ } else {
+ mPendingEvents = current.next;
+ }
+ current.next = null;
+ return current;
+ }
+ previous = current;
+ current = current.next;
+ }
+ return null;
+ }
+
+ private void finishPendingEventLocked(PendingEvent pendingEvent) {
+ if (!pendingEvent.handled) {
+ sendKeyEventToInputFilter(pendingEvent.event, pendingEvent.policyFlags);
+ }
+ // Nullify the event since we do not want it to be
+ // recycled yet. It will be sent to the input filter.
+ pendingEvent.event = null;
+ recyclePendingEventLocked(pendingEvent);
+ }
+
+ private void sendKeyEventToInputFilter(KeyEvent event, int policyFlags) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Injecting event: " + event);
+ }
+ if (mSentEventsVerifier != null) {
+ mSentEventsVerifier.onKeyEvent(event, 0);
+ }
+ policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
+ mMainHandler.obtainMessage(MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER,
+ policyFlags, 0, event).sendToTarget();
+ }
+
+ private void cancelAllPendingEventsLocked() {
+ while (mPendingEvents != null) {
+ PendingEvent pendingEvent = removePendingEventLocked(mPendingEvents.sequence);
+ pendingEvent.handled = false;
+ mInvocationHandler.removeMessages(InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT,
+ pendingEvent);
+ finishPendingEventLocked(pendingEvent);
+ }
+ }
+ }
+ }
+
+ private static final class PendingEvent {
+ PendingEvent next;
+
+ KeyEvent event;
+ int policyFlags;
+ int sequence;
+ boolean handled;
+
+ public void clear() {
+ if (event != null) {
+ event.recycle();
+ event = null;
+ }
+ next = null;
+ policyFlags = 0;
+ sequence = 0;
+ handled = false;
+ }
}
final class SecurityPolicy {
diff --git a/services/java/com/android/server/accessibility/EventStreamTransformation.java b/services/java/com/android/server/accessibility/EventStreamTransformation.java
index 3289a15..8c93e7b 100644
--- a/services/java/com/android/server/accessibility/EventStreamTransformation.java
+++ b/services/java/com/android/server/accessibility/EventStreamTransformation.java
@@ -57,7 +57,7 @@ import android.view.accessibility.AccessibilityEvent;
interface EventStreamTransformation {
/**
- * Receives motion event. Passed are the event transformed by previous
+ * Receives a motion event. Passed are the event transformed by previous
* transformations and the raw event to which no transformations have
* been applied.
*
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 14d808f..fd7cd78 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -22,6 +22,7 @@ import android.accounts.AccountAndUser;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorDescription;
+import android.accounts.CantAddAccountActivity;
import android.accounts.GrantCredentialsPermissionActivity;
import android.accounts.IAccountAuthenticator;
import android.accounts.IAccountAuthenticatorResponse;
@@ -1456,6 +1457,14 @@ public class AccountManagerService
"User is not allowed to add an account!");
} catch (RemoteException re) {
}
+ Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
+ cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ long identityToken = clearCallingIdentity();
+ try {
+ mContext.startActivityAsUser(cantAddAccount, UserHandle.CURRENT);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
return;
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 7710f13..bc1df85 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -936,6 +936,12 @@ public final class ActivityManagerService extends ActivityManagerNative
CompatModeDialog mCompatModeDialog;
long mLastMemUsageReportTime = 0;
+ /**
+ * Flag whether the current user is a "monkey", i.e. whether
+ * the UI is driven by a UI automation tool.
+ */
+ private boolean mUserIsMonkey;
+
final Handler mHandler = new Handler() {
//public Handler() {
// if (localLOGV) Slog.v(TAG, "Handler started!");
@@ -3858,6 +3864,9 @@ public final class ActivityManagerService extends ActivityManagerNative
if (app.userId != userId) {
continue;
}
+ if (appId >= 0 && UserHandle.getAppId(app.uid) != appId) {
+ continue;
+ }
// Package has been specified, we want to hit all processes
// that match it. We need to qualify this by the processes
// that are running under the specified app and user ID.
@@ -4574,7 +4583,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public String getCallingPackage(IBinder token) {
synchronized (this) {
ActivityRecord r = getCallingRecordLocked(token);
- return r != null && r.app != null ? r.info.packageName : null;
+ return r != null ? r.info.packageName : null;
}
}
@@ -7431,11 +7440,27 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ public void setUserIsMonkey(boolean userIsMonkey) {
+ synchronized (this) {
+ synchronized (mPidsSelfLocked) {
+ final int callingPid = Binder.getCallingPid();
+ ProcessRecord precessRecord = mPidsSelfLocked.get(callingPid);
+ if (precessRecord == null) {
+ throw new SecurityException("Unknown process: " + callingPid);
+ }
+ if (precessRecord.instrumentationUiAutomationConnection == null) {
+ throw new SecurityException("Only an instrumentation process "
+ + "with a UiAutomation can call setUserIsMonkey");
+ }
+ }
+ mUserIsMonkey = userIsMonkey;
+ }
+ }
+
public boolean isUserAMonkey() {
- // For now the fact that there is a controller implies
- // we have a monkey.
synchronized (this) {
- return mController != null;
+ // If there is a controller also implies the user is a monkey.
+ return (mUserIsMonkey || mController != null);
}
}
@@ -7733,6 +7758,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
+ public void killUid(int uid, String reason) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("killUid only available to the system");
+ }
+ synchronized (this) {
+ killPackageProcessesLocked(null, UserHandle.getAppId(uid), UserHandle.getUserId(uid),
+ ProcessList.FOREGROUND_APP_ADJ-1, false, true, true, false,
+ reason != null ? reason : "kill uid");
+ }
+ }
+
+ @Override
public boolean killProcessesBelowForeground(String reason) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("killProcessesBelowForeground() only available to system");
@@ -8350,13 +8387,13 @@ public final class ActivityManagerService extends ActivityManagerNative
final String processName = app == null ? "system_server"
: (r == null ? "unknown" : r.processName);
- handleApplicationCrashInner(r, processName, crashInfo);
+ handleApplicationCrashInner("crash", r, processName, crashInfo);
}
/* Native crash reporting uses this inner version because it needs to be somewhat
* decoupled from the AM-managed cleanup lifecycle
*/
- void handleApplicationCrashInner(ProcessRecord r, String processName,
+ void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
ApplicationErrorReport.CrashInfo crashInfo) {
EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
UserHandle.getUserId(Binder.getCallingUid()), processName,
@@ -8366,7 +8403,7 @@ public final class ActivityManagerService extends ActivityManagerNative
crashInfo.throwFileName,
crashInfo.throwLineNumber);
- addErrorToDropBox("crash", r, processName, null, null, null, null, null, crashInfo);
+ addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
crashApplication(r, crashInfo);
}
@@ -12420,6 +12457,9 @@ public final class ActivityManagerService extends ActivityManagerNative
} catch (RemoteException re) {
/* ignore */
}
+ // Only a UiAutomation can set this flag and now that
+ // it is finished we make sure it is reset to its default.
+ mUserIsMonkey = false;
}
app.instrumentationWatcher = null;
app.instrumentationUiAutomationConnection = null;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 3d7da7b..0f1700d 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -47,6 +47,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -2594,8 +2595,7 @@ final class ActivityStack {
}
boolean abort = !mService.mIntentFirewall.checkStartActivity(intent,
- callerApp==null?null:callerApp.info, callingPackage, callingUid, callingPid,
- resolvedType, aInfo);
+ callerApp==null?null:callerApp.info, callingUid, callingPid, resolvedType, aInfo);
if (mMainStack) {
if (mService.mController != null) {
@@ -4601,11 +4601,13 @@ final class ActivityStack {
private final void logStartActivity(int tag, ActivityRecord r,
TaskRecord task) {
+ final Uri data = r.intent.getData();
+ final String strData = data != null ? data.toSafeString() : null;
+
EventLog.writeEvent(tag,
r.userId, System.identityHashCode(r), task.taskId,
r.shortComponentName, r.intent.getAction(),
- r.intent.getType(), r.intent.getDataString(),
- r.intent.getFlags());
+ r.intent.getType(), strData, r.intent.getFlags());
}
/**
diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java
index e83433f..0688c50 100644
--- a/services/java/com/android/server/am/NativeCrashListener.java
+++ b/services/java/com/android/server/am/NativeCrashListener.java
@@ -82,7 +82,7 @@ class NativeCrashListener extends Thread {
ci.stackTrace = mCrashReport;
if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");
- mAm.handleApplicationCrashInner(mApp, mApp.processName, ci);
+ mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci);
if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");
} catch (Exception e) {
Slog.e(TAG, "Unable to report native crash", e);
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 8ff1c7d..fccaab5 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -376,37 +376,37 @@ class ServiceRecord extends Binder {
// icon, but this used to be able to slip through, so for
// those dirty apps give it the app's icon.
foregroundNoti.icon = appInfo.icon;
- if (foregroundNoti.contentView == null) {
- // In this case the app may not have specified a
- // content view... so we'll give them something to show.
- CharSequence appName = appInfo.loadLabel(
- ams.mContext.getPackageManager());
- if (appName == null) {
- appName = appInfo.packageName;
- }
- Context ctx = null;
- try {
- ctx = ams.mContext.createPackageContext(
- appInfo.packageName, 0);
- Intent runningIntent = new Intent(
- Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- runningIntent.setData(Uri.fromParts("package",
- appInfo.packageName, null));
- PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
- runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- foregroundNoti.setLatestEventInfo(ctx,
- ams.mContext.getString(
- com.android.internal.R.string
- .app_running_notification_title,
- appName),
- ams.mContext.getString(
- com.android.internal.R.string
- .app_running_notification_text,
- appName),
- pi);
- } catch (PackageManager.NameNotFoundException e) {
- foregroundNoti.icon = 0;
- }
+
+ // Do not allow apps to present a sneaky invisible content view either.
+ foregroundNoti.contentView = null;
+ foregroundNoti.bigContentView = null;
+ CharSequence appName = appInfo.loadLabel(
+ ams.mContext.getPackageManager());
+ if (appName == null) {
+ appName = appInfo.packageName;
+ }
+ Context ctx = null;
+ try {
+ ctx = ams.mContext.createPackageContext(
+ appInfo.packageName, 0);
+ Intent runningIntent = new Intent(
+ Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ runningIntent.setData(Uri.fromParts("package",
+ appInfo.packageName, null));
+ PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
+ runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ foregroundNoti.setLatestEventInfo(ctx,
+ ams.mContext.getString(
+ com.android.internal.R.string
+ .app_running_notification_title,
+ appName),
+ ams.mContext.getString(
+ com.android.internal.R.string
+ .app_running_notification_text,
+ appName),
+ pi);
+ } catch (PackageManager.NameNotFoundException e) {
+ foregroundNoti.icon = 0;
}
}
if (foregroundNoti.icon == 0) {
diff --git a/services/java/com/android/server/content/ContentService.java b/services/java/com/android/server/content/ContentService.java
index 68cf5fc..f82cf01 100644
--- a/services/java/com/android/server/content/ContentService.java
+++ b/services/java/com/android/server/content/ContentService.java
@@ -459,7 +459,7 @@ public final class ContentService extends IContentService.Stub {
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- return syncManager.getSyncStorageEngine().getIsSyncable(
+ return syncManager.getIsSyncable(
account, userId, providerName);
}
} finally {
diff --git a/services/java/com/android/server/content/SyncManager.java b/services/java/com/android/server/content/SyncManager.java
index b3f9bf1..1c883ec 100644
--- a/services/java/com/android/server/content/SyncManager.java
+++ b/services/java/com/android/server/content/SyncManager.java
@@ -21,6 +21,7 @@ import android.accounts.AccountAndUser;
import android.accounts.AccountManager;
import android.app.ActivityManager;
import android.app.AlarmManager;
+import android.app.AppGlobals;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -41,6 +42,7 @@ import android.content.SyncInfo;
import android.content.SyncResult;
import android.content.SyncStatusInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.RegisteredServicesCache;
@@ -491,6 +493,36 @@ public class SyncManager {
return mSyncStorageEngine;
}
+ public int getIsSyncable(Account account, int userId, String providerName) {
+ int isSyncable = mSyncStorageEngine.getIsSyncable(account, userId, providerName);
+ UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
+
+ // If it's not a restricted user, return isSyncable
+ if (userInfo == null || !userInfo.isRestricted()) return isSyncable;
+
+ // Else check if the sync adapter has opted-in or not
+ RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
+ mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(providerName, account.type), userId);
+ if (syncAdapterInfo == null) return isSyncable;
+
+ PackageInfo pInfo = null;
+ try {
+ pInfo = AppGlobals.getPackageManager().getPackageInfo(
+ syncAdapterInfo.componentName.getPackageName(), 0, userId);
+ if (pInfo == null) return isSyncable;
+ } catch (RemoteException re) {
+ // Shouldn't happen
+ return isSyncable;
+ }
+ if (pInfo.restrictedAccountType != null
+ && pInfo.restrictedAccountType.equals(account.type)) {
+ return isSyncable;
+ } else {
+ return 0;
+ }
+ }
+
private void ensureAlarmService() {
if (mAlarmService == null) {
mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
@@ -608,7 +640,7 @@ public class SyncManager {
}
for (String authority : syncableAuthorities) {
- int isSyncable = mSyncStorageEngine.getIsSyncable(account.account, account.userId,
+ int isSyncable = getIsSyncable(account.account, account.userId,
authority);
if (isSyncable == 0) {
continue;
@@ -1930,7 +1962,7 @@ public class SyncManager {
continue;
}
- if (mSyncStorageEngine.getIsSyncable(info.account, info.userId, info.authority)
+ if (getIsSyncable(info.account, info.userId, info.authority)
== 0) {
continue;
}
@@ -2069,7 +2101,7 @@ public class SyncManager {
}
// drop this sync request if it isn't syncable
- int syncableState = mSyncStorageEngine.getIsSyncable(
+ int syncableState = getIsSyncable(
op.account, op.userId, op.authority);
if (syncableState == 0) {
operationIterator.remove();
diff --git a/services/java/com/android/server/firewall/AndFilter.java b/services/java/com/android/server/firewall/AndFilter.java
index cabf00b..e4276d0 100644
--- a/services/java/com/android/server/firewall/AndFilter.java
+++ b/services/java/com/android/server/firewall/AndFilter.java
@@ -26,11 +26,10 @@ import java.io.IOException;
class AndFilter extends FilterList {
@Override
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
- String callerPackage, int callerUid, int callerPid, String resolvedType,
- ApplicationInfo resolvedApp) {
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
for (int i=0; i<children.size(); i++) {
- if (!children.get(i).matches(ifw, intent, callerApp, callerPackage, callerUid,
- callerPid, resolvedType, resolvedApp)) {
+ if (!children.get(i).matches(ifw, intent, callerApp, callerUid, callerPid, resolvedType,
+ resolvedApp)) {
return false;
}
}
diff --git a/services/java/com/android/server/firewall/CategoryFilter.java b/services/java/com/android/server/firewall/CategoryFilter.java
index d5e9fe8..4938cb8 100644
--- a/services/java/com/android/server/firewall/CategoryFilter.java
+++ b/services/java/com/android/server/firewall/CategoryFilter.java
@@ -34,7 +34,7 @@ class CategoryFilter implements Filter {
}
@Override
- public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, String callerPackage,
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
Set<String> categories = intent.getCategories();
if (categories == null) {
diff --git a/services/java/com/android/server/firewall/Filter.java b/services/java/com/android/server/firewall/Filter.java
index 7639466..0e783e8 100644
--- a/services/java/com/android/server/firewall/Filter.java
+++ b/services/java/com/android/server/firewall/Filter.java
@@ -26,17 +26,14 @@ interface Filter {
* @param ifw The IntentFirewall instance
* @param intent The intent being started/bound/broadcast
* @param callerApp An ApplicationInfo of an application in the caller's process. This may not
- * be the specific app that is actually sending the intent. This also may be
- * null, if the caller is the system process, or an unrecognized process (e.g.
- * am start)
- * @param callerPackage The package name of the component sending the intent. This value is
-* provided by the caller and might be forged/faked.
+ * be the specific app that is actually sending the intent. This also may be
+ * null, if the caller is the system process, or an unrecognized process (e.g.
+ * am start)
* @param callerUid
* @param callerPid
* @param resolvedType The resolved mime type of the intent
* @param resolvedApp The application that contains the resolved component that the intent is
*/
boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
- String callerPackage, int callerUid, int callerPid, String resolvedType,
- ApplicationInfo resolvedApp);
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp);
}
diff --git a/services/java/com/android/server/firewall/IntentFirewall.java b/services/java/com/android/server/firewall/IntentFirewall.java
index ebbbd86..08e6b45 100644
--- a/services/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/java/com/android/server/firewall/IntentFirewall.java
@@ -16,18 +16,21 @@
package com.android.server.firewall;
+import android.app.AppGlobals;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.os.Environment;
-import android.os.ServiceManager;
+import android.os.RemoteException;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.util.XmlUtils;
+import com.android.server.EventLogTags;
import com.android.server.IntentResolver;
-import com.android.server.pm.PackageManagerService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -42,13 +45,22 @@ import java.util.List;
public class IntentFirewall {
private static final String TAG = "IntentFirewall";
- private static final String RULES_FILENAME = "ifw.xml";
+ // e.g. /data/system/ifw/ifw.xml or /data/secure/system/ifw/ifw.xml
+ private static final File RULES_FILE =
+ new File(Environment.getSystemSecureDirectory(), "ifw/ifw.xml");
+
+ private static final int LOG_PACKAGES_MAX_LENGTH = 150;
+ private static final int LOG_PACKAGES_SUFFICIENT_LENGTH = 125;
private static final String TAG_RULES = "rules";
private static final String TAG_ACTIVITY = "activity";
private static final String TAG_SERVICE = "service";
private static final String TAG_BROADCAST = "broadcast";
+ private static final int TYPE_ACTIVITY = 0;
+ private static final int TYPE_SERVICE = 1;
+ private static final int TYPE_BROADCAST = 2;
+
private static final HashMap<String, FilterFactory> factoryMap;
private final AMSInterface mAms;
@@ -74,7 +86,6 @@ public class IntentFirewall {
StringFilter.HOST,
StringFilter.MIME_TYPE,
StringFilter.PATH,
- StringFilter.SENDER_PACKAGE,
StringFilter.SSP,
CategoryFilter.FACTORY,
@@ -93,22 +104,19 @@ public class IntentFirewall {
public IntentFirewall(AMSInterface ams) {
mAms = ams;
- File dataSystemDir = new File(Environment.getDataDirectory(), "system");
- File rulesFile = new File(dataSystemDir, RULES_FILENAME);
- readRules(rulesFile);
+ readRules(getRulesFile());
}
- public boolean checkStartActivity(Intent intent, ApplicationInfo callerApp,
- String callerPackage, int callerUid, int callerPid, String resolvedType,
- ActivityInfo resolvedActivity) {
+ public boolean checkStartActivity(Intent intent, ApplicationInfo callerApp, int callerUid,
+ int callerPid, String resolvedType, ActivityInfo resolvedActivity) {
List<Rule> matchingRules = mActivityResolver.queryIntent(intent, resolvedType, false, 0);
boolean log = false;
boolean block = false;
for (int i=0; i< matchingRules.size(); i++) {
Rule rule = matchingRules.get(i);
- if (rule.matches(this, intent, callerApp, callerPackage, callerUid, callerPid,
- resolvedType, resolvedActivity.applicationInfo)) {
+ if (rule.matches(this, intent, callerApp, callerUid, callerPid, resolvedType,
+ resolvedActivity.applicationInfo)) {
block |= rule.getBlock();
log |= rule.getLog();
@@ -121,12 +129,85 @@ public class IntentFirewall {
}
if (log) {
- // TODO: log info about intent to event log
+ logIntent(TYPE_ACTIVITY, intent, callerUid, resolvedType);
}
return !block;
}
+ private static void logIntent(int intentType, Intent intent, int callerUid,
+ String resolvedType) {
+ // The component shouldn't be null, but let's double check just to be safe
+ ComponentName cn = intent.getComponent();
+ String shortComponent = null;
+ if (cn != null) {
+ shortComponent = cn.flattenToShortString();
+ }
+
+ String callerPackages = null;
+ int callerPackageCount = 0;
+ IPackageManager pm = AppGlobals.getPackageManager();
+ if (pm != null) {
+ try {
+ String[] callerPackagesArray = pm.getPackagesForUid(callerUid);
+ if (callerPackagesArray != null) {
+ callerPackageCount = callerPackagesArray.length;
+ callerPackages = joinPackages(callerPackagesArray);
+ }
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Remote exception while retrieving packages", ex);
+ }
+ }
+
+ EventLogTags.writeIfwIntentMatched(intentType, shortComponent, callerUid,
+ callerPackageCount, callerPackages, intent.getAction(), resolvedType,
+ intent.getDataString(), intent.getFlags());
+ }
+
+ /**
+ * Joins a list of package names such that the resulting string is no more than
+ * LOG_PACKAGES_MAX_LENGTH.
+ *
+ * Only full package names will be added to the result, unless every package is longer than the
+ * limit, in which case one of the packages will be truncated and added. In this case, an
+ * additional '-' character will be added to the end of the string, to denote the truncation.
+ *
+ * If it encounters a package that won't fit in the remaining space, it will continue on to the
+ * next package, unless the total length of the built string so far is greater than
+ * LOG_PACKAGES_SUFFICIENT_LENGTH, in which case it will stop and return what it has.
+ */
+ private static String joinPackages(String[] packages) {
+ boolean first = true;
+ StringBuilder sb = new StringBuilder();
+ for (int i=0; i<packages.length; i++) {
+ String pkg = packages[i];
+
+ // + 1 length for the comma. This logic technically isn't correct for the first entry,
+ // but it's not critical.
+ if (sb.length() + pkg.length() + 1 < LOG_PACKAGES_MAX_LENGTH) {
+ if (!first) {
+ sb.append(',');
+ } else {
+ first = false;
+ }
+ sb.append(pkg);
+ } else if (sb.length() >= LOG_PACKAGES_SUFFICIENT_LENGTH) {
+ return sb.toString();
+ }
+ }
+ if (sb.length() == 0 && packages.length > 0) {
+ String pkg = packages[0];
+ // truncating from the end - the last part of the package name is more likely to be
+ // interesting/unique
+ return pkg.substring(pkg.length() - LOG_PACKAGES_MAX_LENGTH + 1) + '-';
+ }
+ return null;
+ }
+
+ public static File getRulesFile() {
+ return RULES_FILE;
+ }
+
private void readRules(File rulesFile) {
FileInputStream fis;
try {
@@ -309,7 +390,12 @@ public class IntentFirewall {
}
boolean signaturesMatch(int uid1, int uid2) {
- PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
- return pm.checkUidSignatures(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ return pm.checkUidSignatures(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Remote exception while checking signatures", ex);
+ return false;
+ }
}
}
diff --git a/services/java/com/android/server/firewall/NotFilter.java b/services/java/com/android/server/firewall/NotFilter.java
index 2ff108a..f0fc337 100644
--- a/services/java/com/android/server/firewall/NotFilter.java
+++ b/services/java/com/android/server/firewall/NotFilter.java
@@ -33,10 +33,9 @@ class NotFilter implements Filter {
@Override
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
- String callerPackage, int callerUid, int callerPid, String resolvedType,
- ApplicationInfo resolvedApp) {
- return !mChild.matches(ifw, intent, callerApp, callerPackage, callerUid, callerPid,
- resolvedType, resolvedApp);
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ return !mChild.matches(ifw, intent, callerApp, callerUid, callerPid, resolvedType,
+ resolvedApp);
}
public static final FilterFactory FACTORY = new FilterFactory("not") {
diff --git a/services/java/com/android/server/firewall/OrFilter.java b/services/java/com/android/server/firewall/OrFilter.java
index 1ed1c85..72db31e 100644
--- a/services/java/com/android/server/firewall/OrFilter.java
+++ b/services/java/com/android/server/firewall/OrFilter.java
@@ -26,11 +26,10 @@ import java.io.IOException;
class OrFilter extends FilterList {
@Override
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
- String callerPackage, int callerUid, int callerPid, String resolvedType,
- ApplicationInfo resolvedApp) {
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
for (int i=0; i<children.size(); i++) {
- if (children.get(i).matches(ifw, intent, callerApp, callerPackage, callerUid, callerPid,
- resolvedType, resolvedApp)) {
+ if (children.get(i).matches(ifw, intent, callerApp, callerUid, callerPid, resolvedType,
+ resolvedApp)) {
return true;
}
}
diff --git a/services/java/com/android/server/firewall/PortFilter.java b/services/java/com/android/server/firewall/PortFilter.java
index 2b2a198..fe7e085 100644
--- a/services/java/com/android/server/firewall/PortFilter.java
+++ b/services/java/com/android/server/firewall/PortFilter.java
@@ -42,8 +42,7 @@ class PortFilter implements Filter {
@Override
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
- String callerPackage, int callerUid, int callerPid, String resolvedType,
- ApplicationInfo resolvedApp) {
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
int port = -1;
Uri uri = intent.getData();
if (uri != null) {
diff --git a/services/java/com/android/server/firewall/SenderFilter.java b/services/java/com/android/server/firewall/SenderFilter.java
index 0b790bd..58bdd73 100644
--- a/services/java/com/android/server/firewall/SenderFilter.java
+++ b/services/java/com/android/server/firewall/SenderFilter.java
@@ -68,8 +68,7 @@ class SenderFilter {
private static final Filter SIGNATURE = new Filter() {
@Override
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
- String callerPackage, int callerUid, int callerPid, String resolvedType,
- ApplicationInfo resolvedApp) {
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
if (callerApp == null) {
return false;
}
@@ -80,8 +79,7 @@ class SenderFilter {
private static final Filter SYSTEM = new Filter() {
@Override
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
- String callerPackage, int callerUid, int callerPid, String resolvedType,
- ApplicationInfo resolvedApp) {
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
if (callerApp == null) {
// if callerApp is null, the caller is the system process
return false;
@@ -93,8 +91,7 @@ class SenderFilter {
private static final Filter SYSTEM_OR_SIGNATURE = new Filter() {
@Override
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
- String callerPackage, int callerUid, int callerPid, String resolvedType,
- ApplicationInfo resolvedApp) {
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
return isSystemApp(callerApp, callerUid, callerPid) ||
ifw.signaturesMatch(callerUid, resolvedApp.uid);
}
@@ -103,8 +100,7 @@ class SenderFilter {
private static final Filter USER_ID = new Filter() {
@Override
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
- String callerPackage, int callerUid, int callerPid, String resolvedType,
- ApplicationInfo resolvedApp) {
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
// This checks whether the caller is either the system process, or has the same user id
// I.e. the same app, or an app that uses the same shared user id.
// This is the same set of applications that would be able to access the component if
diff --git a/services/java/com/android/server/firewall/SenderPermissionFilter.java b/services/java/com/android/server/firewall/SenderPermissionFilter.java
index 02d8b15..310da20 100644
--- a/services/java/com/android/server/firewall/SenderPermissionFilter.java
+++ b/services/java/com/android/server/firewall/SenderPermissionFilter.java
@@ -34,8 +34,7 @@ class SenderPermissionFilter implements Filter {
@Override
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
- String callerPackage, int callerUid, int callerPid, String resolvedType,
- ApplicationInfo resolvedApp) {
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
// We assume the component is exported here. If the component is not exported, then
// ActivityManager would only resolve to this component for callers from the same uid.
// In this case, it doesn't matter whether the component is exported or not.
diff --git a/services/java/com/android/server/firewall/StringFilter.java b/services/java/com/android/server/firewall/StringFilter.java
index de5a69f..ed5d3f3 100644
--- a/services/java/com/android/server/firewall/StringFilter.java
+++ b/services/java/com/android/server/firewall/StringFilter.java
@@ -119,10 +119,9 @@ abstract class StringFilter implements Filter {
protected abstract boolean matchesValue(String value);
@Override
- public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, String callerPackage,
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
- String value = mValueProvider.getValue(intent, callerApp, callerPackage, resolvedType,
- resolvedApp);
+ String value = mValueProvider.getValue(intent, callerApp, resolvedType, resolvedApp);
return matchesValue(value);
}
@@ -137,7 +136,7 @@ abstract class StringFilter implements Filter {
}
public abstract String getValue(Intent intent, ApplicationInfo callerApp,
- String callerPackage, String resolvedType, ApplicationInfo resolvedApp);
+ String resolvedType, ApplicationInfo resolvedApp);
}
private static class EqualsFilter extends StringFilter {
@@ -231,8 +230,8 @@ abstract class StringFilter implements Filter {
public static final ValueProvider COMPONENT = new ValueProvider("component") {
@Override
- public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
- String resolvedType, ApplicationInfo resolvedApp) {
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
ComponentName cn = intent.getComponent();
if (cn != null) {
return cn.flattenToString();
@@ -243,8 +242,8 @@ abstract class StringFilter implements Filter {
public static final ValueProvider COMPONENT_NAME = new ValueProvider("component-name") {
@Override
- public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
- String resolvedType, ApplicationInfo resolvedApp) {
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
ComponentName cn = intent.getComponent();
if (cn != null) {
return cn.getClassName();
@@ -255,8 +254,8 @@ abstract class StringFilter implements Filter {
public static final ValueProvider COMPONENT_PACKAGE = new ValueProvider("component-package") {
@Override
- public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
- String resolvedType, ApplicationInfo resolvedApp) {
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
ComponentName cn = intent.getComponent();
if (cn != null) {
return cn.getPackageName();
@@ -265,28 +264,18 @@ abstract class StringFilter implements Filter {
}
};
- public static final ValueProvider SENDER_PACKAGE = new ValueProvider("sender-package") {
- @Override
- public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
- String resolvedType, ApplicationInfo resolvedApp) {
- // TODO: We can't trust this value, so maybe should check all packages in the caller process?
- return callerPackage;
- }
- };
-
-
public static final FilterFactory ACTION = new ValueProvider("action") {
@Override
- public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
- String resolvedType, ApplicationInfo resolvedApp) {
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
return intent.getAction();
}
};
public static final ValueProvider DATA = new ValueProvider("data") {
@Override
- public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
- String resolvedType, ApplicationInfo resolvedApp) {
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
Uri data = intent.getData();
if (data != null) {
return data.toString();
@@ -297,16 +286,16 @@ abstract class StringFilter implements Filter {
public static final ValueProvider MIME_TYPE = new ValueProvider("mime-type") {
@Override
- public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
- String resolvedType, ApplicationInfo resolvedApp) {
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
return resolvedType;
}
};
public static final ValueProvider SCHEME = new ValueProvider("scheme") {
@Override
- public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
- String resolvedType, ApplicationInfo resolvedApp) {
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
Uri data = intent.getData();
if (data != null) {
return data.getScheme();
@@ -317,8 +306,8 @@ abstract class StringFilter implements Filter {
public static final ValueProvider SSP = new ValueProvider("scheme-specific-part") {
@Override
- public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
- String resolvedType, ApplicationInfo resolvedApp) {
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
Uri data = intent.getData();
if (data != null) {
return data.getSchemeSpecificPart();
@@ -329,8 +318,8 @@ abstract class StringFilter implements Filter {
public static final ValueProvider HOST = new ValueProvider("host") {
@Override
- public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
- String resolvedType, ApplicationInfo resolvedApp) {
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
Uri data = intent.getData();
if (data != null) {
return data.getHost();
@@ -341,8 +330,8 @@ abstract class StringFilter implements Filter {
public static final ValueProvider PATH = new ValueProvider("path") {
@Override
- public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
- String resolvedType, ApplicationInfo resolvedApp) {
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
Uri data = intent.getData();
if (data != null) {
return data.getPath();
diff --git a/services/java/com/android/server/location/GeofenceProxy.java b/services/java/com/android/server/location/GeofenceProxy.java
new file mode 100644
index 0000000..36e9fcc
--- /dev/null
+++ b/services/java/com/android/server/location/GeofenceProxy.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.location;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.hardware.location.GeofenceHardwareService;
+import android.hardware.location.IGeofenceHardware;
+import android.location.IGeofenceProvider;
+import android.location.IGpsGeofenceHardware;
+import android.content.Context;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import com.android.server.ServiceWatcher;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+public final class GeofenceProxy {
+ private static final String TAG = "GeofenceProxy";
+ private static final String SERVICE_ACTION =
+ "com.android.location.service.GeofenceProvider";
+ private ServiceWatcher mServiceWatcher;
+ private Context mContext;
+ private IGeofenceHardware mGeofenceHardware;
+ private IGpsGeofenceHardware mGpsGeofenceHardware;
+
+ private static final int GEOFENCE_PROVIDER_CONNECTED = 1;
+ private static final int GEOFENCE_HARDWARE_CONNECTED = 2;
+ private static final int GEOFENCE_HARDWARE_DISCONNECTED = 3;
+ private static final int GEOFENCE_GPS_HARDWARE_CONNECTED = 4;
+ private static final int GEOFENCE_GPS_HARDWARE_DISCONNECTED = 5;
+
+ private Runnable mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mHandler.sendEmptyMessage(GEOFENCE_PROVIDER_CONNECTED);
+ }
+ };
+
+ public static GeofenceProxy createAndBind(Context context,
+ List<String> initialPackageNames, Handler handler, IGpsGeofenceHardware gpsGeofence) {
+ GeofenceProxy proxy = new GeofenceProxy(context, initialPackageNames, handler, gpsGeofence);
+ if (proxy.bindGeofenceProvider()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ private GeofenceProxy(Context context, List<String> initialPackageName, Handler handler,
+ IGpsGeofenceHardware gpsGeofence) {
+ mContext = context;
+ mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, initialPackageName,
+ mRunnable, handler);
+ mGpsGeofenceHardware = gpsGeofence;
+ bindHardwareGeofence();
+ }
+
+ private boolean bindGeofenceProvider() {
+ return mServiceWatcher.start();
+ }
+
+ private IGeofenceProvider getGeofenceProviderService() {
+ return IGeofenceProvider.Stub.asInterface(mServiceWatcher.getBinder());
+ }
+
+ private void bindHardwareGeofence() {
+ mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.class),
+ mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.OWNER);
+ }
+
+ private ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mGeofenceHardware = IGeofenceHardware.Stub.asInterface(service);
+ mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_CONNECTED);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mGeofenceHardware = null;
+ mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_DISCONNECTED);
+ }
+ };
+
+ private void setGeofenceHardwareInProvider() {
+ try {
+ getGeofenceProviderService().setGeofenceHardware(mGeofenceHardware);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote Exception: setGeofenceHardwareInProvider: " + e);
+ }
+ }
+
+ private void setGpsGeofence() {
+ try {
+ mGeofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error while connecting to GeofenceHardwareService");
+ }
+ }
+
+
+ // This needs to be reworked, when more services get added,
+ // Might need a state machine or add a framework utility class,
+ private Handler mHandler = new Handler() {
+ private boolean mGeofenceHardwareConnected = false;
+ private boolean mGeofenceProviderConnected = false;
+
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case GEOFENCE_PROVIDER_CONNECTED:
+ mGeofenceProviderConnected = true;
+ if (mGeofenceHardwareConnected) {
+ setGeofenceHardwareInProvider();
+ }
+ break;
+ case GEOFENCE_HARDWARE_CONNECTED:
+ setGpsGeofence();
+ mGeofenceHardwareConnected = true;
+ if (mGeofenceProviderConnected) {
+ setGeofenceHardwareInProvider();
+ }
+ break;
+ case GEOFENCE_HARDWARE_DISCONNECTED:
+ mGeofenceHardwareConnected = false;
+ setGeofenceHardwareInProvider();
+ break;
+ }
+ }
+ };
+}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 3552b6a..1ebff67 100644
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -24,7 +24,10 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
+import android.hardware.location.GeofenceHardwareImpl;
+import android.hardware.location.IGeofenceHardware;
import android.location.Criteria;
+import android.location.IGpsGeofenceHardware;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
import android.location.ILocationManager;
@@ -314,6 +317,8 @@ public class GpsLocationProvider implements LocationProviderInterface {
// only modified on handler thread
private WorkSource mClientSource = new WorkSource();
+ private GeofenceHardwareImpl mGeofenceHardwareImpl;
+
private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
@Override
public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
@@ -367,6 +372,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
return mGpsStatusProvider;
}
+ public IGpsGeofenceHardware getGpsGeofenceProxy() {
+ return mGpsGeofenceBinder;
+ }
+
private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -918,6 +927,31 @@ public class GpsLocationProvider implements LocationProviderInterface {
return result;
}
+ private IGpsGeofenceHardware mGpsGeofenceBinder = new IGpsGeofenceHardware.Stub() {
+ public boolean isHardwareGeofenceSupported() {
+ return native_is_geofence_supported();
+ }
+
+ public boolean addCircularHardwareGeofence(int geofenceId, double latitude,
+ double longitude, double radius, int lastTransition, int monitorTransitions,
+ int notificationResponsiveness, int unknownTimer) {
+ return native_add_geofence(geofenceId, latitude, longitude, radius,
+ lastTransition, monitorTransitions, notificationResponsiveness, unknownTimer);
+ }
+
+ public boolean removeHardwareGeofence(int geofenceId) {
+ return native_remove_geofence(geofenceId);
+ }
+
+ public boolean pauseHardwareGeofence(int geofenceId) {
+ return native_pause_geofence(geofenceId);
+ }
+
+ public boolean resumeHardwareGeofence(int geofenceId, int monitorTransition) {
+ return native_resume_geofence(geofenceId, monitorTransition);
+ }
+ };
+
private boolean deleteAidingData(Bundle extras) {
int flags;
@@ -1017,6 +1051,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
return ((mEngineCapabilities & capability) != 0);
}
+
/**
* called from native code to update our position.
*/
@@ -1320,6 +1355,73 @@ public class GpsLocationProvider implements LocationProviderInterface {
sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
}
+ /**
+ * Called from native to report GPS Geofence transition
+ * All geofence callbacks are called on the same thread
+ */
+ private void reportGeofenceTransition(int geofenceId, int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp, int transition, long transitionTimestamp) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceTransition(geofenceId, flags, latitude, longitude,
+ altitude, speed, bearing, accuracy, timestamp, transition, transitionTimestamp);
+ }
+
+ /**
+ * called from native code to report GPS status change.
+ */
+ private void reportGeofenceStatus(int status, int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceStatus(status, flags, latitude, longitude, altitude,
+ speed, bearing, accuracy, timestamp);
+ }
+
+ /**
+ * called from native code - Geofence Add callback
+ */
+ private void reportGeofenceAddStatus(int geofenceId, int status) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceAddStatus(geofenceId, status);
+ }
+
+ /**
+ * called from native code - Geofence Remove callback
+ */
+ private void reportGeofenceRemoveStatus(int geofenceId, int status) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceRemoveStatus(geofenceId, status);
+ }
+
+ /**
+ * called from native code - Geofence Pause callback
+ */
+ private void reportGeofencePauseStatus(int geofenceId, int status) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofencePauseStatus(geofenceId, status);
+ }
+
+ /**
+ * called from native code - Geofence Resume callback
+ */
+ private void reportGeofenceResumeStatus(int geofenceId, int status) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceResumeStatus(geofenceId, status);
+ }
+
//=============================================================
// NI Client support
//=============================================================
@@ -1650,4 +1752,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
private native void native_update_network_state(boolean connected, int type,
boolean roaming, boolean available, String extraInfo, String defaultAPN);
+
+ // Hardware Geofence support.
+ private static native boolean native_is_geofence_supported();
+ private static native boolean native_add_geofence(int geofenceId, double latitude,
+ double longitude, double radius, int lastTransition,int monitorTransitions,
+ int notificationResponsivenes, int unknownTimer);
+ private static native boolean native_remove_geofence(int geofenceId);
+ private static native boolean native_resume_geofence(int geofenceId, int transitions);
+ private static native boolean native_pause_geofence(int geofenceId);
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index ca7bba2..1b8ee82 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -2308,6 +2308,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public void revokePermission(String packageName, String permissionName) {
+ int changedAppId = -1;
+
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
@@ -2335,6 +2337,30 @@ public class PackageManagerService extends IPackageManager.Stub {
gp.gids = removeInts(gp.gids, bp.gids);
}
mSettings.writeLPr();
+ changedAppId = ps.appId;
+ }
+ }
+
+ if (changedAppId >= 0) {
+ // We changed the perm on someone, kill its processes.
+ IActivityManager am = ActivityManagerNative.getDefault();
+ if (am != null) {
+ final int callingUserId = UserHandle.getCallingUserId();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ //XXX we should only revoke for the calling user's app permissions,
+ // but for now we impact all users.
+ //am.killUid(UserHandle.getUid(callingUserId, changedAppId),
+ // "revoke " + permissionName);
+ int[] users = sUserManager.getUserIds();
+ for (int user : users) {
+ am.killUid(UserHandle.getUid(user, changedAppId),
+ "revoke " + permissionName);
+ }
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
}
@@ -8223,6 +8249,24 @@ public class PackageManagerService extends IPackageManager.Stub {
updatePermissionsLPw(newPackage.packageName, newPackage,
UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
? UPDATE_PERMISSIONS_ALL : 0));
+ // For system-bundled packages, we assume that installing an upgraded version
+ // of the package implies that the user actually wants to run that new code,
+ // so we enable the package.
+ if (isSystemApp(newPackage)) {
+ // NB: implicit assumption that system package upgrades apply to all users
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
+ }
+ PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (ps != null) {
+ if (res.origUsers != null) {
+ for (int userHandle : res.origUsers) {
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
+ userHandle, installerPackageName);
+ }
+ }
+ }
+ }
res.name = pkgName;
res.uid = newPackage.applicationInfo.uid;
res.pkg = newPackage;
@@ -8647,16 +8691,10 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.writeLPr();
}
}
- // A user ID was deleted here. Go through all users and remove it from
- // KeyStore.
- final int appId = outInfo.removedAppId;
- if (appId != -1) {
- final KeyStore keyStore = KeyStore.getInstance();
- if (keyStore != null) {
- for (final int userId : sUserManager.getUserIds()) {
- keyStore.clearUid(UserHandle.getUid(userId, appId));
- }
- }
+ if (outInfo != null) {
+ // A user ID was deleted here. Go through all users and remove it
+ // from KeyStore.
+ removeKeystoreDataIfNeeded(UserHandle.USER_ALL, outInfo.removedAppId);
}
}
@@ -8816,6 +8854,7 @@ public class PackageManagerService extends IPackageManager.Stub {
outInfo.removedUsers = new int[] {removeUser};
}
mInstaller.clearUserData(packageName, removeUser);
+ removeKeystoreDataIfNeeded(removeUser, appId);
schedulePackageCleaning(packageName, removeUser, false);
return true;
}
@@ -8968,29 +9007,34 @@ public class PackageManagerService extends IPackageManager.Stub {
}
PackageParser.Package p;
boolean dataOnly = false;
+ final int appId;
synchronized (mPackages) {
p = mPackages.get(packageName);
- if(p == null) {
+ if (p == null) {
dataOnly = true;
PackageSetting ps = mSettings.mPackages.get(packageName);
- if((ps == null) || (ps.pkg == null)) {
- Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
+ if ((ps == null) || (ps.pkg == null)) {
+ Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
return false;
}
p = ps.pkg;
}
- }
-
- if (!dataOnly) {
- //need to check this only for fully installed applications
- if (p == null) {
- Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
- return false;
+ if (!dataOnly) {
+ // need to check this only for fully installed applications
+ if (p == null) {
+ Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
+ return false;
+ }
+ final ApplicationInfo applicationInfo = p.applicationInfo;
+ if (applicationInfo == null) {
+ Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
+ return false;
+ }
}
- final ApplicationInfo applicationInfo = p.applicationInfo;
- if (applicationInfo == null) {
- Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
- return false;
+ if (p != null && p.applicationInfo != null) {
+ appId = p.applicationInfo.uid;
+ } else {
+ appId = -1;
}
}
int retCode = mInstaller.clearUserData(packageName, userId);
@@ -8999,9 +9043,33 @@ public class PackageManagerService extends IPackageManager.Stub {
+ packageName);
return false;
}
+ removeKeystoreDataIfNeeded(userId, appId);
return true;
}
+ /**
+ * Remove entries from the keystore daemon. Will only remove it if the
+ * {@code appId} is valid.
+ */
+ private static void removeKeystoreDataIfNeeded(int userId, int appId) {
+ if (appId < 0) {
+ return;
+ }
+
+ final KeyStore keyStore = KeyStore.getInstance();
+ if (keyStore != null) {
+ if (userId == UserHandle.USER_ALL) {
+ for (final int individual : sUserManager.getUserIds()) {
+ keyStore.clearUid(UserHandle.getUid(individual, appId));
+ }
+ } else {
+ keyStore.clearUid(UserHandle.getUid(userId, appId));
+ }
+ } else {
+ Slog.w(TAG, "Could not contact keystore to clear entries for app id " + appId);
+ }
+ }
+
public void deleteApplicationCacheFiles(final String packageName,
final IPackageDataObserver observer) {
mContext.enforceCallingOrSelfPermission(
@@ -10655,19 +10723,18 @@ public class PackageManagerService extends IPackageManager.Stub {
|| mSettings.mReadExternalStorageEnforced != enforced) {
mSettings.mReadExternalStorageEnforced = enforced;
mSettings.writeLPr();
-
- // kill any non-foreground processes so we restart them and
- // grant/revoke the GID.
- final IActivityManager am = ActivityManagerNative.getDefault();
- if (am != null) {
- final long token = Binder.clearCallingIdentity();
- try {
- am.killProcessesBelowForeground("setPermissionEnforcement");
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
+ }
+ }
+ // kill any non-foreground processes so we restart them and
+ // grant/revoke the GID.
+ final IActivityManager am = ActivityManagerNative.getDefault();
+ if (am != null) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ am.killProcessesBelowForeground("setPermissionEnforcement");
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
} else {
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index aa1b2ff..df90a56 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -622,6 +622,8 @@ public class UserManagerService extends IUserManager.Stub {
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
+ writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS);
+ writeBoolean(serializer, restrictions, UserManager.DISALLOW_REMOVE_USER);
serializer.endTag(null, TAG_RESTRICTIONS);
}
serializer.endTag(null, TAG_USER);
@@ -742,6 +744,8 @@ public class UserManagerService extends IUserManager.Stub {
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
+ readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS);
+ readBoolean(parser, restrictions, UserManager.DISALLOW_REMOVE_USER);
}
}
}
@@ -963,7 +967,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public List<RestrictionEntry> getApplicationRestrictions(String packageName, int userId) {
if (UserHandle.getCallingUserId() != userId
- || Binder.getCallingUid() != getUidForPackage(packageName)) {
+ || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
checkManageUsersPermission("Only system can get restrictions for other users/apps");
}
synchronized (mPackagesLock) {
@@ -976,7 +980,7 @@ public class UserManagerService extends IUserManager.Stub {
public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
int userId) {
if (UserHandle.getCallingUserId() != userId
- || Binder.getCallingUid() != getUidForPackage(packageName)) {
+ || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
checkManageUsersPermission("Only system can set restrictions for other users/apps");
}
synchronized (mPackagesLock) {
@@ -986,11 +990,14 @@ public class UserManagerService extends IUserManager.Stub {
}
private int getUidForPackage(String packageName) {
+ long ident = Binder.clearCallingIdentity();
try {
return mContext.getPackageManager().getApplicationInfo(packageName,
PackageManager.GET_UNINSTALLED_PACKAGES).uid;
} catch (NameNotFoundException nnfe) {
return -1;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 2652739..1203e02 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -432,7 +432,7 @@ public final class PowerManagerService extends IPowerManager.Stub
mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
- SensorManager sensorManager = new SystemSensorManager(mHandler.getLooper());
+ SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
// The notifier runs on the system server's main looper so as not to interfere
// with the animations and other critical functions of the power manager.
diff --git a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
index d603cfa..9601e9a 100644
--- a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
+++ b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
@@ -56,9 +56,9 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
private static final String UPDATE_CERTIFICATE_KEY = "config_update_certificate";
- private final File updateDir;
- private final File updateContent;
- private final File updateVersion;
+ protected final File updateDir;
+ protected final File updateContent;
+ protected final File updateVersion;
public ConfigUpdateInstallReceiver(String updateDir, String updateContentPath,
String updateMetadataPath, String updateVersionPath) {
@@ -222,12 +222,10 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
return signer.verify(Base64.decode(signature.getBytes(), Base64.DEFAULT));
}
- private void writeUpdate(File dir, File file, byte[] content) throws IOException {
+ protected void writeUpdate(File dir, File file, byte[] content) throws IOException {
FileOutputStream out = null;
File tmp = null;
try {
- // create the temporary file
- tmp = File.createTempFile("journal", "", dir);
// create the parents for the destination file
File parent = file.getParentFile();
parent.mkdirs();
@@ -235,6 +233,8 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
if (!parent.exists()) {
throw new IOException("Failed to create directory " + parent.getCanonicalPath());
}
+ // create the temporary file
+ tmp = File.createTempFile("journal", "", dir);
// mark tmp -rw-r--r--
tmp.setReadable(true, false);
// write to it
diff --git a/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java b/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java
new file mode 100644
index 0000000..9185903
--- /dev/null
+++ b/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.updates;
+
+import com.android.server.firewall.IntentFirewall;
+
+public class IntentFirewallInstallReceiver extends ConfigUpdateInstallReceiver {
+
+ public IntentFirewallInstallReceiver() {
+ super(IntentFirewall.getRulesFile().getParent(), IntentFirewall.getRulesFile().getName(),
+ "metadata/", "version");
+ }
+}
diff --git a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
index 748849e..e8337f6 100644
--- a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
+++ b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
@@ -18,28 +18,127 @@ package com.android.server.updates;
import android.content.Context;
import android.content.Intent;
+import android.os.FileUtils;
import android.os.SELinux;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Base64;
import android.util.Slog;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
+import libcore.io.ErrnoException;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+
public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver {
+ private static final String TAG = "SELinuxPolicyInstallReceiver";
+
+ private static final String sepolicyPath = "sepolicy";
+ private static final String fileContextsPath = "file_contexts";
+ private static final String propertyContextsPath = "property_contexts";
+ private static final String seappContextsPath = "seapp_contexts";
+
public SELinuxPolicyInstallReceiver() {
- super("/data/security/", "sepolicy", "metadata/", "version");
+ super("/data/security/bundle", "sepolicy_bundle", "metadata/", "version");
}
- @Override
- protected void install(byte[] encodedContent, int version) throws IOException {
- super.install(Base64.decode(encodedContent, Base64.DEFAULT), version);
+ private void backupContexts(File contexts) {
+ new File(contexts, seappContextsPath).renameTo(
+ new File(contexts, seappContextsPath + "_backup"));
+
+ new File(contexts, propertyContextsPath).renameTo(
+ new File(contexts, propertyContextsPath + "_backup"));
+
+ new File(contexts, fileContextsPath).renameTo(
+ new File(contexts, fileContextsPath + "_backup"));
+
+ new File(contexts, sepolicyPath).renameTo(
+ new File(contexts, sepolicyPath + "_backup"));
+ }
+
+ private void copyUpdate(File contexts) {
+ new File(updateDir, seappContextsPath).renameTo(new File(contexts, seappContextsPath));
+ new File(updateDir, propertyContextsPath).renameTo(new File(contexts, propertyContextsPath));
+ new File(updateDir, fileContextsPath).renameTo(new File(contexts, fileContextsPath));
+ new File(updateDir, sepolicyPath).renameTo(new File(contexts, sepolicyPath));
+ }
+
+ private int readInt(BufferedInputStream reader) throws IOException {
+ int value = 0;
+ for (int i=0; i < 4; i++) {
+ value = (value << 8) | reader.read();
+ }
+ return value;
+ }
+
+ private int[] readChunkLengths(BufferedInputStream bundle) throws IOException {
+ int[] chunks = new int[4];
+ chunks[0] = readInt(bundle);
+ chunks[1] = readInt(bundle);
+ chunks[2] = readInt(bundle);
+ chunks[3] = readInt(bundle);
+ return chunks;
+ }
+
+ private void installFile(File destination, BufferedInputStream stream, int length)
+ throws IOException {
+ byte[] chunk = new byte[length];
+ stream.read(chunk, 0, length);
+ writeUpdate(updateDir, destination, Base64.decode(chunk, Base64.DEFAULT));
+ }
+
+ private void unpackBundle() throws IOException {
+ BufferedInputStream stream = new BufferedInputStream(new FileInputStream(updateContent));
+ int[] chunkLengths = readChunkLengths(stream);
+ installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[0]);
+ installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[1]);
+ installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[2]);
+ installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[3]);
+ }
+
+ private void applyUpdate() throws IOException, ErrnoException {
+ Slog.i(TAG, "Applying SELinux policy");
+ File contexts = new File(updateDir.getParentFile(), "contexts");
+ File current = new File(updateDir.getParentFile(), "current");
+ File update = new File(updateDir.getParentFile(), "update");
+ File tmp = new File(updateDir.getParentFile(), "tmp");
+ if (current.exists()) {
+ Libcore.os.symlink(updateDir.getPath(), update.getPath());
+ Libcore.os.rename(update.getPath(), current.getPath());
+ } else {
+ Libcore.os.symlink(updateDir.getPath(), current.getPath());
+ }
+ contexts.mkdirs();
+ backupContexts(contexts);
+ copyUpdate(contexts);
+ Libcore.os.symlink(contexts.getPath(), tmp.getPath());
+ Libcore.os.rename(tmp.getPath(), current.getPath());
+ SystemProperties.set("selinux.reload_policy", "1");
+ }
+
+ private void setEnforcingMode(Context context) {
+ boolean mode = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.SELINUX_STATUS, 0) == 1;
+ SELinux.setSELinuxEnforce(mode);
}
@Override
protected void postInstall(Context context, Intent intent) {
- boolean mode = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.SELINUX_STATUS, 0) == 1;
- SELinux.setSELinuxEnforce(mode);
+ try {
+ unpackBundle();
+ applyUpdate();
+ setEnforcingMode(context);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "SELinux policy update malformed: ", e);
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not update selinux policy: ", e);
+ } catch (ErrnoException e) {
+ Slog.e(TAG, "Could not update selinux policy: ", e);
+ }
}
}
diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java
index f8d5d2e..4d23e5c 100644
--- a/services/java/com/android/server/wifi/WifiService.java
+++ b/services/java/com/android/server/wifi/WifiService.java
@@ -734,7 +734,7 @@ public final class WifiService extends IWifiManager.Stub {
if (gateway instanceof Inet4Address) {
info.gateway = NetworkUtils.inetAddressToInt((Inet4Address)gateway);
}
- } else if (r.isHostRoute()) {
+ } else if (r.hasGateway() == false) {
LinkAddress dest = r.getDestination();
if (dest.getAddress() instanceof Inet4Address) {
info.netmask = NetworkUtils.prefixLengthToNetmaskInt(
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
index 297324b..6293dc6 100644
--- a/services/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -4,6 +4,7 @@ package com.android.server.wm;
import android.graphics.Matrix;
import android.util.Slog;
+import android.util.TimeUtils;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -30,6 +31,11 @@ public class AppWindowAnimator {
// Protect with mAnimator.
boolean freezingScreen;
+ /**
+ * How long we last kept the screen frozen.
+ */
+ int lastFreezeDuration;
+
// Offset to the window of all layers in the token, for use by
// AppWindowToken animations.
int animLayerAdjustment;
@@ -287,6 +293,10 @@ public class AppWindowAnimator {
pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen);
pw.print(" allDrawn="); pw.print(allDrawn);
pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment);
+ if (lastFreezeDuration != 0) {
+ pw.print(prefix); pw.print("lastFreezeDuration=");
+ TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println();
+ }
if (animating || animation != null) {
pw.print(prefix); pw.print("animating="); pw.println(animating);
pw.print(prefix); pw.print("animation="); pw.println(animation);
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 3964782..054a075 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -70,6 +70,7 @@ public class WindowAnimator {
int mAboveUniverseLayer = 0;
int mBulkUpdateParams = 0;
+ Object mLastWindowFreezeSource;
SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators =
new SparseArray<WindowAnimator.DisplayContentsAnimator>();
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index cbc42eb..1d1fda5 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -43,6 +43,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import android.app.AppOpsManager;
+import android.util.TimeUtils;
import android.view.IWindowId;
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
@@ -464,6 +465,9 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mTraversalScheduled = false;
boolean mDisplayFrozen = false;
+ long mDisplayFreezeTime = 0;
+ int mLastDisplayFreezeDuration = 0;
+ Object mLastFinishedFreezeSource = null;
boolean mWaitingForConfig = false;
boolean mWindowsFreezingScreen = false;
boolean mClientFreezingScreen = false;
@@ -582,6 +586,7 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mWallpaperForceHidingChanged = false;
boolean mWallpaperMayChange = false;
boolean mOrientationChangeComplete = true;
+ Object mLastWindowFreezeSource = null;
private Session mHoldScreen = null;
private boolean mObscured = false;
boolean mDimming = false;
@@ -3590,7 +3595,10 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
mCurConfiguration = new Configuration(config);
- mWaitingForConfig = false;
+ if (mWaitingForConfig) {
+ mWaitingForConfig = false;
+ mLastFinishedFreezeSource = "new-config";
+ }
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -4209,6 +4217,7 @@ public class WindowManagerService extends IWindowManager.Stub
w.mOrientationChanging = true;
mInnerFields.mOrientationChangeComplete = false;
}
+ w.mLastFreezeDuration = 0;
unfrozeWindows = true;
w.mDisplayContent.layoutNeeded = true;
}
@@ -4216,7 +4225,10 @@ public class WindowManagerService extends IWindowManager.Stub
if (force || unfrozeWindows) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "No longer freezing: " + wtoken);
wtoken.mAppAnimator.freezingScreen = false;
+ wtoken.mAppAnimator.lastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mDisplayFreezeTime);
mAppsFreezingScreen--;
+ mLastFinishedFreezeSource = wtoken;
}
if (unfreezeSurfaceNow) {
if (unfrozeWindows) {
@@ -4242,6 +4254,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (!wtoken.hiddenRequested) {
if (!wtoken.mAppAnimator.freezingScreen) {
wtoken.mAppAnimator.freezingScreen = true;
+ wtoken.mAppAnimator.lastFreezeDuration = 0;
mAppsFreezingScreen++;
if (mAppsFreezingScreen == 1) {
startFreezingDisplayLocked(false, 0, 0);
@@ -4750,6 +4763,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
if (mClientFreezingScreen) {
mClientFreezingScreen = false;
+ mLastFinishedFreezeSource = "client";
final long origId = Binder.clearCallingIdentity();
try {
stopFreezingDisplayLocked();
@@ -5365,6 +5379,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (maxLayer < winAnim.mSurfaceLayer) {
maxLayer = winAnim.mSurfaceLayer;
}
+ if (minLayer > winAnim.mSurfaceLayer) {
+ minLayer = winAnim.mSurfaceLayer;
+ }
// Don't include wallpaper in bounds calculation
if (!ws.mIsWallpaper) {
@@ -5377,17 +5394,14 @@ public class WindowManagerService extends IWindowManager.Stub
frame.union(left, top, right, bottom);
}
- if (ws.mAppToken != null && ws.mAppToken.token == appToken) {
- if (minLayer > ws.mWinAnimator.mSurfaceLayer) {
- minLayer = ws.mWinAnimator.mSurfaceLayer;
- }
- if (ws.isDisplayedLw()) {
- screenshotReady = true;
- }
- if (fullscreen) {
- // No point in continuing down through windows.
- break;
- }
+ if (ws.mAppToken != null && ws.mAppToken.token == appToken &&
+ ws.isDisplayedLw()) {
+ screenshotReady = true;
+ }
+
+ if (fullscreen) {
+ // No point in continuing down through windows.
+ break;
}
}
@@ -5461,14 +5475,12 @@ public class WindowManagerService extends IWindowManager.Stub
rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer);
}
} while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES);
- if (DEBUG_SCREENSHOT && retryCount > MAX_SCREENSHOT_RETRIES) {
- Slog.i(TAG, "Screenshot max retries " + retryCount + " of " + appToken + " appWin="
- + (appWin == null ? "null" : (appWin + " drawState="
- + appWin.mWinAnimator.mDrawState)));
- }
+ if (retryCount > MAX_SCREENSHOT_RETRIES) Slog.i(TAG, "Screenshot max retries " +
+ retryCount + " of " + appToken + " appWin=" + (appWin == null ?
+ "null" : (appWin + " drawState=" + appWin.mWinAnimator.mDrawState)));
if (rawss == null) {
- Slog.w(TAG, "Failure taking screenshot for (" + dw + "x" + dh
+ Slog.w(TAG, "Screenshot failure taking screenshot for (" + dw + "x" + dh
+ ") to layer " + maxLayer);
return null;
}
@@ -5481,7 +5493,7 @@ public class WindowManagerService extends IWindowManager.Stub
canvas.drawBitmap(rawss, matrix, null);
canvas.setBitmap(null);
- if (DEBUG_SCREENSHOT) {
+ if (true || DEBUG_SCREENSHOT) {
// TEST IF IT's ALL BLACK
int[] buffer = new int[bm.getWidth() * bm.getHeight()];
bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
@@ -5494,7 +5506,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (allBlack) {
Slog.i(TAG, "Screenshot " + appWin + " was all black! mSurfaceLayer=" +
- (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null"));
+ (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null") +
+ " minLayer=" + minLayer + " maxLayer=" + maxLayer);
}
}
@@ -5743,6 +5756,7 @@ public class WindowManagerService extends IWindowManager.Stub
w.mOrientationChanging = true;
mInnerFields.mOrientationChangeComplete = false;
}
+ w.mLastFreezeDuration = 0;
}
for (int i=mRotationWatchers.size()-1; i>=0; i--) {
@@ -6241,6 +6255,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (config == null && mWaitingForConfig) {
// Nothing changed but we are waiting for something... stop that!
mWaitingForConfig = false;
+ mLastFinishedFreezeSource = "new-config";
performLayoutAndPlaceSurfacesLocked();
}
return config;
@@ -7037,6 +7052,8 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState w = windows.get(i);
if (w.mOrientationChanging) {
w.mOrientationChanging = false;
+ w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mDisplayFreezeTime);
Slog.w(TAG, "Force clearing orientation change: " + w);
}
}
@@ -7113,6 +7130,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
if (mClientFreezingScreen) {
mClientFreezingScreen = false;
+ mLastFinishedFreezeSource = "client-timeout";
stopFreezingDisplayLocked();
}
}
@@ -8030,6 +8048,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Changing surface while display frozen: " + w);
w.mOrientationChanging = true;
+ w.mLastFreezeDuration = 0;
mInnerFields.mOrientationChangeComplete = false;
if (!mWindowsFreezingScreen) {
mWindowsFreezingScreen = true;
@@ -8418,6 +8437,8 @@ public class WindowManagerService extends IWindowManager.Stub
"Orientation not waiting for draw in "
+ w + ", surface " + winAnimator.mSurfaceControl);
w.mOrientationChanging = false;
+ w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mDisplayFreezeTime);
}
}
}
@@ -8931,6 +8952,8 @@ public class WindowManagerService extends IWindowManager.Stub
winAnimator.mSurfaceResized = false;
} catch (RemoteException e) {
win.mOrientationChanging = false;
+ win.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mDisplayFreezeTime);
}
mResizingWindows.remove(i);
}
@@ -8941,6 +8964,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (mInnerFields.mOrientationChangeComplete) {
if (mWindowsFreezingScreen) {
mWindowsFreezingScreen = false;
+ mLastFinishedFreezeSource = mInnerFields.mLastWindowFreezeSource;
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
}
stopFreezingDisplayLocked();
@@ -9227,6 +9251,7 @@ public class WindowManagerService extends IWindowManager.Stub
mInnerFields.mOrientationChangeComplete = false;
} else {
mInnerFields.mOrientationChangeComplete = true;
+ mInnerFields.mLastWindowFreezeSource = mAnimator.mLastWindowFreezeSource;
if (mWindowsFreezingScreen) {
doRequest = true;
}
@@ -9499,6 +9524,8 @@ public class WindowManagerService extends IWindowManager.Stub
mScreenFrozenLock.acquire();
mDisplayFrozen = true;
+ mDisplayFreezeTime = SystemClock.elapsedRealtime();
+ mLastFinishedFreezeSource = null;
mInputMonitor.freezeInputDispatchingLw();
@@ -9553,6 +9580,15 @@ public class WindowManagerService extends IWindowManager.Stub
}
mDisplayFrozen = false;
+ mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime);
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Screen frozen for ");
+ TimeUtils.formatDuration(mLastDisplayFreezeDuration, sb);
+ if (mLastFinishedFreezeSource != null) {
+ sb.append(" due to ");
+ sb.append(mLastFinishedFreezeSource);
+ }
+ Slog.i(TAG, sb.toString());
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
if (PROFILE_ORIENTATION) {
@@ -10077,6 +10113,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
pw.print(" mInTouchMode="); pw.print(mInTouchMode);
pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
+ pw.print(" mLastDisplayFreezeDuration=");
+ TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw);
+ if ( mLastFinishedFreezeSource != null) {
+ pw.print(" due to ");
+ pw.print(mLastFinishedFreezeSource);
+ }
+ pw.println();
if (dumpAll) {
pw.print(" mSystemDecorRect="); pw.print(mSystemDecorRect.toShortString());
pw.print(" mSystemDecorLayer="); pw.print(mSystemDecorLayer);
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 506fcec..788d514 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD;
@@ -26,6 +27,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import android.app.AppOpsManager;
import android.os.RemoteCallbackList;
+import android.util.TimeUtils;
import android.view.IWindowFocusObserver;
import android.view.IWindowId;
import com.android.server.input.InputWindowHandle;
@@ -266,6 +268,11 @@ final class WindowState implements WindowManagerPolicy.WindowState {
*/
boolean mOrientationChanging;
+ /**
+ * How long we last kept the screen frozen.
+ */
+ int mLastFreezeDuration;
+
/** Is this window now (or just being) removed? */
boolean mRemoved;
@@ -917,6 +924,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return mContentChanged && !mExiting && !mWinAnimator.mLastHidden && mService.okToDisplay()
&& (mFrame.top != mLastFrame.top
|| mFrame.left != mLastFrame.left)
+ && (mAttrs.privateFlags&PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
&& (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove());
}
@@ -1387,6 +1395,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
pw.print(" mAppFreezing="); pw.print(mAppFreezing);
pw.print(" mTurnOnScreen="); pw.println(mTurnOnScreen);
}
+ if (mLastFreezeDuration != 0) {
+ pw.print(prefix); pw.print("mLastFreezeDuration=");
+ TimeUtils.formatDuration(mLastFreezeDuration, pw); pw.println();
+ }
if (mHScale != 1 || mVScale != 1) {
pw.print(prefix); pw.print("mHScale="); pw.print(mHScale);
pw.print(" mVScale="); pw.println(mVScale);
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 3a9f7cb..c07174b 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -505,20 +505,20 @@ class WindowStateAnimator {
public void setAlpha(float alpha) {
super.setAlpha(alpha);
if (alpha != mSurfaceTraceAlpha) {
+ mSurfaceTraceAlpha = alpha;
Slog.v(SURFACE_TAG, "setAlpha: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mSurfaceTraceAlpha = alpha;
}
@Override
public void setLayer(int zorder) {
super.setLayer(zorder);
if (zorder != mLayer) {
+ mLayer = zorder;
Slog.v(SURFACE_TAG, "setLayer: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mLayer = zorder;
sSurfaces.remove(this);
int i;
@@ -535,20 +535,20 @@ class WindowStateAnimator {
public void setPosition(float x, float y) {
super.setPosition(x, y);
if (x != mPosition.x || y != mPosition.y) {
+ mPosition.set(x, y);
Slog.v(SURFACE_TAG, "setPosition: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mPosition.set(x, y);
}
@Override
public void setSize(int w, int h) {
super.setSize(w, h);
if (w != mSize.x || h != mSize.y) {
+ mSize.set(w, h);
Slog.v(SURFACE_TAG, "setSize: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mSize.set(w, h);
}
@Override
@@ -556,10 +556,10 @@ class WindowStateAnimator {
super.setWindowCrop(crop);
if (crop != null) {
if (!crop.equals(mWindowCrop)) {
+ mWindowCrop.set(crop);
Slog.v(SURFACE_TAG, "setWindowCrop: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mWindowCrop.set(crop);
}
}
@@ -567,28 +567,28 @@ class WindowStateAnimator {
public void setLayerStack(int layerStack) {
super.setLayerStack(layerStack);
if (layerStack != mLayerStack) {
+ mLayerStack = layerStack;
Slog.v(SURFACE_TAG, "setLayerStack: " + this + ". Called by " + Debug.getCallers(3));
}
- mLayerStack = layerStack;
}
@Override
public void hide() {
super.hide();
if (mShown) {
+ mShown = false;
Slog.v(SURFACE_TAG, "hide: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mShown = false;
}
@Override
public void show() {
super.show();
if (!mShown) {
+ mShown = true;
Slog.v(SURFACE_TAG, "show: " + this + ". Called by "
+ Debug.getCallers(3));
}
- mShown = true;
}
@Override
@@ -1307,6 +1307,7 @@ class WindowStateAnimator {
if (w.mOrientationChanging) {
if (!w.isDrawnLw()) {
mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
+ mAnimator.mLastWindowFreezeSource = w;
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation continue waiting for draw in " + w);
} else {
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index d097a93..b313d48 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -32,6 +32,7 @@ LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
libandroidfw \
libcutils \
+ liblog \
libhardware \
libhardware_legacy \
libnativehelper \
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
index 036fc43..98de12a 100644
--- a/services/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -43,6 +43,12 @@ static jmethodID method_reportNiNotification;
static jmethodID method_requestRefLocation;
static jmethodID method_requestSetID;
static jmethodID method_requestUtcTime;
+static jmethodID method_reportGeofenceTransition;
+static jmethodID method_reportGeofenceStatus;
+static jmethodID method_reportGeofenceAddStatus;
+static jmethodID method_reportGeofenceRemoveStatus;
+static jmethodID method_reportGeofencePauseStatus;
+static jmethodID method_reportGeofenceResumeStatus;
static const GpsInterface* sGpsInterface = NULL;
static const GpsXtraInterface* sGpsXtraInterface = NULL;
@@ -50,6 +56,7 @@ static const AGpsInterface* sAGpsInterface = NULL;
static const GpsNiInterface* sGpsNiInterface = NULL;
static const GpsDebugInterface* sGpsDebugInterface = NULL;
static const AGpsRilInterface* sAGpsRilInterface = NULL;
+static const GpsGeofencingInterface* sGpsGeofencingInterface = NULL;
// temporary storage for GPS callbacks
static GpsSvStatus sGpsSvStatus;
@@ -107,7 +114,7 @@ static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length)
static void set_capabilities_callback(uint32_t capabilities)
{
- ALOGD("set_capabilities_callback: %ld\n", capabilities);
+ ALOGD("set_capabilities_callback: %du\n", capabilities);
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(mCallbacksObj, method_setEngineCapabilities, capabilities);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -233,6 +240,97 @@ AGpsRilCallbacks sAGpsRilCallbacks = {
create_thread_callback,
};
+static void gps_geofence_transition_callback(int32_t geofence_id, GpsLocation* location,
+ int32_t transition, GpsUtcTime timestamp)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceTransition, geofence_id,
+ location->flags, (jdouble)location->latitude, (jdouble)location->longitude,
+ (jdouble)location->altitude,
+ (jfloat)location->speed, (jfloat)location->bearing,
+ (jfloat)location->accuracy, (jlong)location->timestamp,
+ transition, timestamp);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_status_callback(int32_t status, GpsLocation* location)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jint flags = 0;
+ jdouble latitude = 0;
+ jdouble longitude = 0;
+ jdouble altitude = 0;
+ jfloat speed = 0;
+ jfloat bearing = 0;
+ jfloat accuracy = 0;
+ jlong timestamp = 0;
+ if (location != NULL) {
+ flags = location->flags;
+ latitude = location->latitude;
+ longitude = location->longitude;
+ altitude = location->altitude;
+ speed = location->speed;
+ bearing = location->bearing;
+ accuracy = location->accuracy;
+ timestamp = location->timestamp;
+ }
+
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, status,
+ flags, latitude, longitude, altitude, speed, bearing, accuracy, timestamp);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_add_callback(int32_t geofence_id, int32_t status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (status != GPS_GEOFENCE_OPERATION_SUCCESS) {
+ ALOGE("Error in geofence_add_callback: %d\n", status);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceAddStatus, geofence_id, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_remove_callback(int32_t geofence_id, int32_t status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (status != GPS_GEOFENCE_OPERATION_SUCCESS) {
+ ALOGE("Error in geofence_remove_callback: %d\n", status);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceRemoveStatus, geofence_id, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_resume_callback(int32_t geofence_id, int32_t status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (status != GPS_GEOFENCE_OPERATION_SUCCESS) {
+ ALOGE("Error in geofence_resume_callback: %d\n", status);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceResumeStatus, geofence_id, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_pause_callback(int32_t geofence_id, int32_t status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (status != GPS_GEOFENCE_OPERATION_SUCCESS) {
+ ALOGE("Error in geofence_pause_callback: %d\n", status);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofencePauseStatus, geofence_id, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+GpsGeofenceCallbacks sGpsGeofenceCallbacks = {
+ gps_geofence_transition_callback,
+ gps_geofence_status_callback,
+ gps_geofence_add_callback,
+ gps_geofence_remove_callback,
+ gps_geofence_pause_callback,
+ gps_geofence_resume_callback,
+ create_thread_callback,
+};
+
static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
int err;
hw_module_t* module;
@@ -249,6 +347,18 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env,
method_requestRefLocation = env->GetMethodID(clazz,"requestRefLocation","(I)V");
method_requestSetID = env->GetMethodID(clazz,"requestSetID","(I)V");
method_requestUtcTime = env->GetMethodID(clazz,"requestUtcTime","()V");
+ method_reportGeofenceTransition = env->GetMethodID(clazz,"reportGeofenceTransition",
+ "(IIDDDFFFJIJ)V");
+ method_reportGeofenceStatus = env->GetMethodID(clazz,"reportGeofenceStatus",
+ "(IIDDDFFFJ)V");
+ method_reportGeofenceAddStatus = env->GetMethodID(clazz,"reportGeofenceAddStatus",
+ "(II)V");
+ method_reportGeofenceRemoveStatus = env->GetMethodID(clazz,"reportGeofenceRemoveStatus",
+ "(II)V");
+ method_reportGeofenceResumeStatus = env->GetMethodID(clazz,"reportGeofenceResumeStatus",
+ "(II)V");
+ method_reportGeofencePauseStatus = env->GetMethodID(clazz,"reportGeofencePauseStatus",
+ "(II)V");
err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
@@ -270,6 +380,8 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env,
(const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
sAGpsRilInterface =
(const AGpsRilInterface*)sGpsInterface->get_extension(AGPS_RIL_INTERFACE);
+ sGpsGeofencingInterface =
+ (const GpsGeofencingInterface*)sGpsInterface->get_extension(GPS_GEOFENCING_INTERFACE);
}
}
@@ -287,7 +399,7 @@ static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject o
if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)
return false;
- // if XTRA initialization fails we will disable it by sGpsXtraInterface to null,
+ // if XTRA initialization fails we will disable it by sGpsXtraInterface to NULL,
// but continue to allow the rest of the GPS interface to work.
if (sGpsXtraInterface && sGpsXtraInterface->init(&sGpsXtraCallbacks) != 0)
sGpsXtraInterface = NULL;
@@ -297,6 +409,8 @@ static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject o
sGpsNiInterface->init(&sGpsNiCallbacks);
if (sAGpsRilInterface)
sAGpsRilInterface->init(&sAGpsRilCallbacks);
+ if (sGpsGeofencingInterface)
+ sGpsGeofencingInterface->init(&sGpsGeofenceCallbacks);
return true;
}
@@ -565,6 +679,62 @@ static void android_location_GpsLocationProvider_update_network_state(JNIEnv* en
}
}
+static jboolean android_location_GpsLocationProvider_is_geofence_supported(JNIEnv* env,
+ jobject obj) {
+ if (sGpsGeofencingInterface != NULL) {
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_add_geofence(JNIEnv* env, jobject obj,
+ jint geofence_id, jdouble latitude, jdouble longitude, jdouble radius,
+ jint last_transition, jint monitor_transition, jint notification_responsiveness,
+ jint unknown_timer) {
+ if (sGpsGeofencingInterface != NULL) {
+ sGpsGeofencingInterface->add_geofence_area(geofence_id, latitude, longitude,
+ radius, last_transition, monitor_transition, notification_responsiveness,
+ unknown_timer);
+ return JNI_TRUE;
+ } else {
+ ALOGE("Geofence interface not available");
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_remove_geofence(JNIEnv* env, jobject obj,
+ jint geofence_id) {
+ if (sGpsGeofencingInterface != NULL) {
+ sGpsGeofencingInterface->remove_geofence_area(geofence_id);
+ return JNI_TRUE;
+ } else {
+ ALOGE("Geofence interface not available");
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_pause_geofence(JNIEnv* env, jobject obj,
+ jint geofence_id) {
+ if (sGpsGeofencingInterface != NULL) {
+ sGpsGeofencingInterface->pause_geofence(geofence_id);
+ return JNI_TRUE;
+ } else {
+ ALOGE("Geofence interface not available");
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_resume_geofence(JNIEnv* env, jobject obj,
+ jint geofence_id, jint monitor_transition) {
+ if (sGpsGeofencingInterface != NULL) {
+ sGpsGeofencingInterface->resume_geofence(geofence_id, monitor_transition);
+ return JNI_TRUE;
+ } else {
+ ALOGE("Geofence interface not available");
+ }
+ return JNI_FALSE;
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
@@ -591,6 +761,11 @@ static JNINativeMethod sMethods[] = {
{"native_agps_ni_message", "([BI)V", (void *)android_location_GpsLocationProvider_agps_send_ni_message},
{"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state},
{"native_update_network_state", "(ZIZZLjava/lang/String;Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_update_network_state },
+ {"native_is_geofence_supported", "()Z", (void*) android_location_GpsLocationProvider_is_geofence_supported},
+ {"native_add_geofence", "(IDDDIIII)Z", (void *)android_location_GpsLocationProvider_add_geofence},
+ {"native_remove_geofence", "(I)Z", (void *)android_location_GpsLocationProvider_remove_geofence},
+ {"native_pause_geofence", "(I)Z", (void *)android_location_GpsLocationProvider_pause_geofence},
+ {"native_resume_geofence", "(II)Z", (void *)android_location_GpsLocationProvider_resume_geofence}
};
int register_android_server_location_GpsLocationProvider(JNIEnv* env)
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 6e2a70d..31e01c0 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -162,13 +162,13 @@ public final class CellIdentityCdma implements Parcelable {
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("CdmaCellIdentitiy:");
- sb.append(super.toString());
+ StringBuilder sb = new StringBuilder("CellIdentitiyCdma:{");
sb.append(" mNetworkId="); sb.append(mNetworkId);
sb.append(" mSystemId="); sb.append(mSystemId);
sb.append(" mBasestationId="); sb.append(mBasestationId);
sb.append(" mLongitude="); sb.append(mLongitude);
sb.append(" mLatitude="); sb.append(mLatitude);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index bda96be..98113e7 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -147,13 +147,13 @@ public final class CellIdentityGsm implements Parcelable {
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("GsmCellIdentitiy:");
- sb.append(super.toString());
+ StringBuilder sb = new StringBuilder("CellIdentitiyGsm:{");
sb.append(" mMcc=").append(mMcc);
- sb.append(" mMnc=").append(mMcc);
+ sb.append(" mMnc=").append(mMnc);
sb.append(" mLac=").append(mLac);
sb.append(" mCid=").append(mCid);
sb.append(" mPsc=").append(mPsc);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index f72d583..86924bd 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -142,13 +142,13 @@ public final class CellIdentityLte implements Parcelable {
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("LteCellIdentitiy:");
- sb.append(super.toString());
+ StringBuilder sb = new StringBuilder("CellIdentitiyLte:{");
sb.append(" mMcc="); sb.append(mMcc);
sb.append(" mMnc="); sb.append(mMnc);
sb.append(" mCi="); sb.append(mCi);
sb.append(" mPci="); sb.append(mPci);
sb.append(" mTac="); sb.append(mTac);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index f367f99..fe3c68b 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -149,7 +149,7 @@ public abstract class CellInfo implements Parcelable {
StringBuffer sb = new StringBuffer();
String timeStampType;
- sb.append(" mRegistered=").append(mRegistered ? "YES" : "NO");
+ sb.append("mRegistered=").append(mRegistered ? "YES" : "NO");
timeStampType = timeStampTypeToString(mTimeStampType);
sb.append(" mTimeStampType=").append(timeStampType);
sb.append(" mTimeStamp=").append(mTimeStamp).append("ns");
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index a5d6e9c..6f2f1f6 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -87,10 +87,11 @@ public final class CellInfoCdma extends CellInfo implements Parcelable {
public String toString() {
StringBuffer sb = new StringBuffer();
- sb.append("CellInfoCdma:");
+ sb.append("CellInfoCdma:{");
sb.append(super.toString());
- sb.append(", ").append(mCellIdentityCdma);
- sb.append(", ").append(mCellSignalStrengthCdma);
+ sb.append(" ").append(mCellIdentityCdma);
+ sb.append(" ").append(mCellSignalStrengthCdma);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index bf0eca8..1bedddb 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -87,10 +87,11 @@ public final class CellInfoGsm extends CellInfo implements Parcelable {
public String toString() {
StringBuffer sb = new StringBuffer();
- sb.append("CellInfoGsm:");
+ sb.append("CellInfoGsm:{");
sb.append(super.toString());
- sb.append(", ").append(mCellIdentityGsm);
- sb.append(", ").append(mCellSignalStrengthGsm);
+ sb.append(" ").append(mCellIdentityGsm);
+ sb.append(" ").append(mCellSignalStrengthGsm);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index d7a58b6..287c9f0 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -91,10 +91,11 @@ public final class CellInfoLte extends CellInfo implements Parcelable {
public String toString() {
StringBuffer sb = new StringBuffer();
- sb.append("CellInfoLte:");
+ sb.append("CellInfoLte:{");
sb.append(super.toString());
- sb.append(", ").append(mCellIdentityLte);
- sb.append(", ").append(mCellSignalStrengthLte);
+ sb.append(" ").append(mCellIdentityLte);
+ sb.append(" ").append(mCellSignalStrengthLte);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 3ed9cef..674955c 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -274,6 +274,33 @@ public class SignalStrength implements Parcelable {
}
/**
+ * Make a SignalStrength object from the given parcel as passed up by
+ * the ril which does not have isGsm. isGsm will be changed by ServiceStateTracker
+ * so the default is a don't care.
+ *
+ * @hide
+ */
+ public static SignalStrength makeSignalStrengthFromRilParcel(Parcel in) {
+ if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
+
+ SignalStrength ss = new SignalStrength();
+ ss.mGsmSignalStrength = in.readInt();
+ ss.mGsmBitErrorRate = in.readInt();
+ ss.mCdmaDbm = in.readInt();
+ ss.mCdmaEcio = in.readInt();
+ ss.mEvdoDbm = in.readInt();
+ ss.mEvdoEcio = in.readInt();
+ ss.mEvdoSnr = in.readInt();
+ ss.mLteSignalStrength = in.readInt();
+ ss.mLteRsrp = in.readInt();
+ ss.mLteRsrq = in.readInt();
+ ss.mLteRssnr = in.readInt();
+ ss.mLteCqi = in.readInt();
+
+ return ss;
+ }
+
+ /**
* {@link Parcelable#writeToParcel}
*/
public void writeToParcel(Parcel out, int flags) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4aee902..6400e68 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1341,7 +1341,8 @@ public class TelephonyManager {
}
/**
- * Returns all observed cell information of the device.
+ * Returns all observed cell information of the device. This does
+ * not cause or change the rate of PhoneStateListner#onCellInfoChanged.
*
* @return List of CellInfo or null if info unavailable.
*
@@ -1357,4 +1358,24 @@ public class TelephonyManager {
return null;
}
}
+
+ /**
+ * Sets the minimum time in milli-seconds between {@link PhoneStateListener#onCellInfoChanged
+ * PhoneStateListener.onCellInfoChanged} will be invoked.
+ *
+ * The default, 0, means invoke onCellInfoChanged when any of the reported
+ * information changes. Setting the value to INT_MAX(0x7fffffff) means never issue
+ * A onCellInfoChanged.
+ *
+ * @param rateInMillis the rate
+ *
+ * @hide
+ */
+ public void setCellInfoListRate(int rateInMillis) {
+ try {
+ getITelephony().setCellInfoListRate(rateInMillis);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 1449ab1..b78f589 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -294,5 +294,10 @@ interface ITelephony {
* Returns the all observed cell information of the device.
*/
List<CellInfo> getAllCellInfo();
+
+ /**
+ * Sets minimum time in milli-seconds between onCellInfoChanged
+ */
+ void setCellInfoListRate(int rateInMillis);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 077ad68..9650b99 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -260,6 +260,8 @@ cat include/telephony/ril.h | \
int RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU = 106;
int RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS = 107;
int RIL_REQUEST_VOICE_RADIO_TECH = 108;
+ int RIL_REQUEST_GET_CELL_INFO_LIST = 109;
+ int RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE = 110;
int RIL_UNSOL_RESPONSE_BASE = 1000;
int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001;
@@ -297,4 +299,5 @@ cat include/telephony/ril.h | \
int RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE = 1033;
int RIL_UNSOL_RIL_CONNECTED = 1034;
int RIL_UNSOL_VOICE_RADIO_TECH_CHANGED = 1035;
+ int RIL_UNSOL_CELL_INFO_LIST = 1036;
}
diff --git a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
index 4d60c83..7ae0fb8 100644
--- a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
+++ b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
@@ -127,6 +127,10 @@ public class AppCompatibility extends InstrumentationTestCase {
homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Intent intent = mPackageManager.getLaunchIntentForPackage(packageName);
+ // Skip if the apk does not have a launch intent.
+ if (intent == null) {
+ return null;
+ }
// We check for any Crash or ANR dialogs that are already up, and we ignore them. This is
// so that we don't report crashes that were caused by prior apps (which those particular
diff --git a/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java b/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java
index 854a3f4..dbaedf9 100644
--- a/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java
+++ b/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java
@@ -47,7 +47,7 @@ public class ButtonActivity extends Activity
{
public void onClick (View v)
{
- InputMethodManager imm = InputMethodManager.getInstance(instance);
+ InputMethodManager imm = InputMethodManager.getInstance();
if (mKeyboardIsActive)
{
imm.hideSoftInputFromInputMethod(v.getWindowToken(), 0);
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java
index bc77e04..32f80a3 100644
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java
+++ b/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java
@@ -66,7 +66,7 @@ public abstract class ImfBaseTestCase<T extends Activity> extends Instrumentatio
mExpectAutoPop = (keyboardType == Configuration.KEYBOARD_NOKEYS ||
keyboardType == Configuration.KEYBOARD_UNDEFINED);
- mImm = InputMethodManager.getInstance(mTargetActivity);
+ mImm = InputMethodManager.getInstance();
KeyguardManager keyguardManager =
(KeyguardManager) getInstrumentation().getContext().getSystemService(
diff --git a/tests/RenderScriptTests/FBOTest/Android.mk b/tests/RenderScriptTests/FBOTest/Android.mk
index 434d592..7a578d9 100644
--- a/tests/RenderScriptTests/FBOTest/Android.mk
+++ b/tests/RenderScriptTests/FBOTest/Android.mk
@@ -23,4 +23,6 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-fil
LOCAL_PACKAGE_NAME := FBOTest
+LOCAL_SDK_VERSION := 17
+
include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/Fountain/Android.mk b/tests/RenderScriptTests/Fountain/Android.mk
index 4a6560b..0517aef 100644
--- a/tests/RenderScriptTests/Fountain/Android.mk
+++ b/tests/RenderScriptTests/Fountain/Android.mk
@@ -21,8 +21,7 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-# TODO: build fails with this set
-# LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 17
LOCAL_PACKAGE_NAME := RsFountain
diff --git a/tests/RenderScriptTests/HelloWorld/Android.mk b/tests/RenderScriptTests/HelloWorld/Android.mk
index 54824f4..c1c08ec 100644
--- a/tests/RenderScriptTests/HelloWorld/Android.mk
+++ b/tests/RenderScriptTests/HelloWorld/Android.mk
@@ -23,6 +23,6 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-fil
LOCAL_PACKAGE_NAME := RsHelloWorld
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 17
include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/MiscSamples/Android.mk b/tests/RenderScriptTests/MiscSamples/Android.mk
index bdff46a..ee3567b 100644
--- a/tests/RenderScriptTests/MiscSamples/Android.mk
+++ b/tests/RenderScriptTests/MiscSamples/Android.mk
@@ -23,6 +23,6 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-fil
LOCAL_PACKAGE_NAME := RsMiscSamples
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 17
include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/ModelViewer/Android.mk b/tests/RenderScriptTests/ModelViewer/Android.mk
index 18ceac5..86724cf 100644
--- a/tests/RenderScriptTests/ModelViewer/Android.mk
+++ b/tests/RenderScriptTests/ModelViewer/Android.mk
@@ -22,6 +22,8 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+LOCAL_SDK_VERSION := 17
+
LOCAL_PACKAGE_NAME := ModelViewer
include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/PerfTest/Android.mk b/tests/RenderScriptTests/PerfTest/Android.mk
index 0d1e7d2..e9ee771 100644
--- a/tests/RenderScriptTests/PerfTest/Android.mk
+++ b/tests/RenderScriptTests/PerfTest/Android.mk
@@ -24,6 +24,8 @@ LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+LOCAL_SDK_VERSION := 17
+
LOCAL_PACKAGE_NAME := PerfTest
include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/SceneGraph/Android.mk b/tests/RenderScriptTests/SceneGraph/Android.mk
index 163a95d..6047305 100644
--- a/tests/RenderScriptTests/SceneGraph/Android.mk
+++ b/tests/RenderScriptTests/SceneGraph/Android.mk
@@ -21,6 +21,8 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+LOCAL_SDK_VERSION := 17
+
LOCAL_PACKAGE_NAME := SceneGraphTest
include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/ShadersTest/Android.mk b/tests/RenderScriptTests/ShadersTest/Android.mk
index 0912591..fb6356e 100644
--- a/tests/RenderScriptTests/ShadersTest/Android.mk
+++ b/tests/RenderScriptTests/ShadersTest/Android.mk
@@ -21,6 +21,8 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+LOCAL_SDK_VERSION := 17
+
LOCAL_PACKAGE_NAME := ShadersTest
include $(BUILD_PACKAGE)
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java b/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
index 5d0b155..9862116 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
@@ -49,6 +49,8 @@ public class NotificationBuilderTest extends Activity
{
private final static String TAG = "NotificationTestList";
+ private final static String NOTIFY_TAG = "foo";
+
NotificationManager mNM;
Handler mHandler;
int mStartDelay;
@@ -117,34 +119,34 @@ public class NotificationBuilderTest extends Activity
public void onClick(View v) {
switch (v.getId()) {
case R.id.clear_1:
- mNM.cancel(1);
+ cancelNotification(1);
break;
case R.id.clear_2:
- mNM.cancel(2);
+ cancelNotification(2);
break;
case R.id.clear_3:
- mNM.cancel(3);
+ cancelNotification(3);
break;
case R.id.clear_4:
- mNM.cancel(4);
+ cancelNotification(4);
break;
case R.id.clear_5:
- mNM.cancel(5);
+ cancelNotification(5);
break;
case R.id.clear_6:
- mNM.cancel(6);
+ cancelNotification(6);
break;
case R.id.clear_7:
- mNM.cancel(7);
+ cancelNotification(7);
break;
case R.id.clear_8:
- mNM.cancel(8);
+ cancelNotification(8);
break;
case R.id.clear_9:
- mNM.cancel(9);
+ cancelNotification(9);
break;
case R.id.clear_10:
- mNM.cancel(10);
+ cancelNotification(10);
break;
case R.id.notify_1:
sendNotification(1);
@@ -196,11 +198,15 @@ public class NotificationBuilderTest extends Activity
final Notification n = buildNotification(id);
mHandler.postDelayed(new Runnable() {
public void run() {
- mNM.notify(id, n);
+ mNM.notify(NOTIFY_TAG, id, n);
}
}, mStartDelay);
}
+ private void cancelNotification(final int id) {
+ mNM.cancel(NOTIFY_TAG, id);
+ }
+
private static CharSequence subst(CharSequence in, char ch, CharSequence sub) {
int i=0;
SpannableStringBuilder edit = new SpannableStringBuilder(in);
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 4345098..ba160b1 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -33,13 +33,10 @@ import android.util.Log;
import android.net.Uri;
import android.os.SystemClock;
import android.widget.RemoteViews;
-import android.widget.TextView;
-import android.widget.ProgressBar;
import android.os.PowerManager;
// private NM API
import android.app.INotificationManager;
-import com.android.internal.statusbar.StatusBarNotification;
public class NotificationTestList extends TestActivity
{
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 5b88669..9b1658a 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -39,14 +39,14 @@ LOCAL_C_INCLUDES += external/libpng
LOCAL_C_INCLUDES += external/zlib
LOCAL_C_INCLUDES += build/libs/host/include
-#LOCAL_WHOLE_STATIC_LIBRARIES :=
LOCAL_STATIC_LIBRARIES := \
libhost \
libandroidfw \
libutils \
libcutils \
libexpat \
- libpng
+ libpng \
+ liblog
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -lrt -ldl -lpthread
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 84f5a5c..cadac02 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -592,6 +592,10 @@ int doDump(Bundle* bundle)
goto bail;
}
printf("uses-permission: %s\n", name.string());
+ int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
+ if (!req) {
+ printf("optional-permission: %s\n", name.string());
+ }
}
}
} else if (strcmp("badging", option) == 0) {
@@ -1033,6 +1037,10 @@ int doDump(Bundle* bundle)
hasWriteCallLogPermission = true;
}
printf("uses-permission:'%s'\n", name.string());
+ int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
+ if (!req) {
+ printf("optional-permission:'%s'\n", name.string());
+ }
} else {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
diff --git a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java
index 7a6e52e..dc4f9c8 100644
--- a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java
+++ b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java
@@ -22,6 +22,6 @@ package android.view.inputmethod;
public class InputMethodManager_Accessor {
public static void resetInstance() {
- InputMethodManager.mInstance = null;
+ InputMethodManager.sInstance = null;
}
}
diff --git a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
index f056040..7c98847 100644
--- a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
@@ -35,28 +35,15 @@ public class InputMethodManager_Delegate {
// ---- Overridden methods ----
@LayoutlibDelegate
- /*package*/ static InputMethodManager getInstance(Looper mainLooper) {
- synchronized (InputMethodManager.mInstanceSync) {
- if (InputMethodManager.mInstance != null) {
- return InputMethodManager.mInstance;
+ /*package*/ static InputMethodManager getInstance() {
+ synchronized (InputMethodManager.class) {
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm == null) {
+ imm = new InputMethodManager(
+ new BridgeIInputMethodManager(), Looper.getMainLooper());
+ InputMethodManager.sInstance = imm;
}
-
- InputMethodManager.mInstance = new InputMethodManager(new BridgeIInputMethodManager(),
- mainLooper);
- }
- return InputMethodManager.mInstance;
- }
-
- @LayoutlibDelegate
- /*package*/ static InputMethodManager getInstance(Context context) {
- synchronized (InputMethodManager.mInstanceSync) {
- if (InputMethodManager.mInstance != null) {
- return InputMethodManager.mInstance;
- }
-
- InputMethodManager.mInstance = new InputMethodManager(new BridgeIInputMethodManager(),
- Looper.myLooper());
+ return imm;
}
- return InputMethodManager.mInstance;
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index f109e39..cbefd3d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -232,7 +232,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
sCurrentContext = mContext;
// create an InputMethodManager
- InputMethodManager.getInstance(Looper.myLooper());
+ InputMethodManager.getInstance();
LayoutLog currentLog = mParams.getLog();
Bridge.setLog(currentLog);
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
index dd57ae6..ad8de69 100644
--- a/tools/obbtool/Android.mk
+++ b/tools/obbtool/Android.mk
@@ -20,7 +20,8 @@ LOCAL_CFLAGS := -Wall -Werror
LOCAL_STATIC_LIBRARIES := \
libutils \
libandroidfw \
- libcutils
+ libcutils \
+ liblog
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -ldl -lpthread
diff --git a/tools/validatekeymaps/Android.mk b/tools/validatekeymaps/Android.mk
index fce2e93..90fbc08 100644
--- a/tools/validatekeymaps/Android.mk
+++ b/tools/validatekeymaps/Android.mk
@@ -20,7 +20,8 @@ LOCAL_CFLAGS := -Wall -Werror
LOCAL_STATIC_LIBRARIES := \
libandroidfw \
libutils \
- libcutils
+ libcutils \
+ liblog
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -ldl -lpthread
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 47f1fbf..23a4e71 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -634,6 +634,8 @@ class WifiConfigStore {
if (config.priority > mLastPriority) {
mLastPriority = config.priority;
}
+ config.ipAssignment = IpAssignment.DHCP;
+ config.proxySettings = ProxySettings.NONE;
mConfiguredNetworks.put(config.networkId, config);
mNetworkIds.put(configKey(config), config.networkId);
}
@@ -831,8 +833,9 @@ class WifiConfigStore {
while (true) {
int id = -1;
- IpAssignment ipAssignment = IpAssignment.UNASSIGNED;
- ProxySettings proxySettings = ProxySettings.UNASSIGNED;
+ // Default is DHCP with no proxy
+ IpAssignment ipAssignment = IpAssignment.DHCP;
+ ProxySettings proxySettings = ProxySettings.NONE;
LinkProperties linkProperties = new LinkProperties();
String proxyHost = null;
int proxyPort = -1;
@@ -902,7 +905,8 @@ class WifiConfigStore {
config.ipAssignment = ipAssignment;
break;
case UNASSIGNED:
- //Ignore
+ loge("BUG: Found UNASSIGNED IP on file, use DHCP");
+ config.ipAssignment = IpAssignment.DHCP;
break;
default:
loge("Ignore invalid ip assignment while reading");
@@ -920,7 +924,8 @@ class WifiConfigStore {
config.proxySettings = proxySettings;
break;
case UNASSIGNED:
- //Ignore
+ loge("BUG: Found UNASSIGNED proxy on file, use NONE");
+ config.proxySettings = ProxySettings.NONE;
break;
default:
loge("Ignore invalid proxy settings while reading");
@@ -1106,7 +1111,8 @@ class WifiConfigStore {
break setVariables;
}
- if (config.enterpriseConfig != null) {
+ if (config.enterpriseConfig != null &&
+ config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
@@ -1176,6 +1182,8 @@ class WifiConfigStore {
WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
if (currentConfig == null) {
currentConfig = new WifiConfiguration();
+ currentConfig.ipAssignment = IpAssignment.DHCP;
+ currentConfig.proxySettings = ProxySettings.NONE;
currentConfig.networkId = netId;
}
@@ -1196,7 +1204,7 @@ class WifiConfigStore {
WifiConfiguration newConfig) {
boolean ipChanged = false;
boolean proxyChanged = false;
- LinkProperties linkProperties = new LinkProperties();
+ LinkProperties linkProperties = null;
switch (newConfig.ipAssignment) {
case STATIC:
@@ -1262,10 +1270,10 @@ class WifiConfigStore {
}
if (!ipChanged) {
- addIpSettingsFromConfig(linkProperties, currentConfig);
+ linkProperties = copyIpSettingsFromConfig(currentConfig);
} else {
currentConfig.ipAssignment = newConfig.ipAssignment;
- addIpSettingsFromConfig(linkProperties, newConfig);
+ linkProperties = copyIpSettingsFromConfig(newConfig);
log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " +
linkProperties.toString());
}
@@ -1291,8 +1299,9 @@ class WifiConfigStore {
return new NetworkUpdateResult(ipChanged, proxyChanged);
}
- private void addIpSettingsFromConfig(LinkProperties linkProperties,
- WifiConfiguration config) {
+ private LinkProperties copyIpSettingsFromConfig(WifiConfiguration config) {
+ LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(config.linkProperties.getInterfaceName());
for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) {
linkProperties.addLinkAddress(linkAddr);
}
@@ -1302,6 +1311,7 @@ class WifiConfigStore {
for (InetAddress dns : config.linkProperties.getDnses()) {
linkProperties.addDns(dns);
}
+ return linkProperties;
}
/**
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index f73a13c..4e7497c 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -19,17 +19,12 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
import android.security.Credentials;
+import android.security.KeyStore;
import android.text.TextUtils;
-import com.android.org.bouncycastle.asn1.ASN1InputStream;
-import com.android.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.org.bouncycastle.asn1.DEROctetString;
-import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
-
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.KeyFactory;
-import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
@@ -481,7 +476,8 @@ public class WifiEnterpriseConfig implements Parcelable {
String caCertName = Credentials.CA_CERTIFICATE + name;
if (mClientCertificate != null) {
byte[] privKeyData = mClientPrivateKey.getEncoded();
- ret = keyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID);
+ ret = keyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
+ KeyStore.FLAG_ENCRYPTED);
if (ret == false) {
return ret;
}
@@ -525,7 +521,7 @@ public class WifiEnterpriseConfig implements Parcelable {
Certificate cert) {
try {
byte[] certData = Credentials.convertToPem(cert);
- return keyStore.put(name, certData, Process.WIFI_UID);
+ return keyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_ENCRYPTED);
} catch (IOException e1) {
return false;
} catch (CertificateException e2) {
@@ -533,7 +529,7 @@ public class WifiEnterpriseConfig implements Parcelable {
}
}
- void removeKeys(android.security.KeyStore keyStore) {
+ void removeKeys(KeyStore keyStore) {
String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX);
// a valid client certificate is configured
if (!TextUtils.isEmpty(client)) {
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index c0a3bc1..a5d98cc 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1648,15 +1648,7 @@ public class WifiStateMachine extends StateMachine {
private void handleNetworkDisconnect() {
if (DBG) log("Stopping DHCP and clearing IP");
- /*
- * stop DHCP
- */
- if (mDhcpStateMachine != null) {
- /* In case we were in middle of DHCP operation
- restore back powermode */
- handlePostDhcpSetup();
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
- }
+ stopDhcp();
try {
mNwService.clearInterfaceAddresses(mInterfaceName);
@@ -1734,6 +1726,24 @@ public class WifiStateMachine extends StateMachine {
}
+ void startDhcp() {
+ if (mDhcpStateMachine == null) {
+ mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
+ mContext, WifiStateMachine.this, mInterfaceName);
+
+ }
+ mDhcpStateMachine.registerForPreDhcpNotification();
+ mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
+ }
+
+ void stopDhcp() {
+ if (mDhcpStateMachine != null) {
+ /* In case we were in middle of DHCP operation restore back powermode */
+ handlePostDhcpSetup();
+ mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
+ }
+ }
+
void handlePostDhcpSetup() {
/* Restore power save and suspend optimizations */
setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
@@ -1751,7 +1761,7 @@ public class WifiStateMachine extends StateMachine {
mDhcpResults = dhcpResults;
}
LinkProperties linkProperties = dhcpResults.linkProperties;
- mWifiConfigStore.setLinkProperties(mLastNetworkId, linkProperties);
+ mWifiConfigStore.setLinkProperties(mLastNetworkId, new LinkProperties(linkProperties));
InetAddress addr = null;
Iterator<InetAddress> addrs = linkProperties.getAddresses().iterator();
if (addrs.hasNext()) {
@@ -2432,6 +2442,9 @@ public class WifiStateMachine extends StateMachine {
if (DBG) log("Already in delayed stop");
break;
}
+ /* disconnect right now, but leave the driver running for a bit */
+ mWifiConfigStore.disableAllNetworks();
+
mInDelayedStop = true;
mDelayedStopCounter++;
if (DBG) log("Delayed stop message " + mDelayedStopCounter);
@@ -2452,6 +2465,9 @@ public class WifiStateMachine extends StateMachine {
mDelayedStopCounter++;
mAlarmManager.cancel(mDriverStopIntent);
if (DBG) log("Delayed stop ignored due to start");
+ if (mOperationalMode == CONNECT_MODE) {
+ mWifiConfigStore.enableAllNetworks();
+ }
}
break;
case CMD_DELAYED_STOP_DRIVER:
@@ -2996,15 +3012,10 @@ public class WifiStateMachine extends StateMachine {
@Override
public void enter() {
if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
- //start DHCP
- if (mDhcpStateMachine == null) {
- mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
- mContext, WifiStateMachine.this, mInterfaceName);
-
- }
- mDhcpStateMachine.registerForPreDhcpNotification();
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
+ startDhcp();
} else {
+ // stop any running dhcp before assigning static IP
+ stopDhcp();
DhcpResults dhcpResults = new DhcpResults(
mWifiConfigStore.getLinkProperties(mLastNetworkId));
dhcpResults.linkProperties.setInterfaceName(mInterfaceName);
@@ -3352,9 +3363,13 @@ public class WifiStateMachine extends StateMachine {
public boolean processMessage(Message message) {
switch (message.what) {
case WifiMonitor.WPS_SUCCESS_EVENT:
+ // Ignore intermediate success, wait for full connection
+ break;
+ case WifiMonitor.NETWORK_CONNECTION_EVENT:
replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
mSourceMessage.recycle();
mSourceMessage = null;
+ deferMessage(message);
transitionTo(mDisconnectedState);
break;
case WifiMonitor.WPS_OVERLAP_EVENT:
@@ -3398,7 +3413,6 @@ public class WifiStateMachine extends StateMachine {
case CMD_ENABLE_NETWORK:
case CMD_RECONNECT:
case CMD_REASSOCIATE:
- case WifiMonitor.NETWORK_CONNECTION_EVENT: /* Handled after exiting WPS state */
deferMessage(message);
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT: