summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk3
-rw-r--r--api/current.txt125
-rw-r--r--api/removed.txt6
-rw-r--r--api/system-current.txt131
-rw-r--r--api/system-removed.txt6
-rw-r--r--api/test-current.txt125
-rw-r--r--api/test-removed.txt6
-rw-r--r--core/java/android/app/Activity.java95
-rw-r--r--core/java/android/app/ActivityManager.java6
-rw-r--r--core/java/android/app/ActivityThread.java31
-rw-r--r--core/java/android/app/IActivityManager.aidl2
-rw-r--r--core/java/android/app/ITaskStackListener.aidl7
-rw-r--r--core/java/android/app/TaskStackListener.java4
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java67
-rw-r--r--core/java/android/app/assist/AssistStructure.java111
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java6
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java5
-rw-r--r--core/java/android/bluetooth/BluetoothSocket.java10
-rw-r--r--core/java/android/content/pm/LauncherApps.java2
-rw-r--r--core/java/android/content/pm/ShortcutManager.java5
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java8
-rw-r--r--core/java/android/net/ConnectivityManager.java3
-rw-r--r--core/java/android/net/INetworkScoreService.aidl17
-rw-r--r--core/java/android/net/LocalServerSocket.java2
-rw-r--r--core/java/android/net/LocalSocket.java34
-rw-r--r--core/java/android/net/LocalSocketImpl.java2
-rw-r--r--core/java/android/net/NetworkScoreManager.java23
-rw-r--r--core/java/android/net/NetworkScorerAppManager.java4
-rw-r--r--core/java/android/os/PowerManager.java51
-rwxr-xr-xcore/java/android/provider/Settings.java10
-rw-r--r--core/java/android/service/autofill/AutoFillService.java105
-rw-r--r--core/java/android/service/autofill/FillCallback.java112
-rw-r--r--core/java/android/service/autofill/FillableInputField.java99
-rw-r--r--core/java/android/service/autofill/IAutoFillAppCallback.aidl (renamed from core/java/android/service/autofill/IAutoFillCallback.aidl)7
-rw-r--r--core/java/android/service/autofill/IAutoFillManagerService.aidl3
-rw-r--r--core/java/android/service/autofill/IAutoFillServerCallback.aidl31
-rw-r--r--core/java/android/service/autofill/IAutoFillService.aidl11
-rw-r--r--core/java/android/service/autofill/SaveCallback.java44
-rw-r--r--core/java/android/text/DynamicLayout.java10
-rw-r--r--core/java/android/text/Layout.java135
-rw-r--r--core/java/android/text/StaticLayout.java20
-rw-r--r--core/java/android/text/TextLine.java54
-rw-r--r--core/java/android/text/TextUtils.java16
-rw-r--r--core/java/android/text/style/AccessibilityClickableSpan.java156
-rw-r--r--core/java/android/text/style/AccessibilityURLSpan.java79
-rw-r--r--core/java/android/text/style/ClickableSpan.java15
-rw-r--r--core/java/android/util/EventLog.java2
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java64
-rw-r--r--core/java/android/view/Display.java14
-rw-r--r--core/java/android/view/SurfaceHolder.java27
-rw-r--r--core/java/android/view/SurfaceView.java16
-rw-r--r--core/java/android/view/View.java199
-rw-r--r--core/java/android/view/ViewGroup.java33
-rw-r--r--core/java/android/view/ViewRootImpl.java31
-rw-r--r--core/java/android/view/ViewStructure.java26
-rw-r--r--core/java/android/view/WindowManagerPolicy.java27
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java79
-rw-r--r--core/java/android/view/animation/AnimationSet.java2
-rw-r--r--core/java/android/view/autofill/AutoFillId.aidl19
-rw-r--r--core/java/android/view/autofill/AutoFillId.java125
-rw-r--r--core/java/android/view/autofill/AutoFillType.aidl19
-rw-r--r--core/java/android/view/autofill/AutoFillType.java211
-rw-r--r--core/java/android/view/autofill/AutoFillValue.aidl19
-rw-r--r--core/java/android/view/autofill/AutoFillValue.java150
-rw-r--r--core/java/android/view/autofill/Dataset.aidl19
-rw-r--r--core/java/android/view/autofill/Dataset.java181
-rw-r--r--core/java/android/view/autofill/DatasetField.java86
-rw-r--r--core/java/android/view/autofill/FieldId.aidl19
-rw-r--r--core/java/android/view/autofill/FillResponse.aidl19
-rw-r--r--core/java/android/view/autofill/FillResponse.java288
-rw-r--r--core/java/android/view/autofill/Helper.java48
-rw-r--r--core/java/android/view/autofill/VirtualViewDelegate.java123
-rw-r--r--core/java/android/webkit/WebView.java4
-rw-r--r--core/java/android/webkit/WebViewFactory.java27
-rw-r--r--core/java/android/webkit/WebViewProvider.java2
-rw-r--r--core/java/android/widget/CompoundButton.java25
-rw-r--r--core/java/android/widget/EditText.java26
-rw-r--r--core/java/android/widget/RadioGroup.java26
-rw-r--r--core/java/android/widget/Switch.java15
-rw-r--r--core/java/android/widget/TextView.java47
-rw-r--r--core/java/com/android/internal/logging/LogBuilder.java5
-rw-r--r--core/java/com/android/internal/os/WebViewZygoteInit.java5
-rw-r--r--core/java/com/android/internal/os/Zygote.java9
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java5
-rw-r--r--core/java/com/android/internal/view/BaseSurfaceHolder.java2
-rw-r--r--core/java/com/android/internal/view/IInputConnectionWrapper.java16
-rw-r--r--core/java/com/android/internal/view/SurfaceCallbackHelper.java77
-rw-r--r--core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp4
-rw-r--r--core/jni/android_os_Debug.cpp28
-rw-r--r--core/jni/android_os_Trace.cpp9
-rw-r--r--core/jni/android_text_StaticLayout.cpp6
-rw-r--r--core/jni/android_util_Binder.cpp16
-rw-r--r--core/jni/android_util_Log.cpp2
-rw-r--r--core/jni/android_util_jar_StrictJarFile.cpp3
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp34
-rw-r--r--core/jni/fd_utils.cpp12
-rw-r--r--core/jni/fd_utils.h4
-rw-r--r--core/res/res/values/config.xml10
-rw-r--r--core/res/res/values/ids.xml2
-rw-r--r--core/res/res/values/strings.xml11
-rw-r--r--core/res/res/values/symbols.xml6
-rw-r--r--core/tests/coretests/AndroidManifest.xml27
-rw-r--r--core/tests/coretests/src/android/os/PowerManagerVrTest.java149
-rw-r--r--core/tests/coretests/src/android/os/TestVrActivity.java59
-rw-r--r--core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java143
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java6
-rw-r--r--libs/androidfw/BackupData.cpp7
-rw-r--r--libs/androidfw/BackupHelpers.cpp19
-rw-r--r--libs/androidfw/include/androidfw/CursorWindow.h2
-rw-r--r--libs/hwui/AnimatorManager.cpp2
-rw-r--r--libs/hwui/DamageAccumulator.cpp2
-rw-r--r--libs/hwui/DeviceInfo.cpp8
-rw-r--r--libs/hwui/GpuMemoryTracker.h3
-rw-r--r--libs/hwui/Interpolator.cpp7
-rw-r--r--libs/hwui/JankTracker.cpp16
-rw-r--r--libs/hwui/PixelBuffer.h3
-rw-r--r--libs/hwui/Properties.cpp10
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp8
-rw-r--r--libs/hwui/hwui/MinikinSkia.cpp3
-rw-r--r--libs/hwui/hwui/MinikinUtils.cpp8
-rw-r--r--libs/hwui/utils/GLUtils.h2
-rw-r--r--libs/input/PointerController.cpp4
-rw-r--r--libs/input/SpriteController.cpp4
-rw-r--r--media/mca/filterpacks/native/base/geometry.cpp4
-rw-r--r--media/mca/filterpacks/native/base/time_util.cpp4
-rw-r--r--opengl/java/android/opengl/GLSurfaceView.java41
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java55
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java5
-rw-r--r--packages/SystemUI/AndroidManifest.xml15
-rw-r--r--packages/SystemUI/res/drawable-ldrtl-sw900dp-hdpi/ic_sysbar_back_dark.pngbin0 -> 1715 bytes
-rw-r--r--packages/SystemUI/res/drawable-ldrtl-sw900dp-mdpi/ic_sysbar_back_dark.pngbin0 -> 994 bytes
-rw-r--r--packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back.pngbin2497 -> 2739 bytes
-rw-r--r--packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back_dark.pngbin0 -> 2496 bytes
-rw-r--r--packages/SystemUI/res/drawable-ldrtl-sw900dp-xxhdpi/ic_sysbar_back_dark.pngbin0 -> 4592 bytes
-rw-r--r--packages/SystemUI/res/drawable-ldrtl-sw900dp-xxxhdpi/ic_sysbar_back_dark.pngbin0 -> 7152 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_dark.pngbin0 -> 1764 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_ime_dark.pngbin0 -> 1833 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_docked_dark.pngbin0 -> 1411 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_home_dark.pngbin0 -> 2153 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_menu_dark.pngbin0 -> 974 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_recent_dark.pngbin0 -> 1083 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-land-hdpi/ic_sysbar_docked_dark.pngbin0 -> 1358 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-land-mdpi/ic_sysbar_docked_dark.pngbin0 -> 602 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked.pngbin0 -> 1596 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked_dark.pngbin0 -> 1518 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-land-xxhdpi/ic_sysbar_docked_dark.pngbin0 -> 2744 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-land-xxxhdpi/ic_sysbar_docked_dark.pngbin0 -> 4245 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_dark.pngbin0 -> 1011 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_ime_dark.pngbin0 -> 1063 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_docked_dark.pngbin0 -> 608 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_home_dark.pngbin0 -> 1070 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_menu_dark.pngbin0 -> 538 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_recent_dark.pngbin0 -> 595 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back.pngbin2371 -> 2843 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_dark.pngbin0 -> 2583 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_ime.pngbin2389 -> 3036 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked.pngbin0 -> 1610 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked_dark.pngbin0 -> 1513 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home.pngbin3323 -> 3256 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home_dark.pngbin0 -> 2952 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_menu_dark.pngbin0 -> 1463 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent.pngbin895 -> 1535 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent_dark.pngbin0 -> 1461 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_back_dark.pngbin0 -> 4703 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_docked_dark.pngbin0 -> 2695 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_home_dark.pngbin0 -> 5635 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_ime_dark.pngbin0 -> 4806 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_menu_dark.pngbin0 -> 2805 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_recent_dark.pngbin0 -> 2574 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_dark.pngbin0 -> 7338 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_ime_dark.pngbin0 -> 7537 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_docked_dark.pngbin0 -> 4155 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_home_dark.pngbin0 -> 8971 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_menu_dark.pngbin0 -> 8798 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_recent_dark.pngbin0 -> 4108 bytes
-rw-r--r--packages/SystemUI/res/layout/qs_customize_panel_content.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java159
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java2
-rw-r--r--proto/src/ipconnectivity.proto35
-rw-r--r--proto/src/metrics_constants.proto11
-rw-r--r--services/autofill/java/com/android/server/autofill/AutoFillManagerService.java176
-rw-r--r--services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java214
-rw-r--r--services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java11
-rw-r--r--services/autofill/java/com/android/server/autofill/AutoFillUI.java471
-rw-r--r--services/core/Android.mk2
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java28
-rw-r--r--services/core/java/com/android/server/NetworkScoreService.java42
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java88
-rw-r--r--services/core/java/com/android/server/am/ActivityMetricsLogger.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java75
-rw-r--r--services/core/java/com/android/server/am/TaskChangeNotificationController.java13
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java120
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java26
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java19
-rw-r--r--services/core/java/com/android/server/lights/Light.java1
-rw-r--r--services/core/java/com/android/server/lights/LightsService.java101
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java17
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java23
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java16
-rw-r--r--services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java20
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java16
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java36
-rw-r--r--services/core/java/com/android/server/policy/SplashScreenSurface.java38
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java60
-rw-r--r--services/core/java/com/android/server/updates/TzDataInstallReceiver.java7
-rw-r--r--services/core/java/com/android/server/wm/AppWindowContainerController.java144
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java65
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java165
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java5
-rw-r--r--services/core/jni/com_android_server_connectivity_Vpn.cpp21
-rw-r--r--services/core/jni/com_android_server_lights_LightsService.cpp3
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp118
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java64
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java213
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java52
-rw-r--r--services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java2
-rw-r--r--telecomm/java/android/telecom/Call.java7
-rw-r--r--tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java4
-rw-r--r--tools/layoutlib/bridge/src/android/view/ViewRootImpl_Accessor.java26
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java14
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java43
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml12
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java33
235 files changed, 6240 insertions, 1517 deletions
diff --git a/Android.mk b/Android.mk
index ef9754941df0..8e8b95a3e8aa 100644
--- a/Android.mk
+++ b/Android.mk
@@ -262,8 +262,9 @@ LOCAL_SRC_FILES += \
core/java/android/os/storage/IObbActionListener.aidl \
core/java/android/security/IKeystoreService.aidl \
core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl \
- core/java/android/service/autofill/IAutoFillCallback.aidl \
+ core/java/android/service/autofill/IAutoFillAppCallback.aidl \
core/java/android/service/autofill/IAutoFillManagerService.aidl \
+ core/java/android/service/autofill/IAutoFillServerCallback.aidl \
core/java/android/service/autofill/IAutoFillService.aidl \
core/java/android/service/carrier/ICarrierService.aidl \
core/java/android/service/carrier/ICarrierMessagingCallback.aidl \
diff --git a/api/current.txt b/api/current.txt
index a4402bb7987e..9f6d358bca2f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6368,7 +6368,8 @@ package android.app.assist {
public static class AssistStructure.ViewNode {
method public float getAlpha();
- method public int getAutoFillId();
+ method public android.view.autofill.AutoFillId getAutoFillId();
+ method public android.view.autofill.AutoFillType getAutoFillType();
method public android.app.assist.AssistStructure.ViewNode getChildAt(int);
method public int getChildCount();
method public java.lang.String getClassName();
@@ -6784,6 +6785,7 @@ package android.appwidget {
method protected android.view.View getErrorView();
method protected void prepareView(android.view.View);
method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
+ method public void setAsyncExecutor(java.util.concurrent.Executor);
method public void updateAppWidget(android.widget.RemoteViews);
method public void updateAppWidgetOptions(android.os.Bundle);
method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
@@ -23999,11 +24001,8 @@ package android.net {
method public boolean requestBandwidthUpdate(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
- method public deprecated boolean requestRouteToHost(int, int);
method public deprecated void setNetworkPreference(int);
method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
- method public deprecated int startUsingNetworkFeature(int, java.lang.String);
- method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
method public void unregisterNetworkCallback(android.app.PendingIntent);
field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
@@ -28580,7 +28579,7 @@ package android.opengl {
method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
method public void surfaceCreated(android.view.SurfaceHolder);
method public void surfaceDestroyed(android.view.SurfaceHolder);
- method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
+ method public deprecated void surfaceRedrawNeeded(android.view.SurfaceHolder);
field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -35042,26 +35041,19 @@ package android.service.autofill {
method public void onDisconnected();
method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
+ field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
+ field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
}
public final class FillCallback {
method public void onFailure(java.lang.CharSequence);
- method public void onSuccess(android.service.autofill.FillCallback.FillData);
- }
-
- public static final class FillCallback.FillData {
- }
-
- public static class FillCallback.FillData.Builder {
- ctor public FillCallback.FillData.Builder();
- method public android.service.autofill.FillCallback.FillData build();
- method public android.service.autofill.FillCallback.FillData.Builder setTextField(int, java.lang.String);
+ method public void onSuccess(android.view.autofill.FillResponse);
}
public final class SaveCallback {
method public void onFailure(java.lang.CharSequence);
- method public void onSuccess(int[]);
+ method public void onSuccess(android.view.autofill.AutoFillId[]);
}
}
@@ -36726,6 +36718,7 @@ package android.telecom {
method public void unhold();
method public void unregisterCallback(android.telecom.Call.Callback);
field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
+ field public static final java.lang.String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS";
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_CONNECTING = 9; // 0x9
field public static final int STATE_DIALING = 1; // 0x1
@@ -39459,6 +39452,7 @@ package android.text {
method public android.text.StaticLayout.Builder setHyphenationFrequency(int);
method public android.text.StaticLayout.Builder setIncludePad(boolean);
method public android.text.StaticLayout.Builder setIndents(int[], int[]);
+ method public android.text.StaticLayout.Builder setJustify(boolean);
method public android.text.StaticLayout.Builder setLineSpacing(float, float);
method public android.text.StaticLayout.Builder setMaxLines(int);
method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
@@ -43067,6 +43061,7 @@ package android.view {
public static abstract interface SurfaceHolder.Callback2 implements android.view.SurfaceHolder.Callback {
method public abstract void surfaceRedrawNeeded(android.view.SurfaceHolder);
+ method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, java.lang.Runnable);
}
public class SurfaceView extends android.view.View {
@@ -43147,6 +43142,7 @@ package android.view {
method public void addTouchables(java.util.ArrayList<android.view.View>);
method public android.view.ViewPropertyAnimator animate();
method public void announceForAccessibility(java.lang.CharSequence);
+ method public void autoFill(android.view.autofill.AutoFillValue);
method protected boolean awakenScrollBars();
method protected boolean awakenScrollBars(int);
method protected boolean awakenScrollBars(int, boolean);
@@ -43198,8 +43194,8 @@ package android.view {
method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
method public boolean dispatchNestedScroll(int, int, int, int, int[]);
method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
- method public deprecated void dispatchProvideStructure(android.view.ViewStructure);
- method public void dispatchProvideStructure(android.view.ViewStructure, int);
+ method public void dispatchProvideAutoFillStructure(android.view.ViewStructure, int);
+ method public void dispatchProvideStructure(android.view.ViewStructure);
method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
method protected void dispatchSetActivated(boolean);
@@ -43234,6 +43230,8 @@ package android.view {
method public float getAlpha();
method public android.view.animation.Animation getAnimation();
method public android.os.IBinder getApplicationWindowToken();
+ method public android.view.autofill.AutoFillType getAutoFillType();
+ method public android.view.autofill.VirtualViewDelegate getAutoFillVirtualViewDelegate(android.view.autofill.VirtualViewDelegate.Callback);
method public android.graphics.drawable.Drawable getBackground();
method public android.content.res.ColorStateList getBackgroundTintList();
method public android.graphics.PorterDuff.Mode getBackgroundTintMode();
@@ -43470,10 +43468,10 @@ package android.view {
method protected void onMeasure(int, int);
method protected void onOverScrolled(int, int, boolean, boolean);
method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
- method public deprecated void onProvideStructure(android.view.ViewStructure);
- method public void onProvideStructure(android.view.ViewStructure, int);
- method public deprecated void onProvideVirtualStructure(android.view.ViewStructure);
- method public void onProvideVirtualStructure(android.view.ViewStructure, int);
+ method public void onProvideAutoFillStructure(android.view.ViewStructure, int);
+ method public void onProvideAutoFillVirtualStructure(android.view.ViewStructure, int);
+ method public void onProvideStructure(android.view.ViewStructure);
+ method public void onProvideVirtualStructure(android.view.ViewStructure);
method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
method protected void onRestoreInstanceState(android.os.Parcelable);
method public void onRtlPropertiesChanged(int);
@@ -43680,8 +43678,8 @@ package android.view {
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
- field public static final int ASSIST_FLAG_NON_SANITIZED_TEXT = 2; // 0x2
- field public static final int ASSIST_FLAG_SANITIZED_TEXT = 1; // 0x1
+ field public static final int AUTO_FILL_FLAG_TYPE_FILL = 1; // 0x1
+ field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 2; // 0x2
field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
@@ -44311,6 +44309,7 @@ package android.view {
method public abstract int addChildCount(int);
method public abstract void asyncCommit();
method public abstract android.view.ViewStructure asyncNewChild(int);
+ method public abstract android.view.ViewStructure asyncNewChild(int, int);
method public abstract int getChildCount();
method public abstract android.os.Bundle getExtras();
method public abstract java.lang.CharSequence getHint();
@@ -44319,9 +44318,11 @@ package android.view {
method public abstract int getTextSelectionStart();
method public abstract boolean hasExtras();
method public abstract android.view.ViewStructure newChild(int);
+ method public abstract android.view.ViewStructure newChild(int, int);
method public abstract void setAccessibilityFocused(boolean);
method public abstract void setActivated(boolean);
method public abstract void setAlpha(float);
+ method public abstract void setAutoFillType(android.view.autofill.AutoFillType);
method public abstract void setCheckable(boolean);
method public abstract void setChecked(boolean);
method public abstract void setChildCount(int);
@@ -45542,6 +45543,80 @@ package android.view.animation {
}
+package android.view.autofill {
+
+ public final class AutoFillId implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillId> CREATOR;
+ }
+
+ public final class AutoFillType implements android.os.Parcelable {
+ method public int describeContents();
+ method public static android.view.autofill.AutoFillType forList();
+ method public static android.view.autofill.AutoFillType forText(int);
+ method public static android.view.autofill.AutoFillType forToggle();
+ method public int getSubType();
+ method public boolean isList();
+ method public boolean isText();
+ method public boolean isToggle();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillType> CREATOR;
+ }
+
+ public final class AutoFillValue implements android.os.Parcelable {
+ method public int describeContents();
+ method public static android.view.autofill.AutoFillValue forList(int);
+ method public static android.view.autofill.AutoFillValue forText(java.lang.CharSequence);
+ method public static android.view.autofill.AutoFillValue forToggle(boolean);
+ method public int getListValue();
+ method public java.lang.CharSequence getTextValue();
+ method public boolean getToggleValue();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillValue> CREATOR;
+ }
+
+ public final class Dataset implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.Dataset> CREATOR;
+ }
+
+ public static final class Dataset.Builder {
+ ctor public Dataset.Builder(java.lang.CharSequence);
+ method public android.view.autofill.Dataset build();
+ method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle);
+ method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
+ }
+
+ public final class FillResponse implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.FillResponse> CREATOR;
+ }
+
+ public static final class FillResponse.Builder {
+ ctor public FillResponse.Builder();
+ method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset);
+ method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
+ method public android.view.autofill.FillResponse build();
+ method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+ }
+
+ public abstract class VirtualViewDelegate {
+ ctor public VirtualViewDelegate();
+ method public abstract void autoFill(int, android.view.autofill.AutoFillValue);
+ }
+
+ public static abstract class VirtualViewDelegate.Callback {
+ ctor public VirtualViewDelegate.Callback();
+ method public void onFocusChanged(int, boolean);
+ method public void onNodeRemoved(int...);
+ method public void onValueChanged(int);
+ }
+
+}
+
package android.view.inputmethod {
public class BaseInputConnection implements android.view.inputmethod.InputConnection {
@@ -48987,6 +49062,7 @@ package android.widget {
method public boolean getIncludeFontPadding();
method public android.os.Bundle getInputExtras(boolean);
method public int getInputType();
+ method public boolean getJustify();
method public final android.text.method.KeyListener getKeyListener();
method public final android.text.Layout getLayout();
method public float getLetterSpacing();
@@ -49095,6 +49171,7 @@ package android.widget {
method public void setIncludeFontPadding(boolean);
method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public void setInputType(int);
+ method public void setJustify(boolean);
method public void setKeyListener(android.text.method.KeyListener);
method public void setLetterSpacing(float);
method public void setLineSpacing(float, float);
diff --git a/api/removed.txt b/api/removed.txt
index d7a8bce2d00b..ab22b6ec1720 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -126,6 +126,12 @@ package android.media.tv {
package android.net {
+ public class ConnectivityManager {
+ method public deprecated boolean requestRouteToHost(int, int);
+ method public deprecated int startUsingNetworkFeature(int, java.lang.String);
+ method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
+ }
+
public abstract class PskKeyManager {
ctor public PskKeyManager();
field public static final int MAX_IDENTITY_HINT_LENGTH_BYTES = 128; // 0x80
diff --git a/api/system-current.txt b/api/system-current.txt
index 2ec564302d8c..f2b0eaeb2688 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6423,6 +6423,8 @@ package android.app.admin {
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
field public static final deprecated java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM";
field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMERS = "android.app.extra.PROVISIONING_DISCLAIMERS";
@@ -6435,8 +6437,10 @@ package android.app.admin {
field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME";
field public static final java.lang.String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI";
field public static final java.lang.String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR";
+ field public static final java.lang.String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME";
field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
field public static final java.lang.String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT";
+ field public static final java.lang.String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL";
field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PAC_URL = "android.app.extra.PROVISIONING_WIFI_PAC_URL";
@@ -6571,7 +6575,8 @@ package android.app.assist {
public static class AssistStructure.ViewNode {
method public float getAlpha();
- method public int getAutoFillId();
+ method public android.view.autofill.AutoFillId getAutoFillId();
+ method public android.view.autofill.AutoFillType getAutoFillType();
method public android.app.assist.AssistStructure.ViewNode getChildAt(int);
method public int getChildCount();
method public java.lang.String getClassName();
@@ -7098,6 +7103,7 @@ package android.appwidget {
method protected android.view.View getErrorView();
method protected void prepareView(android.view.View);
method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
+ method public void setAsyncExecutor(java.util.concurrent.Executor);
method public void updateAppWidget(android.widget.RemoteViews);
method public void updateAppWidgetOptions(android.os.Bundle);
method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
@@ -25791,14 +25797,11 @@ package android.net {
method public boolean requestBandwidthUpdate(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
- method public deprecated boolean requestRouteToHost(int, int);
method public deprecated void setNetworkPreference(int);
method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
method public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
method public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
- method public deprecated int startUsingNetworkFeature(int, java.lang.String);
method public void stopTethering(int);
- method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
method public void unregisterNetworkCallback(android.app.PendingIntent);
field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
@@ -31144,7 +31147,7 @@ package android.opengl {
method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
method public void surfaceCreated(android.view.SurfaceHolder);
method public void surfaceDestroyed(android.view.SurfaceHolder);
- method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
+ method public deprecated void surfaceRedrawNeeded(android.view.SurfaceHolder);
field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -37905,26 +37908,19 @@ package android.service.autofill {
method public void onDisconnected();
method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
+ field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
+ field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
}
public final class FillCallback {
method public void onFailure(java.lang.CharSequence);
- method public void onSuccess(android.service.autofill.FillCallback.FillData);
- }
-
- public static final class FillCallback.FillData {
- }
-
- public static class FillCallback.FillData.Builder {
- ctor public FillCallback.FillData.Builder();
- method public android.service.autofill.FillCallback.FillData build();
- method public android.service.autofill.FillCallback.FillData.Builder setTextField(int, java.lang.String);
+ method public void onSuccess(android.view.autofill.FillResponse);
}
public final class SaveCallback {
method public void onFailure(java.lang.CharSequence);
- method public void onSuccess(int[]);
+ method public void onSuccess(android.view.autofill.AutoFillId[]);
}
}
@@ -39660,6 +39656,7 @@ package android.telecom {
method public void unhold();
method public void unregisterCallback(android.telecom.Call.Callback);
field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
+ field public static final java.lang.String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS";
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_CONNECTING = 9; // 0x9
field public static final int STATE_DIALING = 1; // 0x1
@@ -42677,6 +42674,7 @@ package android.text {
method public android.text.StaticLayout.Builder setHyphenationFrequency(int);
method public android.text.StaticLayout.Builder setIncludePad(boolean);
method public android.text.StaticLayout.Builder setIndents(int[], int[]);
+ method public android.text.StaticLayout.Builder setJustify(boolean);
method public android.text.StaticLayout.Builder setLineSpacing(float, float);
method public android.text.StaticLayout.Builder setMaxLines(int);
method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
@@ -46285,6 +46283,7 @@ package android.view {
public static abstract interface SurfaceHolder.Callback2 implements android.view.SurfaceHolder.Callback {
method public abstract void surfaceRedrawNeeded(android.view.SurfaceHolder);
+ method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, java.lang.Runnable);
}
public class SurfaceView extends android.view.View {
@@ -46365,6 +46364,7 @@ package android.view {
method public void addTouchables(java.util.ArrayList<android.view.View>);
method public android.view.ViewPropertyAnimator animate();
method public void announceForAccessibility(java.lang.CharSequence);
+ method public void autoFill(android.view.autofill.AutoFillValue);
method protected boolean awakenScrollBars();
method protected boolean awakenScrollBars(int);
method protected boolean awakenScrollBars(int, boolean);
@@ -46416,8 +46416,8 @@ package android.view {
method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
method public boolean dispatchNestedScroll(int, int, int, int, int[]);
method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
- method public deprecated void dispatchProvideStructure(android.view.ViewStructure);
- method public void dispatchProvideStructure(android.view.ViewStructure, int);
+ method public void dispatchProvideAutoFillStructure(android.view.ViewStructure, int);
+ method public void dispatchProvideStructure(android.view.ViewStructure);
method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
method protected void dispatchSetActivated(boolean);
@@ -46452,6 +46452,8 @@ package android.view {
method public float getAlpha();
method public android.view.animation.Animation getAnimation();
method public android.os.IBinder getApplicationWindowToken();
+ method public android.view.autofill.AutoFillType getAutoFillType();
+ method public android.view.autofill.VirtualViewDelegate getAutoFillVirtualViewDelegate(android.view.autofill.VirtualViewDelegate.Callback);
method public android.graphics.drawable.Drawable getBackground();
method public android.content.res.ColorStateList getBackgroundTintList();
method public android.graphics.PorterDuff.Mode getBackgroundTintMode();
@@ -46688,10 +46690,10 @@ package android.view {
method protected void onMeasure(int, int);
method protected void onOverScrolled(int, int, boolean, boolean);
method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
- method public deprecated void onProvideStructure(android.view.ViewStructure);
- method public void onProvideStructure(android.view.ViewStructure, int);
- method public deprecated void onProvideVirtualStructure(android.view.ViewStructure);
- method public void onProvideVirtualStructure(android.view.ViewStructure, int);
+ method public void onProvideAutoFillStructure(android.view.ViewStructure, int);
+ method public void onProvideAutoFillVirtualStructure(android.view.ViewStructure, int);
+ method public void onProvideStructure(android.view.ViewStructure);
+ method public void onProvideVirtualStructure(android.view.ViewStructure);
method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
method protected void onRestoreInstanceState(android.os.Parcelable);
method public void onRtlPropertiesChanged(int);
@@ -46898,8 +46900,8 @@ package android.view {
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
- field public static final int ASSIST_FLAG_NON_SANITIZED_TEXT = 2; // 0x2
- field public static final int ASSIST_FLAG_SANITIZED_TEXT = 1; // 0x1
+ field public static final int AUTO_FILL_FLAG_TYPE_FILL = 1; // 0x1
+ field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 2; // 0x2
field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
@@ -47529,6 +47531,7 @@ package android.view {
method public abstract int addChildCount(int);
method public abstract void asyncCommit();
method public abstract android.view.ViewStructure asyncNewChild(int);
+ method public abstract android.view.ViewStructure asyncNewChild(int, int);
method public abstract int getChildCount();
method public abstract android.os.Bundle getExtras();
method public abstract java.lang.CharSequence getHint();
@@ -47537,9 +47540,11 @@ package android.view {
method public abstract int getTextSelectionStart();
method public abstract boolean hasExtras();
method public abstract android.view.ViewStructure newChild(int);
+ method public abstract android.view.ViewStructure newChild(int, int);
method public abstract void setAccessibilityFocused(boolean);
method public abstract void setActivated(boolean);
method public abstract void setAlpha(float);
+ method public abstract void setAutoFillType(android.view.autofill.AutoFillType);
method public abstract void setCheckable(boolean);
method public abstract void setChecked(boolean);
method public abstract void setChildCount(int);
@@ -48763,6 +48768,80 @@ package android.view.animation {
}
+package android.view.autofill {
+
+ public final class AutoFillId implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillId> CREATOR;
+ }
+
+ public final class AutoFillType implements android.os.Parcelable {
+ method public int describeContents();
+ method public static android.view.autofill.AutoFillType forList();
+ method public static android.view.autofill.AutoFillType forText(int);
+ method public static android.view.autofill.AutoFillType forToggle();
+ method public int getSubType();
+ method public boolean isList();
+ method public boolean isText();
+ method public boolean isToggle();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillType> CREATOR;
+ }
+
+ public final class AutoFillValue implements android.os.Parcelable {
+ method public int describeContents();
+ method public static android.view.autofill.AutoFillValue forList(int);
+ method public static android.view.autofill.AutoFillValue forText(java.lang.CharSequence);
+ method public static android.view.autofill.AutoFillValue forToggle(boolean);
+ method public int getListValue();
+ method public java.lang.CharSequence getTextValue();
+ method public boolean getToggleValue();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillValue> CREATOR;
+ }
+
+ public final class Dataset implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.Dataset> CREATOR;
+ }
+
+ public static final class Dataset.Builder {
+ ctor public Dataset.Builder(java.lang.CharSequence);
+ method public android.view.autofill.Dataset build();
+ method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle);
+ method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
+ }
+
+ public final class FillResponse implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.FillResponse> CREATOR;
+ }
+
+ public static final class FillResponse.Builder {
+ ctor public FillResponse.Builder();
+ method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset);
+ method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
+ method public android.view.autofill.FillResponse build();
+ method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+ }
+
+ public abstract class VirtualViewDelegate {
+ ctor public VirtualViewDelegate();
+ method public abstract void autoFill(int, android.view.autofill.AutoFillValue);
+ }
+
+ public static abstract class VirtualViewDelegate.Callback {
+ ctor public VirtualViewDelegate.Callback();
+ method public void onFocusChanged(int, boolean);
+ method public void onNodeRemoved(int...);
+ method public void onValueChanged(int);
+ }
+
+}
+
package android.view.inputmethod {
public class BaseInputConnection implements android.view.inputmethod.InputConnection {
@@ -50278,8 +50357,8 @@ package android.webkit {
method public abstract boolean onKeyUp(int, android.view.KeyEvent);
method public abstract void onMeasure(int, int);
method public abstract void onOverScrolled(int, int, boolean, boolean);
+ method public default void onProvideAutoFillVirtualStructure(android.view.ViewStructure, int);
method public abstract void onProvideVirtualStructure(android.view.ViewStructure);
- method public default void onProvideVirtualStructure(android.view.ViewStructure, int);
method public abstract void onScrollChanged(int, int, int, int);
method public abstract void onSizeChanged(int, int, int, int);
method public abstract void onStartTemporaryDetach();
@@ -52565,6 +52644,7 @@ package android.widget {
method public boolean getIncludeFontPadding();
method public android.os.Bundle getInputExtras(boolean);
method public int getInputType();
+ method public boolean getJustify();
method public final android.text.method.KeyListener getKeyListener();
method public final android.text.Layout getLayout();
method public float getLetterSpacing();
@@ -52673,6 +52753,7 @@ package android.widget {
method public void setIncludeFontPadding(boolean);
method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public void setInputType(int);
+ method public void setJustify(boolean);
method public void setKeyListener(android.text.method.KeyListener);
method public void setLetterSpacing(float);
method public void setLineSpacing(float, float);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 7ba88bdb9d33..1ba26f5975d1 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -124,6 +124,12 @@ package android.media.tv {
package android.net {
+ public class ConnectivityManager {
+ method public deprecated boolean requestRouteToHost(int, int);
+ method public deprecated int startUsingNetworkFeature(int, java.lang.String);
+ method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
+ }
+
public abstract class PskKeyManager {
ctor public PskKeyManager();
field public static final int MAX_IDENTITY_HINT_LENGTH_BYTES = 128; // 0x80
diff --git a/api/test-current.txt b/api/test-current.txt
index 7e05b785524e..c1ec21df688c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6390,7 +6390,8 @@ package android.app.assist {
public static class AssistStructure.ViewNode {
method public float getAlpha();
- method public int getAutoFillId();
+ method public android.view.autofill.AutoFillId getAutoFillId();
+ method public android.view.autofill.AutoFillType getAutoFillType();
method public android.app.assist.AssistStructure.ViewNode getChildAt(int);
method public int getChildCount();
method public java.lang.String getClassName();
@@ -6806,6 +6807,7 @@ package android.appwidget {
method protected android.view.View getErrorView();
method protected void prepareView(android.view.View);
method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
+ method public void setAsyncExecutor(java.util.concurrent.Executor);
method public void updateAppWidget(android.widget.RemoteViews);
method public void updateAppWidgetOptions(android.os.Bundle);
method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
@@ -24086,11 +24088,8 @@ package android.net {
method public boolean requestBandwidthUpdate(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
- method public deprecated boolean requestRouteToHost(int, int);
method public deprecated void setNetworkPreference(int);
method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
- method public deprecated int startUsingNetworkFeature(int, java.lang.String);
- method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
method public void unregisterNetworkCallback(android.app.PendingIntent);
field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
@@ -28667,7 +28666,7 @@ package android.opengl {
method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
method public void surfaceCreated(android.view.SurfaceHolder);
method public void surfaceDestroyed(android.view.SurfaceHolder);
- method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
+ method public deprecated void surfaceRedrawNeeded(android.view.SurfaceHolder);
field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -35160,26 +35159,19 @@ package android.service.autofill {
method public void onDisconnected();
method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
+ field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
+ field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
}
public final class FillCallback {
method public void onFailure(java.lang.CharSequence);
- method public void onSuccess(android.service.autofill.FillCallback.FillData);
- }
-
- public static final class FillCallback.FillData {
- }
-
- public static class FillCallback.FillData.Builder {
- ctor public FillCallback.FillData.Builder();
- method public android.service.autofill.FillCallback.FillData build();
- method public android.service.autofill.FillCallback.FillData.Builder setTextField(int, java.lang.String);
+ method public void onSuccess(android.view.autofill.FillResponse);
}
public final class SaveCallback {
method public void onFailure(java.lang.CharSequence);
- method public void onSuccess(int[]);
+ method public void onSuccess(android.view.autofill.AutoFillId[]);
}
}
@@ -36844,6 +36836,7 @@ package android.telecom {
method public void unhold();
method public void unregisterCallback(android.telecom.Call.Callback);
field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
+ field public static final java.lang.String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS";
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_CONNECTING = 9; // 0x9
field public static final int STATE_DIALING = 1; // 0x1
@@ -39579,6 +39572,7 @@ package android.text {
method public android.text.StaticLayout.Builder setHyphenationFrequency(int);
method public android.text.StaticLayout.Builder setIncludePad(boolean);
method public android.text.StaticLayout.Builder setIndents(int[], int[]);
+ method public android.text.StaticLayout.Builder setJustify(boolean);
method public android.text.StaticLayout.Builder setLineSpacing(float, float);
method public android.text.StaticLayout.Builder setMaxLines(int);
method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
@@ -43356,6 +43350,7 @@ package android.view {
public static abstract interface SurfaceHolder.Callback2 implements android.view.SurfaceHolder.Callback {
method public abstract void surfaceRedrawNeeded(android.view.SurfaceHolder);
+ method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, java.lang.Runnable);
}
public class SurfaceView extends android.view.View {
@@ -43436,6 +43431,7 @@ package android.view {
method public void addTouchables(java.util.ArrayList<android.view.View>);
method public android.view.ViewPropertyAnimator animate();
method public void announceForAccessibility(java.lang.CharSequence);
+ method public void autoFill(android.view.autofill.AutoFillValue);
method protected boolean awakenScrollBars();
method protected boolean awakenScrollBars(int);
method protected boolean awakenScrollBars(int, boolean);
@@ -43487,8 +43483,8 @@ package android.view {
method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
method public boolean dispatchNestedScroll(int, int, int, int, int[]);
method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
- method public deprecated void dispatchProvideStructure(android.view.ViewStructure);
- method public void dispatchProvideStructure(android.view.ViewStructure, int);
+ method public void dispatchProvideAutoFillStructure(android.view.ViewStructure, int);
+ method public void dispatchProvideStructure(android.view.ViewStructure);
method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
method protected void dispatchSetActivated(boolean);
@@ -43523,6 +43519,8 @@ package android.view {
method public float getAlpha();
method public android.view.animation.Animation getAnimation();
method public android.os.IBinder getApplicationWindowToken();
+ method public android.view.autofill.AutoFillType getAutoFillType();
+ method public android.view.autofill.VirtualViewDelegate getAutoFillVirtualViewDelegate(android.view.autofill.VirtualViewDelegate.Callback);
method public android.graphics.drawable.Drawable getBackground();
method public android.content.res.ColorStateList getBackgroundTintList();
method public android.graphics.PorterDuff.Mode getBackgroundTintMode();
@@ -43760,10 +43758,10 @@ package android.view {
method protected void onMeasure(int, int);
method protected void onOverScrolled(int, int, boolean, boolean);
method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
- method public deprecated void onProvideStructure(android.view.ViewStructure);
- method public void onProvideStructure(android.view.ViewStructure, int);
- method public deprecated void onProvideVirtualStructure(android.view.ViewStructure);
- method public void onProvideVirtualStructure(android.view.ViewStructure, int);
+ method public void onProvideAutoFillStructure(android.view.ViewStructure, int);
+ method public void onProvideAutoFillVirtualStructure(android.view.ViewStructure, int);
+ method public void onProvideStructure(android.view.ViewStructure);
+ method public void onProvideVirtualStructure(android.view.ViewStructure);
method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
method protected void onRestoreInstanceState(android.os.Parcelable);
method public void onRtlPropertiesChanged(int);
@@ -43970,8 +43968,8 @@ package android.view {
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
- field public static final int ASSIST_FLAG_NON_SANITIZED_TEXT = 2; // 0x2
- field public static final int ASSIST_FLAG_SANITIZED_TEXT = 1; // 0x1
+ field public static final int AUTO_FILL_FLAG_TYPE_FILL = 1; // 0x1
+ field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 2; // 0x2
field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
@@ -44605,6 +44603,7 @@ package android.view {
method public abstract int addChildCount(int);
method public abstract void asyncCommit();
method public abstract android.view.ViewStructure asyncNewChild(int);
+ method public abstract android.view.ViewStructure asyncNewChild(int, int);
method public abstract int getChildCount();
method public abstract android.os.Bundle getExtras();
method public abstract java.lang.CharSequence getHint();
@@ -44613,9 +44612,11 @@ package android.view {
method public abstract int getTextSelectionStart();
method public abstract boolean hasExtras();
method public abstract android.view.ViewStructure newChild(int);
+ method public abstract android.view.ViewStructure newChild(int, int);
method public abstract void setAccessibilityFocused(boolean);
method public abstract void setActivated(boolean);
method public abstract void setAlpha(float);
+ method public abstract void setAutoFillType(android.view.autofill.AutoFillType);
method public abstract void setCheckable(boolean);
method public abstract void setChecked(boolean);
method public abstract void setChildCount(int);
@@ -45838,6 +45839,80 @@ package android.view.animation {
}
+package android.view.autofill {
+
+ public final class AutoFillId implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillId> CREATOR;
+ }
+
+ public final class AutoFillType implements android.os.Parcelable {
+ method public int describeContents();
+ method public static android.view.autofill.AutoFillType forList();
+ method public static android.view.autofill.AutoFillType forText(int);
+ method public static android.view.autofill.AutoFillType forToggle();
+ method public int getSubType();
+ method public boolean isList();
+ method public boolean isText();
+ method public boolean isToggle();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillType> CREATOR;
+ }
+
+ public final class AutoFillValue implements android.os.Parcelable {
+ method public int describeContents();
+ method public static android.view.autofill.AutoFillValue forList(int);
+ method public static android.view.autofill.AutoFillValue forText(java.lang.CharSequence);
+ method public static android.view.autofill.AutoFillValue forToggle(boolean);
+ method public int getListValue();
+ method public java.lang.CharSequence getTextValue();
+ method public boolean getToggleValue();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillValue> CREATOR;
+ }
+
+ public final class Dataset implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.Dataset> CREATOR;
+ }
+
+ public static final class Dataset.Builder {
+ ctor public Dataset.Builder(java.lang.CharSequence);
+ method public android.view.autofill.Dataset build();
+ method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle);
+ method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
+ }
+
+ public final class FillResponse implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.autofill.FillResponse> CREATOR;
+ }
+
+ public static final class FillResponse.Builder {
+ ctor public FillResponse.Builder();
+ method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset);
+ method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
+ method public android.view.autofill.FillResponse build();
+ method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+ }
+
+ public abstract class VirtualViewDelegate {
+ ctor public VirtualViewDelegate();
+ method public abstract void autoFill(int, android.view.autofill.AutoFillValue);
+ }
+
+ public static abstract class VirtualViewDelegate.Callback {
+ ctor public VirtualViewDelegate.Callback();
+ method public void onFocusChanged(int, boolean);
+ method public void onNodeRemoved(int...);
+ method public void onValueChanged(int);
+ }
+
+}
+
package android.view.inputmethod {
public class BaseInputConnection implements android.view.inputmethod.InputConnection {
@@ -49289,6 +49364,7 @@ package android.widget {
method public boolean getIncludeFontPadding();
method public android.os.Bundle getInputExtras(boolean);
method public int getInputType();
+ method public boolean getJustify();
method public final android.text.method.KeyListener getKeyListener();
method public final android.text.Layout getLayout();
method public float getLetterSpacing();
@@ -49397,6 +49473,7 @@ package android.widget {
method public void setIncludeFontPadding(boolean);
method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public void setInputType(int);
+ method public void setJustify(boolean);
method public void setKeyListener(android.text.method.KeyListener);
method public void setLetterSpacing(float);
method public void setLineSpacing(float, float);
diff --git a/api/test-removed.txt b/api/test-removed.txt
index d7a8bce2d00b..ab22b6ec1720 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -126,6 +126,12 @@ package android.media.tv {
package android.net {
+ public class ConnectivityManager {
+ method public deprecated boolean requestRouteToHost(int, int);
+ method public deprecated int startUsingNetworkFeature(int, java.lang.String);
+ method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
+ }
+
public abstract class PskKeyManager {
ctor public PskKeyManager();
field public static final int MAX_IDENTITY_HINT_LENGTH_BYTES = 128; // 0x80
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 0d9e8a0afad2..87e54166b3a1 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -70,9 +70,8 @@ import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StrictMode;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.service.autofill.FillableInputField;
import android.service.autofill.AutoFillService;
-import android.service.autofill.IAutoFillCallback;
+import android.service.autofill.IAutoFillAppCallback;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.TextAssistant;
@@ -115,6 +114,11 @@ import android.view.Window.WindowControllerCallback;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
+import android.view.autofill.VirtualViewDelegate;
+import android.view.autofill.Dataset;
+import android.view.autofill.DatasetField;
+import android.view.autofill.AutoFillId;
+import android.view.autofill.FillResponse;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.Toast;
@@ -130,6 +134,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -697,6 +702,9 @@ public class Activity extends ContextThemeWrapper
private static final String TAG = "Activity";
private static final boolean DEBUG_LIFECYCLE = false;
+ // TODO(b/33197203): set to false once stable
+ private static final boolean DEBUG_AUTO_FILL = true;
+
/** Standard activity result: operation canceled. */
public static final int RESULT_CANCELED = 0;
/** Standard activity result: operation succeeded. */
@@ -847,7 +855,10 @@ public class Activity extends ContextThemeWrapper
private boolean mEatKeyUpEvent;
@GuardedBy("this")
- private IAutoFillCallback mAutoFillCallback;
+ private WeakReference<IAutoFillAppCallback> mAutoFillCallback;
+
+ @GuardedBy("this")
+ private VirtualViewDelegate.Callback mAutoFillDelegateCallback;
private static native String getDlWarning();
@@ -1718,47 +1729,73 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Lazily gets the {@code IAutoFillCallback} for this activitity.
+ * Lazily sets the {@link #mAutoFillDelegateCallback}.
+ */
+ private void setAutoFillDelegateCallback() {
+ synchronized (this) {
+ if (mAutoFillDelegateCallback == null) {
+ mAutoFillDelegateCallback = new VirtualViewDelegate.Callback() {
+ // TODO(b/33197203): implement
+ };
+ }
+ }
+ }
+
+ /**
+ * Lazily gets the {@link IAutoFillAppCallback} for this activitity.
*
* <p>This callback is used by the {@link AutoFillService} app to auto-fill the activity fields.
*/
- IAutoFillCallback getAutoFillCallback() {
+ WeakReference<IAutoFillAppCallback> getAutoFillCallback() {
synchronized (this) {
if (mAutoFillCallback == null) {
- mAutoFillCallback = new IAutoFillCallback.Stub() {
+ final IAutoFillAppCallback cb = new IAutoFillAppCallback.Stub() {
@Override
- public void autofill(@SuppressWarnings("rawtypes") List fields)
- throws RemoteException {
+ public void autoFill(Dataset dataset) throws RemoteException {
+ // TODO(b/33197203): must keep the dataset so subsequent calls pass the same
+ // dataset.extras to service
runOnUiThread(() -> {
final View root = getWindow().getDecorView().getRootView();
- for (Object field : fields) {
- if (!(field instanceof FillableInputField)) {
- Slog.w(TAG, "autofill(): invalid type " + field.getClass());
+ for (DatasetField field : dataset.getFields()) {
+ final AutoFillId id = field.getId();
+ if (id == null) {
+ Log.w(TAG, "autoFill(): null id on " + field);
continue;
}
- FillableInputField autoFillField = (FillableInputField) field;
- final int viewId = autoFillField.getId();
+ final int viewId = id.getViewId();
final View view = root.findViewByAccessibilityIdTraversal(viewId);
- // TODO(b/33197203): should handle other types of view as well, but
- // that will require:
- // - a new interface like AutoFillable
- // - a way for the views to define the type of the autofield value
- if ((view instanceof EditText)) {
- ((EditText) view).setText(autoFillField.getValue());
+ if (view == null) {
+ Log.w(TAG, "autoFill(): no View with id " + viewId);
+ continue;
}
- }
- });
- }
- @Override
- public void showError(String message) {
- runOnUiThread(() -> {
- // TODO(b/33197203): temporary show a toast until it uses the Snack bar.
- Toast.makeText(Activity.this, "Auto-fill request failed: " + message,
- Toast.LENGTH_LONG).show();
+ // TODO(b/33197203): handle protected value (like credit card)
+ if (id.isVirtual()) {
+ // Delegate virtual fields to provider.
+ setAutoFillDelegateCallback();
+ final VirtualViewDelegate mgr = view
+ .getAutoFillVirtualViewDelegate(
+ mAutoFillDelegateCallback);
+ if (mgr == null) {
+ Log.w(TAG, "autoFill(): cannot fill virtual " + id
+ + "; no auto-fill provider for view "
+ + view.getClass());
+ continue;
+ }
+ if (DEBUG_AUTO_FILL) {
+ Log.d(TAG, "autoFill(): delegating " + id
+ + " to virtual manager " + mgr);
+ }
+ mgr.autoFill(id.getVirtualChildId(), field.getValue());
+ } else {
+ // Handle non-virtual fields itself.
+ view.autoFill(field.getValue());
+ }
+ }
});
}
};
+ mAutoFillCallback = new WeakReference<IAutoFillAppCallback>(cb);
}
}
return mAutoFillCallback;
@@ -6096,7 +6133,7 @@ public class Activity extends ContextThemeWrapper
if (mAutoFillCallback != null) {
writer.print(prefix); writer.print("mAutoFillCallback: " );
- writer.println(mAutoFillCallback);
+ writer.println(mAutoFillCallback.get());
}
mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index cd50c4dbec15..d362b012c000 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -483,6 +483,12 @@ public class ActivityManager {
/** @hide requestType for assist context: generate full AssistStructure. */
public static final int ASSIST_CONTEXT_FULL = 1;
+ /** @hide requestType for assist context: generate full AssistStructure for auto-fill. */
+ public static final int ASSIST_CONTEXT_AUTO_FILL = 2;
+
+ /** @hide requestType for assist context: generate full AssistStructure for auto-fill save. */
+ public static final int ASSIST_CONTEXT_AUTO_FILL_SAVE = 3;
+
/** @hide Flag for registerUidObserver: report changes in process state. */
public static final int UID_OBSERVER_PROCSTATE = 1<<0;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e34fabced517..e3bbc92ad6c5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -89,7 +89,7 @@ import android.provider.Settings;
import android.security.NetworkSecurityPolicy;
import android.security.net.config.NetworkSecurityConfigProvider;
import android.service.autofill.AutoFillService;
-import android.service.autofill.IAutoFillCallback;
+import android.service.autofill.IAutoFillAppCallback;
import android.service.voice.VoiceInteractionSession;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
@@ -2884,9 +2884,8 @@ public final class ActivityThread {
// - it does not call onProvideAssistData()
// - it needs an IAutoFillCallback
// - it sets the flags so views can provide autofill-specific data (such as passwords)
- boolean forAutoFill = (cmd.flags
- & (View.ASSIST_FLAG_SANITIZED_TEXT
- | View.ASSIST_FLAG_NON_SANITIZED_TEXT)) != 0;
+ boolean forAutoFill = cmd.requestType == ActivityManager.ASSIST_CONTEXT_AUTO_FILL
+ || cmd.requestType == ActivityManager.ASSIST_CONTEXT_AUTO_FILL_SAVE;
// TODO(b/33197203): decide if lastSessionId logic applies to auto-fill sessions
if (mLastSessionId != cmd.sessionId) {
@@ -2910,22 +2909,23 @@ public final class ActivityThread {
if (!forAutoFill) {
r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data);
r.activity.onProvideAssistData(data);
+ referrer = r.activity.onProvideReferrer();
}
- referrer = r.activity.onProvideReferrer();
if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL || forAutoFill) {
structure = new AssistStructure(r.activity, cmd.flags);
Intent activityIntent = r.activity.getIntent();
- if (cmd.flags > 0) {
+ if (forAutoFill) {
data.putInt(VoiceInteractionSession.KEY_FLAGS, cmd.flags);
}
+ boolean addAutoFillCallback = false;
// TODO(b/33197203): re-evaluate conditions below for auto-fill. In particular,
// FLAG_SECURE might be allowed on AUTO_FILL but not on AUTO_FILL_SAVE)
- if (activityIntent != null && (r.window == null ||
+ boolean notSecure = r.window == null ||
(r.window.getAttributes().flags
- & WindowManager.LayoutParams.FLAG_SECURE) == 0)) {
+ & WindowManager.LayoutParams.FLAG_SECURE) == 0;
+ if (activityIntent != null && notSecure) {
if (forAutoFill) {
- IAutoFillCallback autoFillCallback = r.activity.getAutoFillCallback();
- data.putBinder(AutoFillService.KEY_CALLBACK, autoFillCallback.asBinder());
+ addAutoFillCallback = true;
} else {
Intent intent = new Intent(activityIntent);
intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
@@ -2936,10 +2936,21 @@ public final class ActivityThread {
} else {
if (!forAutoFill) {
content.setDefaultIntent(new Intent());
+ } else {
+ // activityIntent is unlikely to be null, but if it is, we should still
+ // set the auto-fill callback.
+ addAutoFillCallback = notSecure;
}
}
if (!forAutoFill) {
r.activity.onProvideAssistContent(content);
+ } else if (addAutoFillCallback) {
+ IAutoFillAppCallback cb = r.activity.getAutoFillCallback().get();
+ if (cb != null) {
+ data.putBinder(AutoFillService.KEY_CALLBACK, cb.asBinder());
+ } else {
+ Slog.w(TAG, "handleRequestAssistContextExtras(): callback was GCed");
+ }
}
}
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 21854d3cfa10..a2d9c45035d3 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -586,7 +586,7 @@ interface IActivityManager {
void unregisterTaskStackListener(ITaskStackListener listener);
void moveStackToDisplay(int stackId, int displayId);
boolean requestAutoFillData(in IResultReceiver receiver, in Bundle receiverExtras,
- in IBinder activityToken, int flags);
+ int resultCode, in IBinder activityToken, int flags);
void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback);
int restartUserInBackground(int userId);
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index e454ae170e28..ef997c90166e 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -95,4 +95,11 @@ oneway interface ITaskStackListener {
* perform relevant animations before the window disappears.
*/
void onTaskRemovalStarted(int taskId);
+
+ /**
+ * Called when the task has been put in a locked state because one or more of the
+ * activities inside it belong to a managed profile user, and that user has just
+ * been locked.
+ */
+ void onTaskProfileLocked(int taskId, int userId);
}
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 063955224e22..ad5e69b5cbd4 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -74,4 +74,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation)
throws RemoteException {
}
+
+ @Override
+ public void onTaskProfileLocked(int taskId, int userId) {
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a56ff5e15e9b..29633aa4fbdd 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -279,6 +279,8 @@ public class DevicePolicyManager {
* <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_LOCAL_TIME} (convert to String), optional</li>
* <li>{@link #EXTRA_PROVISIONING_TIME_ZONE}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_LOCALE}, optional</li>
@@ -290,6 +292,8 @@ public class DevicePolicyManager {
* <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT} (convert to String), optional</li>
* <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_SUPPORT_URL}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_ORGANIZATION_NAME}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li></ul>
*
* @hide
@@ -688,6 +692,67 @@ public class DevicePolicyManager {
= "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
/**
+ * A String extra holding the localized name of the organization under management.
+ *
+ * The name is displayed only during provisioning.
+ *
+ * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME =
+ "android.app.extra.PROVISIONING_ORGANIZATION_NAME";
+
+ /**
+ * A String extra holding a url to the website of the device's provider. The website can be
+ * opened in a browser during provisioning.
+ *
+ * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PROVISIONING_SUPPORT_URL =
+ "android.app.extra.PROVISIONING_SUPPORT_URL";
+
+ /**
+ * A String extra holding the localized name of the device admin package. It should be the same
+ * as the app label of the package.
+ *
+ * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL =
+ "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
+
+ /**
+ * A {@link Uri} extra pointing to the app icon of device admin package. This image will be
+ * shown during the provisioning.
+ * <h5>The following URI schemes are accepted:</h5>
+ * <ul>
+ * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+ * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
+ * </ul>
+ *
+ * <p> It is the responsibility of the caller to provide an image with a reasonable
+ * pixel density for the device.
+ *
+ * <p> If a content: URI is passed, the intent should have the flag
+ * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and the uri should be added to the
+ * {@link android.content.ClipData} of the intent too.
+ *
+ * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI =
+ "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
+
+ /**
* An int extra holding a minimum required version code for the device admin package. If the
* device admin is already installed on the device, it will only be re-downloaded from
* {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION} if the version of the
@@ -808,7 +873,7 @@ public class DevicePolicyManager {
* </ul>
*
* <p> It is the responsibility of the caller to provide an image with a reasonable
- * pixed density for the device.
+ * pixel density for the device.
*
* <p> If a content: URI is passed, the intent should have the flag
* {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and the uri should be added to the
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 1988e4265fb0..b94264e1ac5d 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -21,6 +21,8 @@ import android.view.ViewStructure;
import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.view.autofill.AutoFillType;
+import android.view.autofill.AutoFillId;
import java.util.ArrayList;
@@ -411,25 +413,30 @@ public class AssistStructure implements Parcelable {
mTitle = root.getTitle();
mDisplayId = root.getDisplayId();
mRoot = new ViewNode();
+
+ // Must explicitly call the proper method based on flags since we don't know which
+ // method (if any) was overridden by the View subclass.
+ boolean forAutoFill = (flags
+ & (View.AUTO_FILL_FLAG_TYPE_FILL
+ | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0;
+
ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false);
if ((root.getWindowFlags()& WindowManager.LayoutParams.FLAG_SECURE) != 0) {
// This is a secure window, so it doesn't want a screenshot, and that
// means we should also not copy out its view hierarchy.
- // Must explicitly set which method to calls since View subclasses might
- // have implemented the deprecated method.
- if (flags == 0) {
- view.onProvideStructure(builder);
+ if (forAutoFill) {
+ view.onProvideAutoFillStructure(builder, flags);
} else {
- view.onProvideStructure(builder, flags);
+ view.onProvideStructure(builder);
}
builder.setAssistBlocked(true);
return;
}
- if (flags == 0) {
- view.dispatchProvideStructure(builder);
+ if (forAutoFill) {
+ view.dispatchProvideAutoFillStructure(builder, flags);
} else {
- view.dispatchProvideStructure(builder, flags);
+ view.dispatchProvideStructure(builder);
}
}
@@ -526,7 +533,10 @@ public class AssistStructure implements Parcelable {
String mIdPackage;
String mIdType;
String mIdEntry;
- int mAutoFillId = View.NO_ID;
+ // TODO(b/33197203): once we have more flags, it might be better to store the individual
+ // fields (viewId and childId) of the field.
+ AutoFillId mAutoFillId;
+ AutoFillType mAutoFillType;
int mX;
int mY;
int mScrollX;
@@ -551,7 +561,11 @@ public class AssistStructure implements Parcelable {
static final int FLAGS_ACTIVATED = 0x00002000;
static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000;
- static final int FLAGS_HAS_AUTO_FILL_ID = 0x80000000;
+ // TODO(b/33197203): auto-fill data is made of many fields and ideally we should verify
+ // one-by-one to optimize what's sent over, but there isn't enough flag bits for that, we'd
+ // need to create a 'flags2' or 'autoFillFlags' field and add these flags there.
+ // So, to keep thinkg simpler for now, let's just use on flag for all of them...
+ static final int FLAGS_HAS_AUTO_FILL_DATA = 0x80000000;
static final int FLAGS_HAS_MATRIX = 0x40000000;
static final int FLAGS_HAS_ALPHA = 0x20000000;
static final int FLAGS_HAS_ELEVATION = 0x10000000;
@@ -595,8 +609,9 @@ public class AssistStructure implements Parcelable {
}
}
}
- if ((flags&FLAGS_HAS_AUTO_FILL_ID) != 0) {
- mAutoFillId = in.readInt();
+ if ((flags&FLAGS_HAS_AUTO_FILL_DATA) != 0) {
+ mAutoFillId = in.readParcelable(null);
+ mAutoFillType = in.readParcelable(null);
}
if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
mX = in.readInt();
@@ -653,8 +668,8 @@ public class AssistStructure implements Parcelable {
if (mId != View.NO_ID) {
flags |= FLAGS_HAS_ID;
}
- if (mAutoFillId != View.NO_ID) {
- flags |= FLAGS_HAS_AUTO_FILL_ID;
+ if (mAutoFillId != null) {
+ flags |= FLAGS_HAS_AUTO_FILL_DATA;
}
if ((mX&~0x7fff) != 0 || (mY&~0x7fff) != 0
|| (mWidth&~0x7fff) != 0 | (mHeight&~0x7fff) != 0) {
@@ -700,8 +715,9 @@ public class AssistStructure implements Parcelable {
}
}
}
- if ((flags&FLAGS_HAS_AUTO_FILL_ID) != 0) {
- out.writeInt(mAutoFillId);
+ if ((flags&FLAGS_HAS_AUTO_FILL_DATA) != 0) {
+ out.writeParcelable(mAutoFillId, 0);
+ out.writeParcelable(mAutoFillType, 0);
}
if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
out.writeInt(mX);
@@ -773,16 +789,26 @@ public class AssistStructure implements Parcelable {
}
/**
- * Returns the id that can be used to auto-fill the view.
+ * Gets the id that can be used to auto-fill the view contents.
*
* <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not
* for assist.
*/
- public int getAutoFillId() {
+ public AutoFillId getAutoFillId() {
return mAutoFillId;
}
/**
+ * Gets the the type of value that can be used to auto-fill the view contents.
+ *
+ * <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not
+ * for assist.
+ */
+ public AutoFillType getAutoFillType() {
+ return mAutoFillType;
+ }
+
+ /**
* Returns the left edge of this view, in pixels, relative to the left edge of its parent.
*/
public int getLeft() {
@@ -1318,17 +1344,23 @@ public class AssistStructure implements Parcelable {
return mNode.mChildren != null ? mNode.mChildren.length : 0;
}
- @Override
- public ViewStructure newChild(int index) {
+ private void setAutoFillId(ViewNode child, boolean forAutoFill, int virtualId) {
+ if (forAutoFill) {
+ child.mAutoFillId = new AutoFillId(mNode.mAutoFillId, virtualId);
+ }
+ }
+
+ private ViewStructure newChild(int index, boolean forAutoFill, int virtualId) {
ViewNode node = new ViewNode();
+ setAutoFillId(node, forAutoFill, virtualId);
mNode.mChildren[index] = node;
return new ViewNodeBuilder(mAssist, node, false);
}
- @Override
- public ViewStructure asyncNewChild(int index) {
+ private ViewStructure asyncNewChild(int index, boolean forAutoFill, int virtualId) {
synchronized (mAssist) {
ViewNode node = new ViewNode();
+ setAutoFillId(node, forAutoFill, virtualId);
mNode.mChildren[index] = node;
ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true);
mAssist.mPendingAsyncChildren.add(builder);
@@ -1337,6 +1369,26 @@ public class AssistStructure implements Parcelable {
}
@Override
+ public ViewStructure newChild(int index) {
+ return newChild(index, false, 0);
+ }
+
+ @Override
+ public ViewStructure newChild(int index, int virtualId) {
+ return newChild(index, true, virtualId);
+ }
+
+ @Override
+ public ViewStructure asyncNewChild(int index) {
+ return asyncNewChild(index, false, 0);
+ }
+
+ @Override
+ public ViewStructure asyncNewChild(int index, int virtualId) {
+ return asyncNewChild(index, true, virtualId);
+ }
+
+ @Override
public void asyncCommit() {
synchronized (mAssist) {
if (!mAsync) {
@@ -1356,9 +1408,20 @@ public class AssistStructure implements Parcelable {
}
@Override
- public void setAutoFillId(int autoFillId) {
- mNode.mAutoFillId = autoFillId;
+ public void setAutoFillId(int viewId) {
+ mNode.mAutoFillId = new AutoFillId(viewId);
+ }
+
+ @Override
+ public AutoFillId getAutoFillId() {
+ return mNode.mAutoFillId;
}
+
+ @Override
+ public void setAutoFillType(AutoFillType type) {
+ mNode.mAutoFillType = type;
+ }
+
}
/** @hide */
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 4c8360f6c345..ef3b1c50ffd3 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -347,11 +347,11 @@ public class AppWidgetHostView extends FrameLayout {
}
/**
- * Sets an executor which can be used for asynchronously inflating and applying the remoteviews.
- * @see {@link RemoteViews#applyAsync(Context, ViewGroup, RemoteViews.OnViewAppliedListener, Executor)}
+ * Sets an executor which can be used for asynchronously inflating. CPU intensive tasks like
+ * view inflation or loading images will be performed on the executor. The updates will still
+ * be applied on the UI thread.
*
* @param executor the executor to use or null.
- * @hide
*/
public void setAsyncExecutor(Executor executor) {
if (mLastExecutionSignal != null) {
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 318968158559..31e779f0dc4b 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -1094,8 +1094,9 @@ public class AppWidgetManager {
* <p>Only apps with a foreground activity or a foreground service can call it. Otherwise
* it'll throw {@link IllegalStateException}.
*
- * <p>When an app calls this API when a previous request is still waiting for a response,
- * the previous request will be canceled.
+ * <p>It's up to the launcher how to handle previous pending requests when the same package
+ * calls this API multiple times in a row. It may ignore the previous requests,
+ * for example.
*
* @param provider The {@link ComponentName} for the {@link
* android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget.
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index b68693880acf..98a5341b3025 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -243,7 +243,7 @@ public final class BluetoothSocket implements Closeable {
}
as.mPfd = new ParcelFileDescriptor(fds[0]);
- as.mSocket = new LocalSocket(fds[0]);
+ as.mSocket = LocalSocket.createConnectedLocalSocket(fds[0]);
as.mSocketIS = as.mSocket.getInputStream();
as.mSocketOS = as.mSocket.getOutputStream();
as.mAddress = RemoteAddr;
@@ -367,7 +367,7 @@ public final class BluetoothSocket implements Closeable {
if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
if (mPfd == null) throw new IOException("bt socket connect failed");
FileDescriptor fd = mPfd.getFileDescriptor();
- mSocket = new LocalSocket(fd);
+ mSocket = LocalSocket.createConnectedLocalSocket(fd);
mSocketIS = mSocket.getInputStream();
mSocketOS = mSocket.getOutputStream();
}
@@ -416,9 +416,9 @@ public final class BluetoothSocket implements Closeable {
if(mSocketState != SocketState.INIT) return EBADFD;
if(mPfd == null) return -1;
FileDescriptor fd = mPfd.getFileDescriptor();
- if (DBG) Log.d(TAG, "bindListen(), new LocalSocket ");
- mSocket = new LocalSocket(fd);
- if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() ");
+ if (DBG) Log.d(TAG, "bindListen(), Create LocalSocket");
+ mSocket = LocalSocket.createConnectedLocalSocket(fd);
+ if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream()");
mSocketIS = mSocket.getInputStream();
mSocketOS = mSocket.getOutputStream();
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 4b5b995cd218..afd757877603 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -1200,7 +1200,7 @@ public class LauncherApps {
/**
* Return {@code TRUE} if a request is valid -- i.e. {@link #accept(Bundle)} has not been
- * called, and it has not been canceled.
+ * called yet.
*/
public boolean isValid() {
try {
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index c8fb3d174fe0..385340000d83 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -846,8 +846,9 @@ public class ShortcutManager {
* <p>Only apps with a foreground activity or a foreground service can call it. Otherwise
* it'll throw {@link IllegalStateException}.
*
- * <p>When an app calls this API when a previous request is still waiting for a response,
- * the previous request will be canceled.
+ * <p>It's up to the launcher how to handle previous pending requests when the same package
+ * calls this API multiple times in a row. It may ignore the previous requests,
+ * for example.
*
* @param shortcut New shortcut to pin. If an app wants to pin an existing (either dynamic
* or manifest) shortcut, then it only needs to have an ID, and other fields don't have to
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 13ba6cc37d22..aea125835748 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -167,6 +167,8 @@ public abstract class DisplayManagerInternal {
public static final int POLICY_DIM = 2;
// Policy: Make the screen bright as usual.
public static final int POLICY_BRIGHT = 3;
+ // Policy: Keep the screen and display optimized for VR mode.
+ public static final int POLICY_VR = 4;
// The basic overall policy to apply: off, doze, dim or bright.
public int policy;
@@ -233,6 +235,10 @@ public abstract class DisplayManagerInternal {
return policy == POLICY_BRIGHT || policy == POLICY_DIM;
}
+ public boolean isVr() {
+ return policy == POLICY_VR;
+ }
+
public void copyFrom(DisplayPowerRequest other) {
policy = other.policy;
useProximitySensor = other.useProximitySensor;
@@ -301,6 +307,8 @@ public abstract class DisplayManagerInternal {
return "DIM";
case POLICY_BRIGHT:
return "BRIGHT";
+ case POLICY_VR:
+ return "VR";
default:
return Integer.toString(policy);
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index d1d5f40739db..ac0c0dc5a916 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1163,6 +1163,7 @@ public class ConnectivityManager {
* {@link #requestNetwork(NetworkRequest, NetworkCallback)} API.
* In {@link VERSION_CODES#M}, and above, this method is unsupported and will
* throw {@code UnsupportedOperationException} if called.
+ * @removed
*/
@Deprecated
public int startUsingNetworkFeature(int networkType, String feature) {
@@ -1219,6 +1220,7 @@ public class ConnectivityManager {
* {@link #unregisterNetworkCallback(NetworkCallback)} API.
* In {@link VERSION_CODES#M}, and above, this method is unsupported and will
* throw {@code UnsupportedOperationException} if called.
+ * @removed
*/
@Deprecated
public int stopUsingNetworkFeature(int networkType, String feature) {
@@ -1633,6 +1635,7 @@ public class ConnectivityManager {
* {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} API.
* In {@link VERSION_CODES#M}, and above, this method is unsupported and will
* throw {@code UnsupportedOperationException} if called.
+ * @removed
*/
@Deprecated
public boolean requestRouteToHost(int networkType, int hostAddress) {
diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl
index 932f03116f15..9573953e6e13 100644
--- a/core/java/android/net/INetworkScoreService.aidl
+++ b/core/java/android/net/INetworkScoreService.aidl
@@ -100,4 +100,21 @@ interface INetworkScoreService
* @hide
*/
boolean requestScores(in NetworkKey[] networks);
+
+ /**
+ * Determine whether the application with the given UID is the enabled scorer.
+ *
+ * @param callingUid the UID to check
+ * @return true if the provided UID is the active scorer, false otherwise.
+ * @hide
+ */
+ boolean isCallerActiveScorer(int callingUid);
+
+ /**
+ * Obtain the package name of the current active network scorer.
+ *
+ * @return the full package name of the current active scorer, or null if there is no active
+ * scorer.
+ */
+ String getActiveScorerPackage();
}
diff --git a/core/java/android/net/LocalServerSocket.java b/core/java/android/net/LocalServerSocket.java
index e1eaf00d0f20..3fcde33071e9 100644
--- a/core/java/android/net/LocalServerSocket.java
+++ b/core/java/android/net/LocalServerSocket.java
@@ -89,7 +89,7 @@ public class LocalServerSocket {
impl.accept(acceptedImpl);
- return LocalSocket.createLocalSocketForAccept(acceptedImpl, LocalSocket.SOCKET_UNKNOWN);
+ return LocalSocket.createLocalSocketForAccept(acceptedImpl);
}
/**
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index d9ad74beb016..8afa1ed3e4a5 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -31,6 +31,7 @@ import java.net.SocketOptions;
public class LocalSocket implements Closeable {
private final LocalSocketImpl impl;
+ /** false if impl.create() needs to be called */
private volatile boolean implCreated;
private LocalSocketAddress localAddress;
private boolean isBound;
@@ -61,19 +62,6 @@ public class LocalSocket implements Closeable {
*/
public LocalSocket(int sockType) {
this(new LocalSocketImpl(), sockType);
- isBound = false;
- isConnected = false;
- }
-
- /**
- * Creates a AF_LOCAL/UNIX domain stream socket with FileDescriptor.
- * @hide
- */
- public LocalSocket(FileDescriptor fd) throws IOException {
- this(new LocalSocketImpl(fd), SOCKET_UNKNOWN);
- isBound = true;
- isConnected = true;
- implCreated = true;
}
private LocalSocket(LocalSocketImpl impl, int sockType) {
@@ -84,9 +72,27 @@ public class LocalSocket implements Closeable {
}
/**
+ * Creates a LocalSocket instances using the FileDescriptor for an already-connected
+ * AF_LOCAL/UNIX domain stream socket. Note: the FileDescriptor must be closed by the caller:
+ * closing the LocalSocket will not close it.
+ *
+ * @hide - used by BluetoothSocket.
+ */
+ public static LocalSocket createConnectedLocalSocket(FileDescriptor fd) {
+ return createConnectedLocalSocket(new LocalSocketImpl(fd), SOCKET_UNKNOWN);
+ }
+
+ /**
* for use with LocalServerSocket.accept()
*/
- static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl, int sockType) {
+ static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl) {
+ return createConnectedLocalSocket(impl, SOCKET_UNKNOWN);
+ }
+
+ /**
+ * Creates a LocalSocket from an existing LocalSocketImpl that is already connected.
+ */
+ private static LocalSocket createConnectedLocalSocket(LocalSocketImpl impl, int sockType) {
LocalSocket socket = new LocalSocket(impl, sockType);
socket.isConnected = true;
socket.isBound = true;
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index d8f782115cf8..05c8afb358b0 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -218,7 +218,7 @@ class LocalSocketImpl
*
* @param fd non-null; bound file descriptor
*/
- /*package*/ LocalSocketImpl(FileDescriptor fd) throws IOException
+ /*package*/ LocalSocketImpl(FileDescriptor fd)
{
this.fd = fd;
}
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 717ac70ab430..2870dd6c5778 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -168,11 +168,11 @@ public class NetworkScoreManager {
* scorer.
*/
public String getActiveScorerPackage() {
- NetworkScorerAppData app = new NetworkScorerAppManager(mContext).getActiveScorer();
- if (app == null) {
- return null;
+ try {
+ return mService.getActiveScorerPackage();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return app.packageName;
}
/**
@@ -339,4 +339,19 @@ public class NetworkScoreManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Determine whether the application with the given UID is the enabled scorer.
+ *
+ * @param callingUid the UID to check
+ * @return true if the provided UID is the active scorer, false otherwise.
+ * @hide
+ */
+ public boolean isCallerActiveScorer(int callingUid) {
+ try {
+ return mService.isCallerActiveScorer(callingUid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index ec0bb25d28eb..23d5af55641c 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -16,7 +16,6 @@
package android.net;
-import android.Manifest;
import android.Manifest.permission;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -28,7 +27,9 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+
import com.android.internal.R;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -226,6 +227,7 @@ public class NetworkScorerAppManager {
}
/** Determine whether the application with the given UID is the enabled scorer. */
+ @Deprecated // Use NetworkScoreManager.isCallerActiveScorer()
public boolean isCallerActiveScorer(int callingUid) {
NetworkScorerAppData defaultApp = getActiveScorer();
if (defaultApp == null) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 9d04929b2c8f..dff0a287e25c 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -49,23 +49,23 @@ import android.util.Log;
* <i>These levels are mutually exclusive - you may only specify one of them.</i>
*
* <table>
- * <tr><th>Flag Value</th>
+ * <tr><th>Flag Value</th>
* <th>CPU</th> <th>Screen</th> <th>Keyboard</th></tr>
*
* <tr><td>{@link #PARTIAL_WAKE_LOCK}</td>
- * <td>On*</td> <td>Off</td> <td>Off</td>
+ * <td>On*</td> <td>Off</td> <td>Off</td>
* </tr>
- *
+ *
* <tr><td>{@link #SCREEN_DIM_WAKE_LOCK}</td>
- * <td>On</td> <td>Dim</td> <td>Off</td>
+ * <td>On</td> <td>Dim</td> <td>Off</td>
* </tr>
*
* <tr><td>{@link #SCREEN_BRIGHT_WAKE_LOCK}</td>
- * <td>On</td> <td>Bright</td> <td>Off</td>
+ * <td>On</td> <td>Bright</td> <td>Off</td>
* </tr>
- *
+ *
* <tr><td>{@link #FULL_WAKE_LOCK}</td>
- * <td>On</td> <td>Bright</td> <td>Bright</td>
+ * <td>On</td> <td>Bright</td> <td>Bright</td>
* </tr>
* </table>
* </p><p>
@@ -85,13 +85,13 @@ import android.util.Log;
* the illumination to remain on once it turns on (e.g. from user activity). This flag
* will force the screen and/or keyboard to turn on immediately, when the WakeLock is
* acquired. A typical use would be for notifications which are important for the user to
- * see immediately.</td>
+ * see immediately.</td>
* </tr>
- *
+ *
* <tr><td>{@link #ON_AFTER_RELEASE}</td>
* <td>If this flag is set, the user activity timer will be reset when the WakeLock is
- * released, causing the illumination to remain on a bit longer. This can be used to
- * reduce flicker if you are cycling between wake lock conditions.</td>
+ * released, causing the illumination to remain on a bit longer. This can be used to
+ * reduce flicker if you are cycling between wake lock conditions.</td>
* </tr>
* </table>
* <p>
@@ -479,6 +479,35 @@ public final class PowerManager {
}
/**
+ * Gets the minimum supported screen brightness setting for VR Mode.
+ * @hide
+ */
+ public int getMinimumScreenBrightnessForVrSetting() {
+ return mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_screenBrightnessForVrSettingMinimum);
+ }
+
+ /**
+ * Gets the maximum supported screen brightness setting for VR Mode.
+ * The screen may be allowed to become dimmer than this value but
+ * this is the maximum value that can be set by the user.
+ * @hide
+ */
+ public int getMaximumScreenBrightnessForVrSetting() {
+ return mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_screenBrightnessForVrSettingMaximum);
+ }
+
+ /**
+ * Gets the default screen brightness for VR setting.
+ * @hide
+ */
+ public int getDefaultScreenBrightnessForVrSetting() {
+ return mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_screenBrightnessForVrSettingDefault);
+ }
+
+ /**
* Returns true if the twilight service should be used to adjust screen brightness
* policy. This setting is experimental and disabled by default.
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cede9eb5e675..2d4a9e93af7e 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2889,6 +2889,15 @@ public final class Settings {
new InclusiveIntegerRangeValidator(0, 255);
/**
+ * The screen backlight brightness between 0 and 255.
+ * @hide
+ */
+ public static final String SCREEN_BRIGHTNESS_FOR_VR = "screen_brightness_for_vr";
+
+ private static final Validator SCREEN_BRIGHTNESS_FOR_VR_VALIDATOR =
+ new InclusiveIntegerRangeValidator(0, 255);
+
+ /**
* Control whether to enable automatic brightness mode.
*/
public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
@@ -3882,6 +3891,7 @@ public final class Settings {
VALIDATORS.put(DIM_SCREEN, DIM_SCREEN_VALIDATOR);
VALIDATORS.put(SCREEN_OFF_TIMEOUT, SCREEN_OFF_TIMEOUT_VALIDATOR);
VALIDATORS.put(SCREEN_BRIGHTNESS, SCREEN_BRIGHTNESS_VALIDATOR);
+ VALIDATORS.put(SCREEN_BRIGHTNESS_FOR_VR, SCREEN_BRIGHTNESS_FOR_VR_VALIDATOR);
VALIDATORS.put(SCREEN_BRIGHTNESS_MODE, SCREEN_BRIGHTNESS_MODE_VALIDATOR);
VALIDATORS.put(MODE_RINGER_STREAMS_AFFECTED, MODE_RINGER_STREAMS_AFFECTED_VALIDATOR);
VALIDATORS.put(MUTE_STREAMS_AFFECTED, MUTE_STREAMS_AFFECTED_VALIDATOR);
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
index a7941c7ebeeb..c2e980c5c277 100644
--- a/core/java/android/service/autofill/AutoFillService.java
+++ b/core/java/android/service/autofill/AutoFillService.java
@@ -17,8 +17,8 @@ package android.service.autofill;
import static android.service.voice.VoiceInteractionSession.KEY_FLAGS;
import static android.service.voice.VoiceInteractionSession.KEY_STRUCTURE;
-import static android.view.View.ASSIST_FLAG_SANITIZED_TEXT;
-import static android.view.View.ASSIST_FLAG_NON_SANITIZED_TEXT;
+import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
+import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
import android.annotation.SdkConstant;
import android.app.Activity;
@@ -32,12 +32,15 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+import android.view.autofill.AutoFillId;
+import android.view.autofill.FillResponse;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
-// TODO(b/33197203): improve javadoc (class and methods)
+// TODO(b/33197203): improve javadoc (of both class and methods); in particular, make sure the
+// life-cycle (and how state could be maintained on server-side) is well documented.
/**
* Top-level service of the current auto-fill service for a given user.
@@ -58,40 +61,50 @@ public abstract class AutoFillService extends Service {
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
- // Bundle keys.
- /** @hide */
- public static final String KEY_CALLBACK = "callback";
+ // Internal bundle keys.
+ /** @hide */ public static final String KEY_CALLBACK = "callback";
+ /** @hide */ public static final String KEY_SAVABLE_IDS = "savable_ids";
+
+ // Prefix for public bundle keys.
+ private static final String KEY_PREFIX = "android.service.autofill.extra.";
+
+ /**
+ * Key of the {@link Bundle} passed to methods such as
+ * {@link #onSaveRequest(AssistStructure, Bundle, CancellationSignal, SaveCallback)}
+ * containing the extras set by
+ * {@link android.view.autofill.FillResponse.Builder#setExtras(Bundle)}.
+ */
+ public static final String EXTRA_RESPONSE_EXTRAS = KEY_PREFIX + "RESPONSE_EXTRAS";
+
+ /**
+ * Key of the {@link Bundle} passed to methods such as
+ * {@link #onSaveRequest(AssistStructure, Bundle, CancellationSignal, SaveCallback)}
+ * containing the extras set by
+ * {@link android.view.autofill.Dataset.Builder#setExtras(Bundle)}.
+ */
+ public static final String EXTRA_DATASET_EXTRAS = KEY_PREFIX + "DATASET_EXTRAS";
// Handler messages.
private static final int MSG_CONNECT = 1;
private static final int MSG_AUTO_FILL_ACTIVITY = 2;
private static final int MSG_DISCONNECT = 3;
- private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
- @Override
- public void send(int resultCode, Bundle resultData) throws RemoteException {
- final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE);
- final IBinder binder = resultData.getBinder(KEY_CALLBACK);
- final int flags = resultData.getInt(KEY_FLAGS, 0);
+ private final IAutoFillService mInterface = new IAutoFillService.Stub() {
+ @Override
+ public void autoFill(AssistStructure structure, IAutoFillServerCallback callback,
+ Bundle extras, int flags) {
mHandlerCaller
- .obtainMessageIOO(MSG_AUTO_FILL_ACTIVITY, flags, structure, binder).sendToTarget();
+ .obtainMessageIOOO(MSG_AUTO_FILL_ACTIVITY, flags, structure, extras, callback)
+ .sendToTarget();
}
- };
-
- private final IAutoFillService mInterface = new IAutoFillService.Stub() {
@Override
public void onConnected() {
mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CONNECT));
}
@Override
- public IResultReceiver getAssistReceiver() {
- return mAssistReceiver;
- }
-
- @Override
public void onDisconnected() {
mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DISCONNECT));
}
@@ -107,10 +120,11 @@ public abstract class AutoFillService extends Service {
break;
} case MSG_AUTO_FILL_ACTIVITY: {
final SomeArgs args = (SomeArgs) msg.obj;
- final AssistStructure structure = (AssistStructure) args.arg1;
- final IBinder binder = (IBinder) args.arg2;
final int flags = msg.arg1;
- requestAutoFill(structure, flags, binder);
+ final AssistStructure structure = (AssistStructure) args.arg1;
+ final Bundle extras = (Bundle) args.arg2;
+ final IAutoFillServerCallback callback = (IAutoFillServerCallback) args.arg3;
+ requestAutoFill(callback, structure, extras, flags);
break;
} case MSG_DISCONNECT: {
onDisconnected();
@@ -146,7 +160,7 @@ public abstract class AutoFillService extends Service {
}
/**
- * Called when the Android System connects to service.
+ * Called when the Android system connects to service.
*
* <p>You should generally do initialization here rather than in {@link #onCreate}.
*/
@@ -155,12 +169,19 @@ public abstract class AutoFillService extends Service {
}
/**
- * Called when user requests service to auto-fill an {@link Activity}.
+ * Called by the Android system do decide if an {@link Activity} can be auto-filled by the
+ * service.
+ *
+ * <p>Service must call one of the {@link FillCallback} methods (like
+ * {@link FillCallback#onSuccess(FillResponse)} or {@link FillCallback#onFailure(CharSequence)})
+ * to notify the result of the request.
*
- * @param structure {@link Activity}'s view structure .
- * @param data bundle with optional parameters (currently none) which is passed along on
- * subsequent calls (so it can be used by the service to share data).
+ * @param structure {@link Activity}'s view structure.
+ * @param data bundle containing additional arguments set by the Android system (currently none)
+ * or data passed by the service on previous calls to fullfill other sections of this activity
+ * (see {@link FillResponse} Javadoc for examples of multiple-sections requests).
* @param cancellationSignal signal for observing cancel requests.
+ * @param callback object used to notify the result of the request.
*/
public abstract void onFillRequest(AssistStructure structure,
Bundle data, CancellationSignal cancellationSignal, FillCallback callback);
@@ -168,29 +189,31 @@ public abstract class AutoFillService extends Service {
/**
* Called when user requests service to save the fields of an {@link Activity}.
*
+ * <p>Service must call one of the {@link SaveCallback} methods (like
+ * {@link SaveCallback#onSuccess(AutoFillId[])} or {@link SaveCallback#onFailure(CharSequence)})
+ * to notify the result of the request.
+ *
* @param structure {@link Activity}'s view structure.
- * @param data same bundle passed to
- * {@link #onFillRequest(AssistStructure, Bundle, CancellationSignal, FillCallback)};
- * might also contain with optional parameters (currently none).
+ * @param data bundle containing additional arguments set by the Android system (currently none)
+ * or data passed by the service in the {@link FillResponse} that originated this call.
* @param cancellationSignal signal for observing cancel requests.
* @param callback object used to notify the result of the request.
*/
public abstract void onSaveRequest(AssistStructure structure,
Bundle data, CancellationSignal cancellationSignal, SaveCallback callback);
- private void requestAutoFill(AssistStructure structure, int flags, IBinder binder) {
- // TODO(b/33197203): pass the Bundle received from mAssistReceiver instead?
- final Bundle data = new Bundle();
+ private void requestAutoFill(IAutoFillServerCallback callback, AssistStructure structure,
+ Bundle data, int flags) {
switch (flags) {
- case ASSIST_FLAG_SANITIZED_TEXT:
- final FillCallback fillCallback = new FillCallback(binder);
+ case AUTO_FILL_FLAG_TYPE_FILL:
+ final FillCallback fillCallback = new FillCallback(callback);
// TODO(b/33197203): hook up the cancelationSignal
onFillRequest(structure, data, new CancellationSignal(), fillCallback);
break;
- case ASSIST_FLAG_NON_SANITIZED_TEXT:
- final SaveCallback saveCallback = new SaveCallback(binder);
+ case AUTO_FILL_FLAG_TYPE_SAVE:
+ final SaveCallback saveCallback = new SaveCallback(callback);
// TODO(b/33197203): hook up the cancelationSignal
- onSaveRequest(structure, null, new CancellationSignal(), saveCallback);
+ onSaveRequest(structure, data, new CancellationSignal(), saveCallback);
break;
default:
Log.w(TAG, "invalid flag on requestAutoFill(): " + flags);
@@ -198,7 +221,7 @@ public abstract class AutoFillService extends Service {
}
/**
- * Called when the Android System disconnects from the service.
+ * Called when the Android system disconnects from the service.
*
* <p> At this point this service may no longer be an active {@link AutoFillService}.
*/
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index 3284b90304f5..5a9a9f68486c 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -18,19 +18,16 @@ package android.service.autofill;
import static android.service.autofill.AutoFillService.DEBUG;
+import android.annotation.Nullable;
import android.app.Activity;
-import android.app.assist.AssistStructure.ViewNode;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
-import android.util.SparseArray;
+import android.view.autofill.FillResponse;
import com.android.internal.util.Preconditions;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
/**
* Handles auto-fill requests from the {@link AutoFillService} into the {@link Activity} being
* auto-filled.
@@ -39,40 +36,50 @@ public final class FillCallback {
private static final String TAG = "FillCallback";
- private final IAutoFillCallback mCallback;
+ private final IAutoFillServerCallback mCallback;
+
+ private boolean mReplied = false;
/** @hide */
- FillCallback(IBinder binder) {
- mCallback = IAutoFillCallback.Stub.asInterface(binder);
+ FillCallback(IAutoFillServerCallback callback) {
+ mCallback = callback;
}
/**
- * Auto-fills the {@link Activity}.
+ * Notifies the Android System that an
+ * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, FillCallback)}
+ * was successfully fulfilled by the service.
*
- * @throws RuntimeException if an error occurred while auto-filling it.
+ * @param response auto-fill information for that activity, or {@code null} when the activity
+ * cannot be auto-filled (for example, if it only contains read-only fields).
+ *
+ * @throws RuntimeException if an error occurred while calling the Android System.
*/
- public void onSuccess(FillData data) {
- if (DEBUG) Log.d(TAG, "onSuccess(): data=" + data);
+ public void onSuccess(@Nullable FillResponse response) {
+ if (DEBUG) Log.d(TAG, "onSuccess(): respose=" + response);
- Preconditions.checkArgument(data != null, "data cannot be null");
+ checkNotRepliedYet();
try {
- mCallback.autofill(data.asList());
+ mCallback.showResponse(response);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
- * Notifies the {@link Activity} that the auto-fill request failed.
+ * Notifies the Android System that an
+ * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, FillCallback)}
+ * could not be fulfilled by the service.
*
- * @param message error message to be displayed.
+ * @param message error message to be displayed to the user.
*
- * @throws RuntimeException if an error occurred while notifying the activity.
+ * @throws RuntimeException if an error occurred while calling the Android System.
*/
public void onFailure(CharSequence message) {
if (DEBUG) Log.d(TAG, "onFailure(): message=" + message);
+ checkNotRepliedYet();
Preconditions.checkArgument(message != null, "message cannot be null");
try {
@@ -82,70 +89,9 @@ public final class FillCallback {
}
}
- /**
- * Data used to fill the fields of an {@link Activity}.
- *
- * <p>This class is immutable.
- */
- public static final class FillData {
-
- private final List<FillableInputField> mList;
-
- private FillData(Builder builder) {
- final int size = builder.mFields.size();
- final List<FillableInputField> list = new ArrayList<>(size);
- for (int i = 0; i < size; i++) {
- list.add(builder.mFields.valueAt(i));
- }
- mList = Collections.unmodifiableList(list);
- // TODO: use FastImmutableArraySet or a similar structure instead?
- }
-
- /**
- * Gets the response as a {@code List} so it can be used in a binder call.
- */
- List<FillableInputField> asList() {
- return mList;
- }
-
- @Override
- public String toString() {
- return "[AutoFillResponse: " + mList + "]";
- }
-
- /**
- * Builder for {@link FillData} objects.
- *
- * <p>Typical usage:
- *
- * <pre class="prettyprint">
- * FillCallback.FillData data = new FillCallback.FillData.Builder()
- * .setTextField(id1, "value 1")
- * .setTextField(id2, "value 2")
- * .build()
- * </pre>
- */
- public static class Builder {
- private final SparseArray<FillableInputField> mFields = new SparseArray<>();
-
- /**
- * Auto-fills a text field.
- *
- * @param id view id as returned by {@link ViewNode#getAutoFillId()}.
- * @param text text to be auto-filled.
- * @return same builder so it can be chained.
- */
- public Builder setTextField(int id, String text) {
- mFields.put(id, FillableInputField.forText(id, text));
- return this;
- }
-
- /**
- * Builds a new {@link FillData} instance.
- */
- public FillData build() {
- return new FillData(this);
- }
- }
+ // There can be only one!!
+ private void checkNotRepliedYet() {
+ Preconditions.checkState(!mReplied, "already replied");
+ mReplied = true;
}
}
diff --git a/core/java/android/service/autofill/FillableInputField.java b/core/java/android/service/autofill/FillableInputField.java
deleted file mode 100644
index 62950b40e5e1..000000000000
--- a/core/java/android/service/autofill/FillableInputField.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2016 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.autofill;
-
-import android.app.assist.AssistStructure.ViewNode;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents a view field that can be auto-filled.
- *
- * <p>Currently only text-fields are supported, so the value of the field can be obtained through
- * {@link #getValue()}.
- *
- * @hide
- */
-public final class FillableInputField implements Parcelable {
-
- private final int mId;
- private final String mValue;
-
- private FillableInputField(int id, String value) {
- mId = id;
- mValue = value;
- }
-
- private FillableInputField(Parcel parcel) {
- mId = parcel.readInt();
- mValue = parcel.readString();
- }
-
- /**
- * Gets the view id as returned by {@link ViewNode#getAutoFillId()}.
- */
- public int getId() {
- return mId;
- }
-
- /**
- * Gets the value of this field.
- */
- public String getValue() {
- return mValue;
-
- }
-
- @Override
- public String toString() {
- return "[AutoFillField: " + mId + "=" + mValue + "]";
- }
-
- /**
- * Creates an {@code AutoFillField} for a text field.
- *
- * @param id view id as returned by {@link ViewNode#getAutoFillId()}.
- * @param text value to be auto-filled.
- */
- public static FillableInputField forText(int id, String text) {
- return new FillableInputField(id, text);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeInt(mId);
- parcel.writeString(mValue);
- }
-
- public static final Parcelable.Creator<FillableInputField> CREATOR =
- new Parcelable.Creator<FillableInputField>() {
- @Override
- public FillableInputField createFromParcel(Parcel source) {
- return new FillableInputField(source);
- }
-
- @Override
- public FillableInputField[] newArray(int size) {
- return new FillableInputField[size];
- }
- };
-}
diff --git a/core/java/android/service/autofill/IAutoFillCallback.aidl b/core/java/android/service/autofill/IAutoFillAppCallback.aidl
index d6d4f39651da..629b1f008d8f 100644
--- a/core/java/android/service/autofill/IAutoFillCallback.aidl
+++ b/core/java/android/service/autofill/IAutoFillAppCallback.aidl
@@ -18,10 +18,11 @@ package android.service.autofill;
import java.util.List;
+import android.view.autofill.Dataset;
+
/**
* @hide
*/
-oneway interface IAutoFillCallback {
- void autofill(in List values);
- void showError(String message);
+oneway interface IAutoFillAppCallback {
+ void autoFill(in Dataset dataset);
}
diff --git a/core/java/android/service/autofill/IAutoFillManagerService.aidl b/core/java/android/service/autofill/IAutoFillManagerService.aidl
index f1251c036469..f8ae57bb8809 100644
--- a/core/java/android/service/autofill/IAutoFillManagerService.aidl
+++ b/core/java/android/service/autofill/IAutoFillManagerService.aidl
@@ -24,6 +24,5 @@ import android.os.Bundle;
* {@hide}
*/
oneway interface IAutoFillManagerService {
-
- void requestAutoFill(IBinder activityToken, int userId, int flags);
+ void requestAutoFill(IBinder activityToken, int userId, in Bundle extras, int flags);
}
diff --git a/core/java/android/service/autofill/IAutoFillServerCallback.aidl b/core/java/android/service/autofill/IAutoFillServerCallback.aidl
new file mode 100644
index 000000000000..9d58c9956a25
--- /dev/null
+++ b/core/java/android/service/autofill/IAutoFillServerCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 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.autofill;
+
+import java.util.List;
+
+import android.view.autofill.AutoFillId;
+import android.view.autofill.FillResponse;
+
+/**
+ * @hide
+ */
+oneway interface IAutoFillServerCallback {
+ void showResponse(in FillResponse response);
+ void showError(String message);
+ void highlightSavedFields(in AutoFillId[] ids);
+}
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index bb122e5eff7d..a1f22bf114e3 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -18,14 +18,15 @@ package android.service.autofill;
import android.app.assist.AssistStructure;
import android.os.Bundle;
-import android.service.autofill.IAutoFillCallback;
+import android.service.autofill.IAutoFillServerCallback;
import com.android.internal.os.IResultReceiver;
/**
* @hide
*/
-interface IAutoFillService {
- oneway void onConnected();
- oneway void onDisconnected();
- IResultReceiver getAssistReceiver();
+oneway interface IAutoFillService {
+ void autoFill(in AssistStructure structure, in IAutoFillServerCallback callback,
+ in Bundle extras, int flags);
+ void onConnected();
+ void onDisconnected();
}
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 4dc7392a963c..627d74c8e349 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -20,9 +20,11 @@ import static android.service.autofill.AutoFillService.DEBUG;
import android.app.Activity;
import android.app.assist.AssistStructure.ViewNode;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import android.view.autofill.AutoFillId;
import com.android.internal.util.Preconditions;
@@ -34,41 +36,53 @@ public final class SaveCallback {
private static final String TAG = "SaveCallback";
- private final IAutoFillCallback mCallback;
+ private final IAutoFillServerCallback mCallback;
+
+ private boolean mReplied = false;
/** @hide */
- SaveCallback(IBinder binder) {
- mCallback = IAutoFillCallback.Stub.asInterface(binder);
+ SaveCallback(IAutoFillServerCallback callback) {
+ mCallback = callback;
}
/**
- * Notifies the {@link Activity} that the save request succeeded.
+ * Notifies the Android System that an
+ * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, SaveCallback)}
+ * was successfully fulfilled by the service.
*
* @param ids ids ({@link ViewNode#getAutoFillId()}) of the fields that were saved.
*
- * @throws RuntimeException if an error occurred while saving the data.
+ * @throws RuntimeException if an error occurred while calling the Android System.
*/
- public void onSuccess(int[] ids) {
+ public void onSuccess(AutoFillId[] ids) {
+ if (DEBUG) Log.d(TAG, "onSuccess(): ids=" + ((ids == null) ? "null" : ids.length));
+
Preconditions.checkArgument(ids != null, "ids cannot be null");
+ checkNotRepliedYet();
Preconditions.checkArgument(ids.length > 0, "ids cannot be empty");
- if (DEBUG) Log.d(TAG, "onSuccess(): ids=" + ids.length);
-
- // TODO(b/33197203): display which ids were saved
+ try {
+ mCallback.highlightSavedFields(ids);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
}
/**
- * Notifies the {@link Activity} that the save request failed.
+ * Notifies the Android System that an
+ * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, SaveCallback)}
+ * could not be fulfilled by the service.
*
- * @param message error message to be displayed.
+ * @param message error message to be displayed to the user.
*
- * @throws RuntimeException if an error occurred while notifying the activity.
+ * @throws RuntimeException if an error occurred while calling the Android System.
*/
public void onFailure(CharSequence message) {
if (DEBUG) Log.d(TAG, "onFailure(): message=" + message);
Preconditions.checkArgument(message != null, "message cannot be null");
+ checkNotRepliedYet();
try {
mCallback.showError(message.toString());
@@ -76,4 +90,10 @@ public final class SaveCallback {
e.rethrowAsRuntimeException();
}
}
+
+ // There can be only one!!
+ private void checkNotRepliedYet() {
+ Preconditions.checkState(!mReplied, "already replied");
+ mReplied = true;
+ }
}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 9ac89960ab95..1e9deebd8dcf 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -85,7 +85,7 @@ public class DynamicLayout extends Layout
this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
spacingmult, spacingadd, includepad,
StaticLayout.BREAK_STRATEGY_SIMPLE, StaticLayout.HYPHENATION_FREQUENCY_NONE,
- ellipsize, ellipsizedWidth);
+ false /* justify */, ellipsize, ellipsizedWidth);
}
/**
@@ -102,7 +102,8 @@ public class DynamicLayout extends Layout
int width, Alignment align, TextDirectionHeuristic textDir,
float spacingmult, float spacingadd,
boolean includepad, int breakStrategy, int hyphenationFrequency,
- TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
+ boolean justify, TextUtils.TruncateAt ellipsize,
+ int ellipsizedWidth) {
super((ellipsize == null)
? display
: (display instanceof Spanned)
@@ -127,6 +128,7 @@ public class DynamicLayout extends Layout
mIncludePad = includepad;
mBreakStrategy = breakStrategy;
+ mJustify = justify;
mHyphenationFrequency = hyphenationFrequency;
/*
@@ -300,7 +302,8 @@ public class DynamicLayout extends Layout
.setEllipsizedWidth(mEllipsizedWidth)
.setEllipsize(mEllipsizeAt)
.setBreakStrategy(mBreakStrategy)
- .setHyphenationFrequency(mHyphenationFrequency);
+ .setHyphenationFrequency(mHyphenationFrequency)
+ .setJustify(mJustify);
reflowed.generate(b, false, true);
int n = reflowed.getLineCount();
// If the new layout has a blank line at the end, but it is not
@@ -808,6 +811,7 @@ public class DynamicLayout extends Layout
private TextUtils.TruncateAt mEllipsizeAt;
private int mBreakStrategy;
private int mHyphenationFrequency;
+ private boolean mJustify;
private PackedIntVector mInts;
private PackedObjectVector<Directions> mObjects;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 3cb81b0a250f..2fc12d30ffd8 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -218,6 +218,11 @@ public abstract class Layout {
mTextDir = textDir;
}
+ /** @hide */
+ protected void setJustify(boolean justify) {
+ mJustify = justify;
+ }
+
/**
* Replace constructor properties of this Layout with new ones. Be careful.
*/
@@ -266,6 +271,99 @@ public abstract class Layout {
drawText(canvas, firstLine, lastLine);
}
+ private boolean isJustificationRequired(int lineNum) {
+ if (!mJustify) return false;
+ final int lineEnd = getLineEnd(lineNum);
+ return lineEnd < mText.length() && mText.charAt(lineEnd - 1) != '\n';
+ }
+
+ private float getJustifyWidth(int lineNum) {
+ Alignment paraAlign = mAlignment;
+ TabStops tabStops = null;
+ boolean tabStopsIsInitialized = false;
+
+ int left = 0;
+ int right = mWidth;
+
+ final int dir = getParagraphDirection(lineNum);
+
+ ParagraphStyle[] spans = NO_PARA_SPANS;
+ if (mSpannedText) {
+ Spanned sp = (Spanned) mText;
+ final int start = getLineStart(lineNum);
+
+ final boolean isFirstParaLine = (start == 0 || mText.charAt(start - 1) == '\n');
+
+ if (isFirstParaLine) {
+ final int spanEnd = sp.nextSpanTransition(start, mText.length(),
+ ParagraphStyle.class);
+ spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
+
+ for (int n = spans.length - 1; n >= 0; n--) {
+ if (spans[n] instanceof AlignmentSpan) {
+ paraAlign = ((AlignmentSpan) spans[n]).getAlignment();
+ break;
+ }
+ }
+ }
+
+ final int length = spans.length;
+ boolean useFirstLineMargin = isFirstParaLine;
+ for (int n = 0; n < length; n++) {
+ if (spans[n] instanceof LeadingMarginSpan2) {
+ int count = ((LeadingMarginSpan2) spans[n]).getLeadingMarginLineCount();
+ int startLine = getLineForOffset(sp.getSpanStart(spans[n]));
+ if (lineNum < startLine + count) {
+ useFirstLineMargin = true;
+ break;
+ }
+ }
+ }
+ for (int n = 0; n < length; n++) {
+ if (spans[n] instanceof LeadingMarginSpan) {
+ LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
+ if (dir == DIR_RIGHT_TO_LEFT) {
+ right -= margin.getLeadingMargin(useFirstLineMargin);
+ } else {
+ left += margin.getLeadingMargin(useFirstLineMargin);
+ }
+ }
+ }
+ }
+
+ if (getLineContainsTab(lineNum)) {
+ tabStops = new TabStops(TAB_INCREMENT, spans);
+ }
+
+ final Alignment align;
+ if (paraAlign == Alignment.ALIGN_LEFT) {
+ align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
+ } else if (paraAlign == Alignment.ALIGN_RIGHT) {
+ align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL;
+ } else {
+ align = paraAlign;
+ }
+
+ final int indentWidth;
+ if (align == Alignment.ALIGN_NORMAL) {
+ if (dir == DIR_LEFT_TO_RIGHT) {
+ indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+ } else {
+ indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+ }
+ } else if (align == Alignment.ALIGN_OPPOSITE) {
+ if (dir == DIR_LEFT_TO_RIGHT) {
+ indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+ } else {
+ indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+ }
+ } else { // Alignment.ALIGN_CENTER
+ indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_CENTER);
+ }
+
+ return right - left - indentWidth;
+ }
+
/**
* @hide
*/
@@ -274,7 +372,7 @@ public abstract class Layout {
int previousLineEnd = getLineStart(firstLine);
ParagraphStyle[] spans = NO_PARA_SPANS;
int spanEnd = 0;
- TextPaint paint = mPaint;
+ final TextPaint paint = mPaint;
CharSequence buf = mText;
Alignment paraAlign = mAlignment;
@@ -288,6 +386,7 @@ public abstract class Layout {
for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) {
int start = previousLineEnd;
previousLineEnd = getLineStart(lineNum + 1);
+ final boolean justify = isJustificationRequired(lineNum);
int end = getLineVisibleEnd(lineNum, start, previousLineEnd);
int ltop = previousLineBottom;
@@ -386,34 +485,42 @@ public abstract class Layout {
}
int x;
+ final int indentWidth;
if (align == Alignment.ALIGN_NORMAL) {
if (dir == DIR_LEFT_TO_RIGHT) {
- x = left + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+ indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+ x = left + indentWidth;
} else {
- x = right + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+ indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+ x = right - indentWidth;
}
} else {
int max = (int)getLineExtent(lineNum, tabStops, false);
if (align == Alignment.ALIGN_OPPOSITE) {
if (dir == DIR_LEFT_TO_RIGHT) {
- x = right - max + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+ indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
+ x = right - max - indentWidth;
} else {
- x = left - max + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+ indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
+ x = left - max + indentWidth;
}
} else { // Alignment.ALIGN_CENTER
+ indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_CENTER);
max = max & ~1;
- x = ((right + left - max) >> 1) +
- getIndentAdjust(lineNum, Alignment.ALIGN_CENTER);
+ x = ((right + left - max) >> 1) + indentWidth;
}
}
paint.setHyphenEdit(getHyphen(lineNum));
Directions directions = getLineDirections(lineNum);
- if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab) {
+ if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab && !justify) {
// XXX: assumes there's nothing additional to be done
canvas.drawText(buf, start, end, x, lbaseline, paint);
} else {
tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops);
+ if (justify) {
+ tl.justify(right - left - indentWidth);
+ }
tl.draw(canvas, x, ltop, lbaseline, lbottom);
}
paint.setHyphenEdit(0);
@@ -1094,6 +1201,9 @@ public abstract class Layout {
TextLine tl = TextLine.obtain();
mPaint.setHyphenEdit(getHyphen(line));
tl.set(mPaint, mText, start, end, dir, directions, hasTabs, tabStops);
+ if (isJustificationRequired(line)) {
+ tl.justify(getJustifyWidth(line));
+ }
float width = tl.metrics(null);
mPaint.setHyphenEdit(0);
TextLine.recycle(tl);
@@ -1118,6 +1228,9 @@ public abstract class Layout {
TextLine tl = TextLine.obtain();
mPaint.setHyphenEdit(getHyphen(line));
tl.set(mPaint, mText, start, end, dir, directions, hasTabs, tabStops);
+ if (isJustificationRequired(line)) {
+ tl.justify(getJustifyWidth(line));
+ }
float width = tl.metrics(null);
mPaint.setHyphenEdit(0);
TextLine.recycle(tl);
@@ -1303,10 +1416,7 @@ public abstract class Layout {
return end - 1;
}
- // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace()
- if (!(ch == ' ' || ch == '\t' || ch == 0x1680 ||
- (0x2000 <= ch && ch <= 0x200A && ch != 0x2007) ||
- ch == 0x205F || ch == 0x3000)) {
+ if (!TextLine.isLineEndSpace(ch)) {
break;
}
@@ -2086,6 +2196,7 @@ public abstract class Layout {
private boolean mSpannedText;
private TextDirectionHeuristic mTextDir;
private SpanSet<LineBackgroundSpan> mLineBackgroundSpans;
+ private boolean mJustify;
public static final int DIR_LEFT_TO_RIGHT = 1;
public static final int DIR_RIGHT_TO_LEFT = -1;
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 081be3afd6d3..cb5b073318d8 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -94,6 +94,7 @@ public class StaticLayout extends Layout {
b.mMaxLines = Integer.MAX_VALUE;
b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
+ b.mJustify = false;
b.mMeasuredText = MeasuredText.obtain();
return b;
@@ -320,6 +321,17 @@ public class StaticLayout extends Layout {
}
/**
+ * Enables or disables paragraph justification. The default value is disabled (false).
+ *
+ * @param justify true for enabling and false for disabling paragraph justification.
+ * @return this builder, useful for chaining.
+ */
+ public Builder setJustify(boolean justify) {
+ mJustify = justify;
+ return this;
+ }
+
+ /**
* Measurement and break iteration is done in native code. The protocol for using
* the native code is as follows.
*
@@ -404,6 +416,7 @@ public class StaticLayout extends Layout {
int mHyphenationFrequency;
int[] mLeftIndents;
int[] mRightIndents;
+ boolean mJustify;
Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
@@ -557,6 +570,7 @@ public class StaticLayout extends Layout {
mLeftIndents = b.mLeftIndents;
mRightIndents = b.mRightIndents;
+ setJustify(b.mJustify);
generate(b, b.mIncludePad, b.mIncludePad);
}
@@ -676,7 +690,8 @@ public class StaticLayout extends Layout {
nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
firstWidth, firstWidthLineCount, restWidth,
- variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency);
+ variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency,
+ b.mJustify);
if (mLeftIndents != null || mRightIndents != null) {
// TODO(raph) performance: it would be better to do this once per layout rather
// than once per paragraph, but that would require a change to the native
@@ -1284,7 +1299,8 @@ public class StaticLayout extends Layout {
// Set up paragraph text and settings; done as one big method to minimize jni crossings
private static native void nSetupParagraph(long nativePtr, char[] text, int length,
float firstWidth, int firstWidthLineCount, float restWidth,
- int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency);
+ int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency,
+ boolean isJustified);
private static native float nAddStyleRun(long nativePtr, long nativePaint,
long nativeTypeface, int start, int end, boolean isRtl);
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index c4118605b44c..fcff9a2e5fca 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -54,6 +54,10 @@ class TextLine {
private char[] mChars;
private boolean mCharsValid;
private Spanned mSpanned;
+
+ // Additional width of whitespace for justification. This value is per whitespace, thus
+ // the line width will increase by mAddedWidth x (number of stretchable whitespaces).
+ private float mAddedWidth;
private final TextPaint mWorkPaint = new TextPaint();
private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet =
new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class);
@@ -177,6 +181,25 @@ class TextLine {
}
}
mTabs = tabStops;
+ mAddedWidth = 0;
+ }
+
+ /**
+ * Justify the line to the given width.
+ */
+ void justify(float justifyWidth) {
+ int end = mLen;
+ while (end > 0 && isLineEndSpace(mText.charAt(mStart + end - 1))) {
+ end--;
+ }
+ final int spaces = countStretchableSpaces(0, end);
+ if (spaces == 0) {
+ // There are no stretchable spaces, so we can't help the justification by adding any
+ // width.
+ return;
+ }
+ final float width = Math.abs(measure(end, false, null));
+ mAddedWidth = (justifyWidth - width) / spaces;
}
/**
@@ -595,6 +618,7 @@ class TextLine {
TextPaint wp = mWorkPaint;
wp.set(mPaint);
+ wp.setWordSpacing(mAddedWidth);
int spanStart = runStart;
int spanLimit;
@@ -695,6 +719,7 @@ class TextLine {
Canvas c, float x, int top, int y, int bottom,
FontMetricsInt fmi, boolean needWidth, int offset) {
+ wp.setWordSpacing(mAddedWidth);
// Get metrics first (even for empty strings or "0" width runs)
if (fmi != null) {
expandMetricsFromPaint(fmi, wp);
@@ -981,5 +1006,34 @@ class TextLine {
return TabStops.nextDefaultStop(h, TAB_INCREMENT);
}
+ private boolean isStretchableWhitespace(int ch) {
+ // TODO: Support other stretchable whitespace. (Bug: 34013491)
+ return ch == 0x0020 || ch == 0x00A0;
+ }
+
+ private int nextStretchableSpace(int start, int end) {
+ for (int i = start; i < end; i++) {
+ final char c = mCharsValid ? mChars[i] : mText.charAt(i + mStart);
+ if (isStretchableWhitespace(c)) return i;
+ }
+ return end;
+ }
+
+ /* Return the number of spaces in the text line, for the purpose of justification */
+ private int countStretchableSpaces(int start, int end) {
+ int count = 0;
+ for (int i = start; i < end; i = nextStretchableSpace(i + 1, end)) {
+ count++;
+ }
+ return count;
+ }
+
+ // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace()
+ public static boolean isLineEndSpace(char ch) {
+ return ch == ' ' || ch == '\t' || ch == 0x1680
+ || (0x2000 <= ch && ch <= 0x200A && ch != 0x2007)
+ || ch == 0x205F || ch == 0x3000;
+ }
+
private static final int TAB_INCREMENT = 20;
}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 58bc9a79d577..bb952ab93077 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -29,6 +29,8 @@ import android.os.Parcelable;
import android.os.SystemProperties;
import android.provider.Settings;
import android.text.style.AbsoluteSizeSpan;
+import android.text.style.AccessibilityClickableSpan;
+import android.text.style.AccessibilityURLSpan;
import android.text.style.AlignmentSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.BulletSpan;
@@ -621,7 +623,11 @@ public class TextUtils {
/** @hide */
public static final int TTS_SPAN = 24;
/** @hide */
- public static final int LAST_SPAN = TTS_SPAN;
+ public static final int ACCESSIBILITY_CLICKABLE_SPAN = 25;
+ /** @hide */
+ public static final int ACCESSIBILITY_URL_SPAN = 26;
+ /** @hide */
+ public static final int LAST_SPAN = ACCESSIBILITY_URL_SPAN;
/**
* Flatten a CharSequence and whatever styles can be copied across processes
@@ -803,6 +809,14 @@ public class TextUtils {
readSpan(p, sp, new TtsSpan(p));
break;
+ case ACCESSIBILITY_CLICKABLE_SPAN:
+ readSpan(p, sp, new AccessibilityClickableSpan(p));
+ break;
+
+ case ACCESSIBILITY_URL_SPAN:
+ readSpan(p, sp, new AccessibilityURLSpan(p));
+ break;
+
default:
throw new RuntimeException("bogus span encoding " + kind);
}
diff --git a/core/java/android/text/style/AccessibilityClickableSpan.java b/core/java/android/text/style/AccessibilityClickableSpan.java
new file mode 100644
index 000000000000..9c305ff0f6ae
--- /dev/null
+++ b/core/java/android/text/style/AccessibilityClickableSpan.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 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.text.style;
+
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.ParcelableSpan;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.internal.R;
+
+import java.lang.ref.WeakReference;
+
+
+/**
+ * {@link ClickableSpan} cannot be parceled, but accessibility services need to be able to cause
+ * their callback handlers to be called. This class serves as a parcelable placeholder for the
+ * real spans.
+ *
+ * This span is also passed back to an app's process when an accessibility service tries to click
+ * it. It contains enough information to track down the original clickable span so it can be
+ * called.
+ *
+ * @hide
+ */
+public class AccessibilityClickableSpan extends ClickableSpan
+ implements ParcelableSpan {
+ // The id of the span this one replaces
+ private final int mOriginalClickableSpanId;
+
+ // Only retain a weak reference to the node to avoid referencing cycles that could create memory
+ // leaks.
+ private WeakReference<AccessibilityNodeInfo> mAccessibilityNodeInfoRef;
+
+
+ /**
+ * @param originalClickableSpanId The id of the span this one replaces
+ */
+ public AccessibilityClickableSpan(int originalClickableSpanId) {
+ mOriginalClickableSpanId = originalClickableSpanId;
+ }
+
+ public AccessibilityClickableSpan(Parcel p) {
+ mOriginalClickableSpanId = p.readInt();
+ }
+
+ @Override
+ public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ @Override
+ public int getSpanTypeIdInternal() {
+ return TextUtils.ACCESSIBILITY_CLICKABLE_SPAN;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ @Override
+ public void writeToParcelInternal(Parcel dest, int flags) {
+ dest.writeInt(mOriginalClickableSpanId);
+ }
+
+ /**
+ * Find the ClickableSpan that matches the one used to create this object.
+ *
+ * @param text The text that contains the original ClickableSpan.
+ * @return The ClickableSpan that matches this object, or {@code null} if no such object
+ * can be found.
+ */
+ public ClickableSpan findClickableSpan(CharSequence text) {
+ if (!(text instanceof Spanned)) {
+ return null;
+ }
+ Spanned sp = (Spanned) text;
+ ClickableSpan[] os = sp.getSpans(0, text.length(), ClickableSpan.class);
+ for (int i = 0; i < os.length; i++) {
+ if (os[i].getId() == mOriginalClickableSpanId) {
+ return os[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Set the accessibilityNodeInfo that this placeholder belongs to. This node is not
+ * included in the parceling logic, and must be set to allow the onClick handler to function.
+ *
+ * @param accessibilityNodeInfo The info this span is part of
+ */
+ public void setAccessibilityNodeInfo(AccessibilityNodeInfo accessibilityNodeInfo) {
+ mAccessibilityNodeInfoRef = new WeakReference<>(accessibilityNodeInfo);
+ }
+
+ /**
+ * Perform the click from an accessibility service. Will not work unless
+ * setAccessibilityNodeInfo is called with a properly initialized node.
+ *
+ * @param unused This argument is required by the superclass but is unused. The real view will
+ * be determined by the AccessibilityNodeInfo.
+ */
+ @Override
+ public void onClick(View unused) {
+ if (mAccessibilityNodeInfoRef == null) {
+ return;
+ }
+ AccessibilityNodeInfo info = mAccessibilityNodeInfoRef.get();
+ if (info == null) {
+ return;
+ }
+ Bundle arguments = new Bundle();
+ arguments.putParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN, this);
+
+ info.performAction(R.id.accessibilityActionClickOnClickableSpan, arguments);
+ }
+
+ public static final Parcelable.Creator<AccessibilityClickableSpan> CREATOR =
+ new Parcelable.Creator<AccessibilityClickableSpan>() {
+ @Override
+ public AccessibilityClickableSpan createFromParcel(Parcel parcel) {
+ return new AccessibilityClickableSpan(parcel);
+ }
+
+ @Override
+ public AccessibilityClickableSpan[] newArray(int size) {
+ return new AccessibilityClickableSpan[size];
+ }
+ };
+}
diff --git a/core/java/android/text/style/AccessibilityURLSpan.java b/core/java/android/text/style/AccessibilityURLSpan.java
new file mode 100644
index 000000000000..014700945c57
--- /dev/null
+++ b/core/java/android/text/style/AccessibilityURLSpan.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 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.text.style;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+/**
+ * URLSpan's onClick method does not work from an accessibility service. This version of it does.
+ * It is used to replace URLSpans in {@link AccessibilityNodeInfo#setText(CharSequence)}
+ * @hide
+ */
+public class AccessibilityURLSpan extends URLSpan implements Parcelable {
+ final AccessibilityClickableSpan mAccessibilityClickableSpan;
+
+ /**
+ * @param spanToReplace The original span
+ */
+ public AccessibilityURLSpan(URLSpan spanToReplace) {
+ super(spanToReplace.getURL());
+ mAccessibilityClickableSpan =
+ new AccessibilityClickableSpan(spanToReplace.getId());
+ }
+
+ public AccessibilityURLSpan(Parcel p) {
+ super(p);
+ mAccessibilityClickableSpan = new AccessibilityClickableSpan(p);
+ }
+
+ @Override
+ public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ @Override
+ public int getSpanTypeIdInternal() {
+ return TextUtils.ACCESSIBILITY_URL_SPAN;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ @Override
+ public void writeToParcelInternal(Parcel dest, int flags) {
+ super.writeToParcelInternal(dest, flags);
+ mAccessibilityClickableSpan.writeToParcel(dest, flags);
+ }
+
+ @Override
+ public void onClick(View unused) {
+ mAccessibilityClickableSpan.onClick(unused);
+ }
+
+ /**
+ * Delegated to AccessibilityClickableSpan
+ * @param accessibilityNodeInfo
+ */
+ public void setAccessibilityNodeInfo(AccessibilityNodeInfo accessibilityNodeInfo) {
+ mAccessibilityClickableSpan.setAccessibilityNodeInfo(accessibilityNodeInfo);
+ }
+}
diff --git a/core/java/android/text/style/ClickableSpan.java b/core/java/android/text/style/ClickableSpan.java
index 989ef54f3f8a..a183427a44d0 100644
--- a/core/java/android/text/style/ClickableSpan.java
+++ b/core/java/android/text/style/ClickableSpan.java
@@ -26,12 +26,15 @@ import android.view.View;
* be called.
*/
public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance {
+ private static int sIdCounter = 0;
+
+ private int mId = sIdCounter++;
/**
* Performs the click action associated with this span.
*/
public abstract void onClick(View widget);
-
+
/**
* Makes the text underlined and in the link color.
*/
@@ -40,4 +43,14 @@ public abstract class ClickableSpan extends CharacterStyle implements UpdateAppe
ds.setColor(ds.linkColor);
ds.setUnderlineText(true);
}
+
+ /**
+ * Get the unique ID for this span.
+ *
+ * @return The unique ID.
+ * @hide
+ */
+ public int getId() {
+ return mId;
+ }
}
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 99f6c2a9c58d..79d16fbfaee8 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -59,7 +59,7 @@ public class EventLog {
private Exception mLastWtf;
// Layout of event log entry received from Android logger.
- // see system/core/include/log/logger.h
+ // see system/core/include/log/log.h
private static final int LENGTH_OFFSET = 0;
private static final int HEADER_SIZE_OFFSET = 2;
private static final int PROCESS_OFFSET = 4;
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index c7b1d03b120b..13ee48ef23ce 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN;
+
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -24,8 +26,12 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
+import android.text.TextUtils;
+import android.text.style.AccessibilityClickableSpan;
+import android.text.style.ClickableSpan;
import android.util.LongSparseArray;
import android.view.View.AttachInfo;
import android.view.accessibility.AccessibilityInteractionClient;
@@ -33,6 +39,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import com.android.internal.R;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Predicate;
@@ -655,17 +662,23 @@ final class AccessibilityInteractionController {
target = mViewRootImpl.mView;
}
if (target != null && isShown(target)) {
- AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
- if (provider != null) {
- if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- succeeded = provider.performAction(virtualDescendantId, action,
- arguments);
- } else {
- succeeded = provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
- action, arguments);
+ if (action == R.id.accessibilityActionClickOnClickableSpan) {
+ // Handle this hidden action separately
+ succeeded = handleClickableSpanActionUiThread(
+ target, virtualDescendantId, arguments);
+ } else {
+ AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
+ if (provider != null) {
+ if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ succeeded = provider.performAction(virtualDescendantId, action,
+ arguments);
+ } else {
+ succeeded = provider.performAction(
+ AccessibilityNodeProvider.HOST_VIEW_ID, action, arguments);
+ }
+ } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ succeeded = target.performAccessibilityAction(action, arguments);
}
- } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- succeeded = target.performAccessibilityAction(action, arguments);
}
}
} finally {
@@ -816,6 +829,37 @@ final class AccessibilityInteractionController {
return (appScale != 1.0f || (spec != null && !spec.isNop()));
}
+ private boolean handleClickableSpanActionUiThread(
+ View view, int virtualDescendantId, Bundle arguments) {
+ Parcelable span = arguments.getParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN);
+ if (!(span instanceof AccessibilityClickableSpan)) {
+ return false;
+ }
+
+ // Find the original ClickableSpan if it's still on the screen
+ AccessibilityNodeInfo infoWithSpan = null;
+ AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
+ if (provider != null) {
+ int idForNode = (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
+ ? AccessibilityNodeProvider.HOST_VIEW_ID : virtualDescendantId;
+ infoWithSpan = provider.createAccessibilityNodeInfo(idForNode);
+ } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ infoWithSpan = view.createAccessibilityNodeInfo();
+ }
+ if (infoWithSpan == null) {
+ return false;
+ }
+
+ // Click on the corresponding span
+ ClickableSpan clickableSpan = ((AccessibilityClickableSpan) span).findClickableSpan(
+ infoWithSpan.getOriginalText());
+ if (clickableSpan != null) {
+ clickableSpan.onClick(view);
+ return true;
+ }
+ return false;
+ }
+
/**
* This class encapsulates a prefetching strategy for the accessibility APIs for
* querying window content. It is responsible to prefetch a batch of
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 3beb00fc8d9e..b37ea8ebeaa0 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -282,6 +282,15 @@ public final class Display {
*/
public static final int STATE_DOZE_SUSPEND = 4;
+ /**
+ * Display state: The display is on and optimized for VR mode.
+ *
+ * @see #getState
+ * @see android.os.PowerManager#isInteractive
+ * @hide
+ */
+ public static final int STATE_VR = 5;
+
/* The color mode constants defined below must be kept in sync with the ones in
* system/graphics.h */
@@ -866,7 +875,8 @@ public final class Display {
* Gets the state of the display, such as whether it is on or off.
*
* @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON},
- * {@link #STATE_DOZE}, {@link #STATE_DOZE_SUSPEND}, or {@link #STATE_UNKNOWN}.
+ * {@link #STATE_DOZE}, {@link #STATE_DOZE_SUSPEND}, or
+ * {@link #STATE_UNKNOWN}.
*/
public int getState() {
synchronized (this) {
@@ -982,6 +992,8 @@ public final class Display {
return "DOZE";
case STATE_DOZE_SUSPEND:
return "DOZE_SUSPEND";
+ case STATE_VR:
+ return "VR";
default:
return Integer.toString(state);
}
diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java
index a3e8312c0a64..2fd2e966dc38 100644
--- a/core/java/android/view/SurfaceHolder.java
+++ b/core/java/android/view/SurfaceHolder.java
@@ -116,9 +116,34 @@ public interface SurfaceHolder {
* size before it has been correctly drawn that way). This will
* typically be preceeded by a call to {@link #surfaceChanged}.
*
+ * As of O, {@link #surfaceRedrawNeededAsync} may be implemented
+ * to provide a non-blocking implementation. If {@link #surfaceRedrawNeededAsync}
+ * is not implemented, then this will be called instead.
+ *
* @param holder The SurfaceHolder whose surface has changed.
*/
- public void surfaceRedrawNeeded(SurfaceHolder holder);
+ void surfaceRedrawNeeded(SurfaceHolder holder);
+
+ /**
+ * An alternative to surfaceRedrawNeeded where it is not required to block
+ * until the redraw is complete. You should initiate the redraw, and return,
+ * later invoking drawingFinished when your redraw is complete.
+ *
+ * This can be useful to avoid blocking your main application thread on rendering.
+ *
+ * As of O, if this is implemented {@link #surfaceRedrawNeeded} will not be called.
+ * However it is still recommended to implement {@link #surfaceRedrawNeeded} for
+ * compatibility with older versions of the platform.
+ *
+ * @param holder The SurfaceHolder which needs redrawing.
+ * @param drawingFinished A runnable to signal completion. This may be invoked
+ * from any thread.
+ *
+ */
+ default void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable drawingFinished) {
+ surfaceRedrawNeeded(holder);
+ drawingFinished.run();
+ }
}
/**
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d46910c2139c..018be866cd8b 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -33,6 +33,7 @@ import android.util.AttributeSet;
import android.util.Log;
import com.android.internal.view.BaseIWindow;
+import com.android.internal.view.SurfaceCallbackHelper;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -639,21 +640,13 @@ public class SurfaceView extends View {
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
- for (SurfaceHolder.Callback c : callbacks) {
- if (c instanceof SurfaceHolder.Callback2) {
- ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
- mSurfaceHolder);
- }
- }
+ SurfaceCallbackHelper sch =
+ new SurfaceCallbackHelper(mSession, mWindow);
+ sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
}
}
} finally {
mIsCreating = false;
- if (redrawNeeded) {
- if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
- + "finishedDrawing");
- mSession.finishDrawing(mWindow);
- }
mSession.performDeferredDestroy(mWindow);
}
} catch (RemoteException ex) {
@@ -876,7 +869,6 @@ public class SurfaceView extends View {
}
private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
-
private static final String LOG_TAG = "SurfaceHolder";
@Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0b1dfa2a321f..aedd0df4e4f0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -39,6 +39,7 @@ import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.TestApi;
import android.annotation.UiThread;
+import android.app.Application.OnProvideAssistDataListener;
import android.content.ClipData;
import android.content.Context;
import android.content.ContextWrapper;
@@ -103,6 +104,9 @@ import android.view.accessibility.AccessibilityNodeProvider;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
+import android.view.autofill.AutoFillType;
+import android.view.autofill.AutoFillValue;
+import android.view.autofill.VirtualViewDelegate;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
@@ -4025,18 +4029,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
- * <p>When setting a {@link android.app.assist.AssistStructure}, its nodes should not contain
- * PII (Personally Identifiable Information).
+ * Set when a request was made to decide if views in an {@link android.app.Activity} can be
+ * auto-filled by an {@link android.service.autofill.AutoFillService}.
+ *
+ * <p>Since this request is made without a explicit user consent, the resulting
+ * {@link android.app.assist.AssistStructure} should not contain any PII
+ * (Personally Identifiable Information).
+ *
+ * <p>Examples:
+ * <ul>
+ * <li>{@link android.widget.TextView} texts should only be included when they were set by
+ * static resources.
+ * <li>{@link android.webkit.WebView} virtual children should be restricted to a subset of
+ * input fields and tags (like {@code id}).
+ * </ul>
*/
- // TODO(b/33197203) (b/33269702): improve documentation: mention all cases, show examples, etc.
- public static final int ASSIST_FLAG_SANITIZED_TEXT = 0x1;
+ // TODO(b/33197203) (b/34078930): improve documentation: mention all cases, show examples, etc.
+ // In particular, be more specific about webview restrictions
+ public static final int AUTO_FILL_FLAG_TYPE_FILL = 0x1;
/**
- * <p>When setting a {@link android.app.assist.AssistStructure}, its nodes should contain all
- * type of data, even sensitive PII (Personally Identifiable Information) like passwords or
- * credit card numbers.
+ * Set when the user explicitly asked a {@link android.service.autofill.AutoFillService} to save
+ * the value of the {@link View}s in an {@link android.app.Activity}.
+ *
+ * <p>The resulting {@link android.app.assist.AssistStructure} can contain any kind of PII
+ * (Personally Identifiable Information). For example, the text of password fields should be
+ * included since that's what's typically saved.
*/
- public static final int ASSIST_FLAG_NON_SANITIZED_TEXT = 0x2;
+ public static final int AUTO_FILL_FLAG_TYPE_SAVE = 0x2;
/**
* Set to true when drawing cache is enabled and cannot be created.
@@ -6873,35 +6893,32 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
* @param structure Fill in with structured view data. The default implementation
* fills in all data that can be inferred from the view itself.
- *
- * @deprecated As of API O sub-classes should override
- * {@link #onProvideStructure(ViewStructure, int)} instead.
*/
- // TODO(b/33197203): set proper API above
- @Deprecated
public void onProvideStructure(ViewStructure structure) {
- onProvideStructure(structure, 0);
+ onProvideStructureForAssistOrAutoFill(structure, 0);
}
/**
- * Called when assist structure is being retrieved from a view as part of
- * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} or as part
- * of an auto-fill request.
- *
- * <p>The default implementation fills in all data that can be inferred from the view itself.
+ * Called when assist structure is being retrieved from a view as part of an auto-fill request.
*
* <p>The structure must be filled according to the request type, which is set in the
* {@code flags} parameter - see the documentation on each flag for more details.
*
- * @param structure Fill in with structured view data. The default implementation
+ * @param structure Fill in with structured view data. The default implementation
* fills in all data that can be inferred from the view itself.
- * @param flags optional flags (see {@link #ASSIST_FLAG_SANITIZED_TEXT} and
- * {@link #ASSIST_FLAG_NON_SANITIZED_TEXT} for more info).
+ * @param flags optional flags (see {@link #AUTO_FILL_FLAG_TYPE_FILL} and
+ * {@link #AUTO_FILL_FLAG_TYPE_SAVE} for more info).
*/
- public void onProvideStructure(ViewStructure structure, int flags) {
+ public void onProvideAutoFillStructure(ViewStructure structure, int flags) {
+ onProvideStructureForAssistOrAutoFill(structure, flags);
+ }
+
+ private void onProvideStructureForAssistOrAutoFill(ViewStructure structure, int flags) {
+ // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well,
+ // this method should take a boolean with the type of request.
boolean forAutoFill = (flags
- & (View.ASSIST_FLAG_SANITIZED_TEXT
- | View.ASSIST_FLAG_NON_SANITIZED_TEXT)) != 0;
+ & (View.AUTO_FILL_FLAG_TYPE_FILL
+ | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0;
final int id = mID;
if (id > 0 && (id&0xff000000) != 0 && (id&0x00ff0000) != 0
&& (id&0x0000ffff) != 0) {
@@ -6923,6 +6940,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// The auto-fill id needs to be unique, but its value doesn't matter, so it's better to
// reuse the accessibility id to save space.
structure.setAutoFillId(getAccessibilityViewId());
+
+ structure.setAutoFillType(getAutoFillType());
}
structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop);
@@ -6973,40 +6992,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* uses {@link #getAccessibilityNodeProvider()} to try to generate this from the
* view's virtual accessibility nodes, if any. You can override this for a more
* optimal implementation providing this data.
- *
- * @deprecated As of API O, sub-classes should override
- * {@link #onProvideVirtualStructure(ViewStructure, int)} instead.
*/
- // TODO(b/33197203): set proper API above
- @Deprecated
public void onProvideVirtualStructure(ViewStructure structure) {
- onProvideVirtualStructure(structure, 0);
+ onProvideVirtualStructureForAssistOrAutoFill(structure, 0);
}
/**
- * Called when assist structure is being retrieved from a view as part of
- * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} or as part
- * of an auto-fill request to generate additional virtual structure under this view.
+ * Called when assist structure is being retrieved from a view as part of an auto-fill request
+ * to generate additional virtual structure under this view.
*
* <p>The defaullt implementation uses {@link #getAccessibilityNodeProvider()} to try to
- * generate this from the view's virtual accessibility nodes, if any. You can override this
+ * generate this from the view's virtual accessibility nodes, if any. You can override this
* for a more optimal implementation providing this data.
*
* <p>The structure must be filled according to the request type, which is set in the
* {@code flags} parameter - see the documentation on each flag for more details.
*
* @param structure Fill in with structured view data.
- * @param flags optional flags (see {@link #ASSIST_FLAG_SANITIZED_TEXT} and
- * {@link #ASSIST_FLAG_NON_SANITIZED_TEXT} for more info).
+ * @param flags optional flags (see {@link #AUTO_FILL_FLAG_TYPE_FILL} and
+ * {@link #AUTO_FILL_FLAG_TYPE_SAVE} for more info).
*/
- public void onProvideVirtualStructure(ViewStructure structure, int flags) {
- boolean sanitize = (flags & View.ASSIST_FLAG_SANITIZED_TEXT) != 0;
-
- if (sanitize) {
- // TODO(b/33197203): change populateVirtualStructure so it sanitizes data in this case.
- return;
- }
+ public void onProvideAutoFillVirtualStructure(ViewStructure structure, int flags) {
+ onProvideVirtualStructureForAssistOrAutoFill(structure, flags);
+ }
+ private void onProvideVirtualStructureForAssistOrAutoFill(ViewStructure structure, int flags) {
+ // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well,
+ // this method should take a boolean with the type of request.
AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
if (provider != null) {
AccessibilityNodeInfo info = createAccessibilityNodeInfo();
@@ -7017,8 +7029,66 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
+ /**
+ * Gets the {@link VirtualViewDelegate} responsible for auto-filling the virtual children of
+ * this view.
+ *
+ * <p>By default returns {@code null} but should be overridden when view provides a virtual
+ * hierachy on {@link OnProvideAssistDataListener} that takes flags used by the AutoFill
+ * Framework (such as {@link #AUTO_FILL_FLAG_TYPE_FILL} and
+ * {@link #AUTO_FILL_FLAG_TYPE_SAVE}).
+ */
+ @Nullable
+ public VirtualViewDelegate getAutoFillVirtualViewDelegate(
+ @SuppressWarnings("unused") VirtualViewDelegate.Callback callback) {
+ return null;
+ }
+
+ /**
+ * Automatically fills the content of this view with the {@code value}.
+ *
+ * <p>By default does nothing, but views should override it (and {@link #getAutoFillType()} to
+ * support the AutoFill Framework.
+ *
+ * <p>Typically, it is implemented by:
+ *
+ * <ol>
+ * <li>Call the proper getter method on {@link AutoFillValue} to fetch the actual value.
+ * <li>Pass the actual value to the equivalent setter in the view.
+ * <ol>
+ *
+ * <p>For example, a text-field view would call:
+ *
+ * <pre class="prettyprint">
+ * CharSequence text = value.getTextValue();
+ * if (text != null) {
+ * setText(text);
+ * }
+ * </pre>
+ */
+ public void autoFill(@SuppressWarnings("unused") AutoFillValue value) {
+ }
+
+ /**
+ * Describes the auto-fill type that should be used on callas to
+ * {@link #autoFill(AutoFillValue)} and
+ * {@link VirtualViewDelegate#autoFill(int, AutoFillValue)}.
+ *
+ * <p>By default returns {@code null}, but views should override it (and
+ * {@link #autoFill(AutoFillValue)} to support the AutoFill Framework.
+ */
+ @Nullable
+ public AutoFillType getAutoFillType() {
+ return null;
+ }
+
private void populateVirtualStructure(ViewStructure structure,
AccessibilityNodeProvider provider, AccessibilityNodeInfo info, int flags) {
+ // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well,
+ // this method should take a boolean with the type of request.
+
+ final boolean sanitized = (flags & View.AUTO_FILL_FLAG_TYPE_FILL) != 0;
+
structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
null, null, null);
Rect rect = structure.getTempRect();
@@ -7056,7 +7126,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
CharSequence cname = info.getClassName();
structure.setClassName(cname != null ? cname.toString() : null);
structure.setContentDescription(info.getContentDescription());
- if (info.getText() != null || info.getError() != null) {
+ if (!sanitized && (info.getText() != null || info.getError() != null)) {
+ // TODO(b/33197203) (b/33269702): when sanitized, try to use the Accessibility API to
+ // just set sanitized values (like text coming from resource files), rather than not
+ // setting it at all.
structure.setText(info.getText(), info.getTextSelectionStart(),
info.getTextSelectionEnd());
}
@@ -7077,14 +7150,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* Dispatch creation of {@link ViewStructure} down the hierarchy. The default
* implementation calls {@link #onProvideStructure} and
* {@link #onProvideVirtualStructure}.
- *
- * @deprecated As of API O, sub-classes should override
- * {@link #dispatchProvideStructure(ViewStructure, int)} instead.
*/
- // TODO(b/33197203): set proper API above
- @Deprecated
public void dispatchProvideStructure(ViewStructure structure) {
- dispatchProvideStructure(structure, 0);
+ dispatchProvideStructureForAssistOrAutoFill(structure, 0);
}
/**
@@ -7093,22 +7161,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* <p>The structure must be filled according to the request type, which is set in the
* {@code flags} parameter - see the documentation on each flag for more details.
*
- * <p>The default implementation calls {@link #onProvideStructure(ViewStructure, int)} and
- * {@link #onProvideVirtualStructure(ViewStructure, int)}.
+ * <p>The default implementation calls {@link #onProvideAutoFillStructure(ViewStructure, int)}
+ * and {@link #onProvideAutoFillVirtualStructure(ViewStructure, int)}.
*
* @param structure Fill in with structured view data.
- * @param flags optional flags (see {@link #ASSIST_FLAG_SANITIZED_TEXT} and
- * {@link #ASSIST_FLAG_NON_SANITIZED_TEXT} for more info).
+ * @param flags optional flags (see {@link #AUTO_FILL_FLAG_TYPE_FILL} and
+ * {@link #AUTO_FILL_FLAG_TYPE_SAVE} for more info).
*/
- public void dispatchProvideStructure(ViewStructure structure, int flags) {
+ public void dispatchProvideAutoFillStructure(ViewStructure structure, int flags) {
+ dispatchProvideStructureForAssistOrAutoFill(structure, flags);
+ }
+
+ private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure, int flags) {
+ // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well,
+ // this method should take a boolean with the type of request.
boolean forAutoFill = (flags
- & (View.ASSIST_FLAG_SANITIZED_TEXT
- | View.ASSIST_FLAG_NON_SANITIZED_TEXT)) != 0;
+ & (View.AUTO_FILL_FLAG_TYPE_FILL
+ | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0;
boolean blocked = forAutoFill ? isAutoFillBlocked() : isAssistBlocked();
if (!blocked) {
- onProvideStructure(structure, flags);
- onProvideVirtualStructure(structure, flags);
+ if (forAutoFill) {
+ onProvideAutoFillStructure(structure, flags);
+ onProvideAutoFillVirtualStructure(structure, flags);
+ } else {
+ onProvideStructure(structure);
+ onProvideVirtualStructure(structure);
+ }
} else {
structure.setClassName(getAccessibilityClassName().toString());
structure.setAssistBlocked(true);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 56501ecd9f4a..1f1af4bf0094 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3156,18 +3156,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
+ * Dispatch creation of {@link ViewStructure} down the hierarchy. This implementation
+ * adds in all child views of the view group, in addition to calling the default View
+ * implementation.
+ */
+ @Override
+ public void dispatchProvideStructure(ViewStructure structure) {
+ super.dispatchProvideStructure(structure);
+ dispatchProvideStructureForAssistOrAutoFill(structure, 0);
+ }
+
+ /**
* {@inheritDoc}
*
* <p>This implementation adds in all child views of the view group, in addition to calling the
* default {@link View} implementation.
*/
@Override
- public void dispatchProvideStructure(ViewStructure structure, int flags) {
- super.dispatchProvideStructure(structure, flags);
+ public void dispatchProvideAutoFillStructure(ViewStructure structure, int flags) {
+ super.dispatchProvideAutoFillStructure(structure, flags);
+ dispatchProvideStructureForAssistOrAutoFill(structure, flags);
+ }
+ private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure, int flags) {
+ // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well,
+ // this method should take a boolean with the type of request.
boolean forAutoFill = (flags
- & (View.ASSIST_FLAG_SANITIZED_TEXT
- | View.ASSIST_FLAG_NON_SANITIZED_TEXT)) != 0;
+ & (View.AUTO_FILL_FLAG_TYPE_FILL
+ | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0;
boolean blocked = forAutoFill ? isAutoFillBlocked() : isAssistBlocked();
@@ -3233,12 +3249,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
preorderedList, children, childIndex);
final ViewStructure cstructure = structure.newChild(i);
- // Must explicitly check which recursive method to call because child might
- // not be overriding the new, flags-based version
- if (flags == 0) {
- child.dispatchProvideStructure(cstructure);
+ // Must explicitly check which recursive method to call.
+ if (forAutoFill) {
+ child.dispatchProvideAutoFillStructure(cstructure, flags);
} else {
- child.dispatchProvideStructure(cstructure, flags);
+ child.dispatchProvideStructure(cstructure);
}
}
if (preorderedList != null) preorderedList.clear();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d6db634e6c64..b42f7695c3de 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -96,6 +96,7 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.policy.PhoneFallbackEventHandler;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
+import com.android.internal.view.SurfaceCallbackHelper;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -528,11 +529,17 @@ public final class ViewRootImpl implements ViewParent,
*/
public void notifyChildRebuilt() {
if (mView instanceof RootViewSurfaceTaker) {
+ if (mSurfaceHolderCallback != null) {
+ mSurfaceHolder.removeCallback(mSurfaceHolderCallback);
+ }
+
mSurfaceHolderCallback =
((RootViewSurfaceTaker)mView).willYouTakeTheSurface();
+
if (mSurfaceHolderCallback != null) {
mSurfaceHolder = new TakenSurfaceHolder();
mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
+ mSurfaceHolder.addCallback(mSurfaceHolderCallback);
} else {
mSurfaceHolder = null;
}
@@ -581,6 +588,7 @@ public final class ViewRootImpl implements ViewParent,
if (mSurfaceHolderCallback != null) {
mSurfaceHolder = new TakenSurfaceHolder();
mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
+ mSurfaceHolder.addCallback(mSurfaceHolderCallback);
}
}
@@ -1957,7 +1965,6 @@ public final class ViewRootImpl implements ViewParent,
mSurfaceHolder.ungetCallbacks();
mIsCreating = true;
- mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
@@ -1967,8 +1974,6 @@ public final class ViewRootImpl implements ViewParent,
surfaceChanged = true;
}
if (surfaceChanged || surfaceGenerationId != mSurface.getGenerationId()) {
- mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder,
- lp.format, mWidth, mHeight);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
@@ -1981,7 +1986,6 @@ public final class ViewRootImpl implements ViewParent,
} else if (hadSurface) {
mSurfaceHolder.ungetCallbacks();
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
- mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder);
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceDestroyed(mSurfaceHolder);
@@ -2646,21 +2650,18 @@ public final class ViewRootImpl implements ViewParent,
if (LOCAL_LOGV) {
Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
+
if (mSurfaceHolder != null && mSurface.isValid()) {
- mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
+ SurfaceCallbackHelper sch = new SurfaceCallbackHelper(mWindowSession, mWindow);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
- if (callbacks != null) {
- for (SurfaceHolder.Callback c : callbacks) {
- if (c instanceof SurfaceHolder.Callback2) {
- ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(mSurfaceHolder);
- }
- }
+
+ sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
+ } else {
+ try {
+ mWindowSession.finishDrawing(mWindow);
+ } catch (RemoteException e) {
}
}
- try {
- mWindowSession.finishDrawing(mWindow);
- } catch (RemoteException e) {
- }
}
}
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index e9ff9d0edba6..839e11c01303 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -19,6 +19,10 @@ package android.view;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
+import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillType;
+import android.view.autofill.AutoFillValue;
+import android.view.autofill.VirtualViewDelegate;
/**
* Container for storing additional per-view data generated by {@link View#onProvideStructure
@@ -258,6 +262,12 @@ public abstract class ViewStructure {
public abstract ViewStructure newChild(int index);
/**
+ * Like {@link #newChild(int)}, but providing a {@code virtualId} to the child so it can be
+ * auto-filled by {@link VirtualViewDelegate#autoFill(int, AutoFillValue)}.
+ */
+ public abstract ViewStructure newChild(int index, int virtualId);
+
+ /**
* Like {@link #newChild}, but allows the caller to asynchronously populate the returned
* child. It can transfer the returned {@link ViewStructure} to another thread for it
* to build its content (and children etc). Once done, some thread must call
@@ -268,6 +278,17 @@ public abstract class ViewStructure {
public abstract ViewStructure asyncNewChild(int index);
/**
+ * Like {@link #asyncNewChild(int)}, but providing a {@code virtualId} to the child so it can be
+ * auto-filled by {@link VirtualViewDelegate#autoFill(int, AutoFillValue)}.
+ */
+ public abstract ViewStructure asyncNewChild(int index, int virtualId);
+
+ /**
+ * Sets the {@link AutoFillType} that can be used to auto-fill this node.
+ */
+ public abstract void setAutoFillType(AutoFillType info);
+
+ /**
* Call when done populating a {@link ViewStructure} returned by
* {@link #asyncNewChild}.
*/
@@ -277,5 +298,8 @@ public abstract class ViewStructure {
public abstract Rect getTempRect();
/** @hide */
- public abstract void setAutoFillId(int autoFillId);
+ public abstract void setAutoFillId(int viewId);
+
+ /** @hide */
+ public abstract AutoFillId getAutoFillId();
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index caa09cccbab1..dd852567360f 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -437,6 +437,15 @@ public interface WindowManagerPolicy {
}
/**
+ * Holds the contents of a starting window. {@link #addSplashScreen} needs to wrap the
+ * contents of the starting window into an class implementing this interface, which then will be
+ * held by WM and passed into {@link #removeSplashScreen} when the starting window is no
+ * longer needed.
+ */
+ interface StartingSurface {
+ }
+
+ /**
* Interface for calling back in to the window manager that is private
* between it and the policy.
*/
@@ -738,17 +747,17 @@ public interface WindowManagerPolicy {
* context to for resources.
*
* @return Optionally you can return the View that was used to create the
- * window, for easy removal in removeStartingWindow.
+ * window, for easy removal in removeSplashScreen.
*
- * @see #removeStartingWindow
+ * @see #removeSplashScreen
*/
- public View addStartingWindow(IBinder appToken, String packageName,
- int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel,
- int labelRes, int icon, int logo, int windowFlags, Configuration overrideConfig);
+ public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
+ CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
+ int logo, int windowFlags, Configuration overrideConfig);
/**
* Called when the first window of an application has been displayed, while
- * {@link #addStartingWindow} has created a temporary initial window for
+ * {@link #addSplashScreen} has created a temporary initial window for
* that application. You should at this point remove the window from the
* window manager. This is called without the window manager locked so
* that you can call back into it.
@@ -759,11 +768,11 @@ public interface WindowManagerPolicy {
* even if you previously returned one.
*
* @param appToken Token of the application that has started.
- * @param window Window View that was returned by createStartingWindow.
+ * @param surface Surface that was returned by {@link #addSplashScreen}.
*
- * @see #addStartingWindow
+ * @see #addSplashScreen
*/
- public void removeStartingWindow(IBinder appToken, View window);
+ public void removeSplashScreen(IBinder appToken, StartingSurface surface);
/**
* Prepare for a window being added to the window manager. You can throw an
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 91278612072b..91468dae703e 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -25,7 +25,13 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.InputType;
+import android.text.Spannable;
+import android.text.Spanned;
import android.text.TextUtils;
+import android.text.style.AccessibilityClickableSpan;
+import android.text.style.AccessibilityURLSpan;
+import android.text.style.ClickableSpan;
+import android.text.style.URLSpan;
import android.util.ArraySet;
import android.util.LongArray;
import android.util.Pools.SynchronizedPool;
@@ -464,6 +470,14 @@ public class AccessibilityNodeInfo implements Parcelable {
public static final String ACTION_ARGUMENT_PROGRESS_VALUE =
"android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
+ /**
+ * Argument to pass the {@link AccessibilityClickableSpan}.
+ * For use with R.id.accessibilityActionClickOnClickableSpan
+ * @hide
+ */
+ public static final String ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN =
+ "android.view.accessibility.action.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN";
+
// Focus types
/**
@@ -628,6 +642,8 @@ public class AccessibilityNodeInfo implements Parcelable {
private CharSequence mPackageName;
private CharSequence mClassName;
+ // Hidden, unparceled value used to hold the original value passed to setText
+ private CharSequence mOriginalText;
private CharSequence mText;
private CharSequence mError;
private CharSequence mContentDescription;
@@ -2213,14 +2229,49 @@ public class AccessibilityNodeInfo implements Parcelable {
/**
* Gets the text of this node.
+ * <p>
+ * <strong>Note:</strong> If the text contains {@link ClickableSpan}s or {@link URLSpan}s,
+ * these spans will have been replaced with ones whose {@link ClickableSpan#onClick(View)}
+ * can be called from an {@link AccessibilityService}. When called from a service, the
+ * {@link View} argument is ignored and the corresponding span will be found on the view that
+ * this {@code AccessibilityNodeInfo} represents and called with that view as its argument.
+ * <p>
+ * This treatment of {@link ClickableSpan}s means that the text returned from this method may
+ * different slightly one passed to {@link #setText(CharSequence)}, although they will be
+ * equivalent according to {@link TextUtils#equals(CharSequence, CharSequence)}. The
+ * {@link ClickableSpan#onClick(View)} of any spans, however, will generally not work outside
+ * of an accessibility service.
+ * </p>
*
* @return The text.
*/
public CharSequence getText() {
+ // Attach this node to any spans that need it
+ if (mText instanceof Spanned) {
+ Spanned spanned = (Spanned) mText;
+ AccessibilityClickableSpan[] clickableSpans =
+ spanned.getSpans(0, mText.length(), AccessibilityClickableSpan.class);
+ for (int i = 0; i < clickableSpans.length; i++) {
+ clickableSpans[i].setAccessibilityNodeInfo(this);
+ }
+ AccessibilityURLSpan[] urlSpans =
+ spanned.getSpans(0, mText.length(), AccessibilityURLSpan.class);
+ for (int i = 0; i < urlSpans.length; i++) {
+ urlSpans[i].setAccessibilityNodeInfo(this);
+ }
+ }
return mText;
}
/**
+ * Get the text passed to setText before any changes to the spans.
+ * @hide
+ */
+ public CharSequence getOriginalText() {
+ return mOriginalText;
+ }
+
+ /**
* Sets the text of this node.
* <p>
* <strong>Note:</strong> Cannot be called from an
@@ -2234,6 +2285,34 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
public void setText(CharSequence text) {
enforceNotSealed();
+ mOriginalText = text;
+ // Replace any ClickableSpans in mText with placeholders
+ if (text instanceof Spanned) {
+ ClickableSpan[] spans =
+ ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
+ if (spans.length > 0) {
+ Spannable spannable = Spannable.Factory.getInstance().newSpannable(text);
+ for (int i = 0; i < spans.length; i++) {
+ ClickableSpan span = spans[i];
+ if ((span instanceof AccessibilityClickableSpan)
+ || (span instanceof AccessibilityURLSpan)) {
+ // We've already done enough
+ break;
+ }
+ int spanToReplaceStart = spannable.getSpanStart(span);
+ int spanToReplaceEnd = spannable.getSpanEnd(span);
+ int spanToReplaceFlags = spannable.getSpanFlags(span);
+ spannable.removeSpan(span);
+ ClickableSpan replacementSpan = (span instanceof URLSpan)
+ ? new AccessibilityURLSpan((URLSpan) span)
+ : new AccessibilityClickableSpan(span.getId());
+ spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd,
+ spanToReplaceFlags);
+ }
+ mText = spannable;
+ return;
+ }
+ }
mText = (text == null) ? null : text.subSequence(0, text.length());
}
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 09d4dfc93b19..767024ecfecf 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -237,7 +237,7 @@ public class AnimationSet extends Animation {
mDuration = a.getStartOffset() + a.getDuration();
mLastEnd = mStartOffset + mDuration;
} else {
- mLastEnd = Math.max(mLastEnd, a.getStartOffset() + a.getDuration());
+ mLastEnd = Math.max(mLastEnd, mStartOffset + a.getStartOffset() + a.getDuration());
mDuration = mLastEnd - mStartOffset;
}
}
diff --git a/core/java/android/view/autofill/AutoFillId.aidl b/core/java/android/view/autofill/AutoFillId.aidl
new file mode 100644
index 000000000000..56f0338ccafb
--- /dev/null
+++ b/core/java/android/view/autofill/AutoFillId.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2017, 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.autofill;
+
+parcelable AutoFillId; \ No newline at end of file
diff --git a/core/java/android/view/autofill/AutoFillId.java b/core/java/android/view/autofill/AutoFillId.java
new file mode 100644
index 000000000000..b7b694d26030
--- /dev/null
+++ b/core/java/android/view/autofill/AutoFillId.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 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.autofill;
+
+import static android.view.autofill.Helper.DEBUG;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A unique identifier for an auto-fill node inside an {@link android.app.Activity}.
+ */
+public final class AutoFillId implements Parcelable {
+
+ private int mViewId;
+ private boolean mVirtual;
+ private int mVirtualId;
+
+ /** @hide */
+ public AutoFillId(int id) {
+ mVirtual = false;
+ mViewId = id;
+ }
+
+ /** @hide */
+ public AutoFillId(AutoFillId parent, int virtualChildId) {
+ mVirtual = true;
+ mViewId = parent.mViewId;
+ mVirtualId = virtualChildId;
+ }
+
+ /** @hide */
+ public int getViewId() {
+ return mViewId;
+ }
+
+ /** @hide */
+ public int getVirtualChildId() {
+ return mVirtualId;
+ }
+
+ /** @hide */
+ public boolean isVirtual() {
+ return mVirtual;
+ }
+
+ /////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + mViewId;
+ result = prime * result + mVirtualId;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ final AutoFillId other = (AutoFillId) obj;
+ if (mViewId != other.mViewId) return false;
+ if (mVirtualId != other.mVirtualId) return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ if (!DEBUG) return super.toString();
+
+ final StringBuilder builder = new StringBuilder("FieldId [viewId=").append(mViewId);
+ if (mVirtual) {
+ builder.append(", virtualId=").append(mVirtualId);
+ }
+ return builder.append(']').toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mViewId);
+ parcel.writeInt(mVirtual ? 1 : 0);
+ parcel.writeInt(mVirtualId);
+ }
+
+ private AutoFillId(Parcel parcel) {
+ mViewId = parcel.readInt();
+ mVirtual = parcel.readInt() == 1;
+ mVirtualId = parcel.readInt();
+ }
+
+ public static final Parcelable.Creator<AutoFillId> CREATOR =
+ new Parcelable.Creator<AutoFillId>() {
+ @Override
+ public AutoFillId createFromParcel(Parcel source) {
+ return new AutoFillId(source);
+ }
+
+ @Override
+ public AutoFillId[] newArray(int size) {
+ return new AutoFillId[size];
+ }
+ };
+}
diff --git a/core/java/android/view/autofill/AutoFillType.aidl b/core/java/android/view/autofill/AutoFillType.aidl
new file mode 100644
index 000000000000..a63d7c50f38b
--- /dev/null
+++ b/core/java/android/view/autofill/AutoFillType.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, 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.autofill;
+
+parcelable AutoFillType; \ No newline at end of file
diff --git a/core/java/android/view/autofill/AutoFillType.java b/core/java/android/view/autofill/AutoFillType.java
new file mode 100644
index 000000000000..017f7f8ca3e8
--- /dev/null
+++ b/core/java/android/view/autofill/AutoFillType.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2016 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.autofill;
+
+import static android.view.autofill.Helper.DEBUG;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * Defines the type of a object that can be used to auto-fill a {@link View} so the
+ * {@link android.service.autofill.AutoFillService} can use the proper {@link AutoFillValue} to
+ * fill it.
+ *
+ * <p>Some {@link AutoFillType}s can have an optional {@code sub-type}: the
+ * main {@code type} defines the view's UI control category (like a text field), while the optional
+ * {@code sub-type} define its semantics (like a postal address).
+ */
+public final class AutoFillType implements Parcelable {
+
+ // Cached instance for types that don't have subtype; it uses the "lazy initialization holder
+ // class idiom" (Effective Java, Item 71) to avoid memory utilization when auto-fill is not
+ // enabled.
+ private static class DefaultTypesHolder {
+ static final AutoFillType TOGGLE = new AutoFillType(TYPE_TOGGLE, 0);
+ static final AutoFillType LIST = new AutoFillType(TYPE_LIST, 0);
+ }
+
+ private static final int TYPE_TEXT = 1;
+ private static final int TYPE_TOGGLE = 2;
+ // TODO(b/33197203): make sure it works with Spinners and/or add a new type for them
+ // (since they're often used for credit card selection)
+ private static final int TYPE_LIST = 3;
+
+ // TODO(b/33197203): add others, like date picker? That would be trick, because they're set as:
+ // updateDate(int year, int month, int dayOfMonth)
+ // So, we would have to either use a long representing the Date.time(), or a custom long
+ // representing:
+ // year * 10000 + month * 100 + day
+ // Then a custom getDatePickerValue(Bundle) that returns an immutable object with these 3 fields
+
+ private final int mType;
+ private final int mSubType;
+
+ private AutoFillType(int type, int subType) {
+ mType = type;
+ mSubType = subType;
+ }
+
+ /**
+ * Checks if this is a type for a text field, which is filled by a {@link CharSequence}.
+ *
+ * <p>{@link AutoFillValue} instances for auto-filling a {@link View} can be obtained through
+ * {@link AutoFillValue#forText(CharSequence)}, and the value of a bundle passed to auto-fill a
+ * {@link View} can be fetched through {@link AutoFillValue#getTextValue()}.
+ *
+ * <p>Sub-type for this type is the value defined by {@link TextView#getInputType()}.
+ */
+ public boolean isText() {
+ return mType == TYPE_TEXT;
+ }
+
+ /**
+ * Checks if this is a a type for a togglable field, which is filled by a {@code boolean}.
+ *
+ * <p>{@link AutoFillValue} instances for auto-filling a {@link View} can be obtained through
+ * {@link AutoFillValue#forToggle(boolean)}, and the value of a bundle passed to auto-fill a
+ * {@link View} can be fetched through {@link AutoFillValue#getToggleValue()}.
+ *
+ * <p>This type has no sub-types.
+ */
+ public boolean isToggle() {
+ return mType == TYPE_TOGGLE;
+ }
+
+ /**
+ * Checks if this is a type for a selection list field, which is filled by a {@code integer}
+ * representing the element index inside the list (starting at {@code 0}.
+ *
+ * <p>{@link AutoFillValue} instances for auto-filling a {@link View} can be obtained through
+ * {@link AutoFillValue#forList(int)}, and the value of a bundle passed to auto-fill a
+ * {@link View} can be fetched through {@link AutoFillValue#getListValue()}.
+ *
+ * <p>This type has no sub-types.
+ */
+ public boolean isList() {
+ return mType == TYPE_LIST;
+ }
+
+
+ /**
+ * Gets the optional sub-type, representing the {@link View}'s semantic.
+ *
+ * @return {@code 0} if type does not support sub-types.
+ */
+ public int getSubType() {
+ return mSubType;
+ }
+
+ /////////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////////
+
+ @Override
+ public String toString() {
+ if (!DEBUG) return super.toString();
+
+ return "AutoFillType [type=" + mType + ", subType=" + mSubType + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + mSubType;
+ result = prime * result + mType;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ final AutoFillType other = (AutoFillType) obj;
+ if (mSubType != other.mSubType) return false;
+ if (mType != other.mType) return false;
+ return true;
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mType);
+ parcel.writeInt(mSubType);
+ }
+
+ private AutoFillType(Parcel parcel) {
+ mType = parcel.readInt();
+ mSubType = parcel.readInt();
+ }
+
+ public static final Parcelable.Creator<AutoFillType> CREATOR =
+ new Parcelable.Creator<AutoFillType>() {
+ @Override
+ public AutoFillType createFromParcel(Parcel source) {
+ return new AutoFillType(source);
+ }
+
+ @Override
+ public AutoFillType[] newArray(int size) {
+ return new AutoFillType[size];
+ }
+ };
+
+ ////////////////////
+ // Factory methods //
+ ////////////////////
+
+ /**
+ * Creates a text field type, which is filled by a {@link CharSequence}.
+ *
+ * <p>See {@link #isText()} for more info.
+ */
+ public static AutoFillType forText(int inputType) {
+ return new AutoFillType(TYPE_TEXT, inputType);
+ }
+
+ /**
+ * Creates a type that can be toggled which is filled by a {@code boolean}.
+ *
+ * <p>See {@link #isToggle()} for more info.
+ */
+ public static AutoFillType forToggle() {
+ return DefaultTypesHolder.TOGGLE;
+ }
+
+ /**
+ * Creates a selection list, which is filled by a {@code integer} representing the element index
+ * inside the list (starting at {@code 0}.
+ *
+ * <p>See {@link #isList()} for more info.
+ */
+ public static AutoFillType forList() {
+ return DefaultTypesHolder.LIST;
+ }
+}
diff --git a/core/java/android/view/autofill/AutoFillValue.aidl b/core/java/android/view/autofill/AutoFillValue.aidl
new file mode 100644
index 000000000000..3b284b96dc7c
--- /dev/null
+++ b/core/java/android/view/autofill/AutoFillValue.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, 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.autofill;
+
+parcelable AutoFillValue; \ No newline at end of file
diff --git a/core/java/android/view/autofill/AutoFillValue.java b/core/java/android/view/autofill/AutoFillValue.java
new file mode 100644
index 000000000000..c39f26b9b864
--- /dev/null
+++ b/core/java/android/view/autofill/AutoFillValue.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 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.autofill;
+
+import static android.view.autofill.Helper.DEBUG;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.View;
+
+/**
+ * Abstracts how a {@link View} can be auto-filled by an
+ * {@link android.service.autofill.AutoFillService}.
+ *
+ * <p>Each {@link AutoFillValue} has a {@code type} and optionally a {@code sub-type}: the
+ * {@code type} defines the view's UI control category (like a text field), while the optional
+ * {@code sub-type} define its semantics (like a postal address).
+ */
+public final class AutoFillValue implements Parcelable {
+
+ private final CharSequence mText;
+ private final int mListIndex;
+ private final boolean mToggle;
+
+ private AutoFillValue(CharSequence text, int listIndex, boolean toggle) {
+ mText = text;
+ mListIndex = listIndex;
+ mToggle = toggle;
+ }
+
+ /**
+ * Gets the value to auto-fill a text field.
+ *
+ * <p>See {@link AutoFillType#isText()} for more info.
+ */
+ public CharSequence getTextValue() {
+ return mText;
+ }
+
+ /**
+ * Gets the value to auto-fill a toggable field.
+ *
+ * <p>See {@link AutoFillType#isToggle()} for more info.
+ */
+ public boolean getToggleValue() {
+ return mToggle;
+ }
+
+ /**
+ * Gets the value to auto-fill a selection list field.
+ *
+ * <p>See {@link AutoFillType#isList()} for more info.
+ */
+ public int getListValue() {
+ return mListIndex;
+ }
+
+ /////////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////////
+
+ @Override
+ public String toString() {
+ if (!DEBUG) return super.toString();
+
+ return "AutoFillValue[text=" + mText + ", listIndex=" + mListIndex + ", toggle=" + mToggle
+ + "]";
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeCharSequence(mText);
+ parcel.writeInt(mListIndex);
+ parcel.writeInt(mToggle ? 1 : 0);
+ }
+
+ private AutoFillValue(Parcel parcel) {
+ mText = parcel.readCharSequence();
+ mListIndex = parcel.readInt();
+ mToggle = parcel.readInt() == 1;
+ }
+
+ public static final Parcelable.Creator<AutoFillValue> CREATOR =
+ new Parcelable.Creator<AutoFillValue>() {
+ @Override
+ public AutoFillValue createFromParcel(Parcel source) {
+ return new AutoFillValue(source);
+ }
+
+ @Override
+ public AutoFillValue[] newArray(int size) {
+ return new AutoFillValue[size];
+ }
+ };
+
+ ////////////////////
+ // Factory methods //
+ ////////////////////
+
+ // TODO(b/33197203): add unit tests for each supported type (new / get should return same value)
+ /**
+ * Creates a new {@link AutoFillValue} to auto-fill a text field.
+ *
+ * <p>See {@link AutoFillType#isText()} for more info.
+ */
+ public static AutoFillValue forText(CharSequence value) {
+ return new AutoFillValue(value, 0, false);
+ }
+
+ /**
+ * Creates a new {@link AutoFillValue} to auto-fill a toggable field.
+ *
+ * <p>See {@link AutoFillType#isToggle()} for more info.
+ */
+ public static AutoFillValue forToggle(boolean value) {
+ return new AutoFillValue(null, 0, value);
+ }
+
+ /**
+ * Creates a new {@link AutoFillValue} to auto-fill a selection list field.
+ *
+ * <p>See {@link AutoFillType#isList()} for more info.
+ */
+ public static AutoFillValue forList(int value) {
+ return new AutoFillValue(null, value, false);
+ }
+}
diff --git a/core/java/android/view/autofill/Dataset.aidl b/core/java/android/view/autofill/Dataset.aidl
new file mode 100644
index 000000000000..2a8e67caace1
--- /dev/null
+++ b/core/java/android/view/autofill/Dataset.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, 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.autofill;
+
+parcelable Dataset; \ No newline at end of file
diff --git a/core/java/android/view/autofill/Dataset.java b/core/java/android/view/autofill/Dataset.java
new file mode 100644
index 000000000000..a73eb7740e80
--- /dev/null
+++ b/core/java/android/view/autofill/Dataset.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2016 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.autofill;
+
+import static android.view.autofill.Helper.DEBUG;
+import static android.view.autofill.Helper.append;
+
+import android.app.Activity;
+import android.app.assist.AssistStructure.ViewNode;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.autofill.AutoFillService;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A set of data that can be used to auto-fill an {@link Activity}.
+ *
+ * <p>It contains:
+ *
+ * <ol>
+ * <li>A name used to identify the dataset in the UI.
+ * <li>A list of id/value pairs for the fields that can be auto-filled.
+ * <li>An optional {@link Bundle} with extras (used only by the service creating it).
+ * </ol>
+ *
+ * See {@link FillResponse} for examples.
+ */
+public final class Dataset implements Parcelable {
+
+ private final CharSequence mName;
+ private final ArrayList<DatasetField> mFields;
+ private final Bundle mExtras;
+
+ private Dataset(Dataset.Builder builder) {
+ mName = builder.mName;
+ // TODO(b/33197203): make an immutable copy of mFields?
+ mFields = builder.mFields;
+ mExtras = builder.mExtras;
+ }
+
+ /** @hide */
+ public CharSequence getName() {
+ return mName;
+ }
+
+ /** @hide */
+ public List<DatasetField> getFields() {
+ return mFields;
+ }
+
+ /** @hide */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public String toString() {
+ if (!DEBUG) return super.toString();
+
+ final StringBuilder builder = new StringBuilder("Dataset [name=").append(mName)
+ .append(", fields=").append(mFields).append(", extras=");
+ append(builder, mExtras);
+ return builder.append(']').toString();
+ }
+
+ /**
+ * A builder for {@link Dataset} objects.
+ */
+ public static final class Builder {
+ private CharSequence mName;
+ private final ArrayList<DatasetField> mFields = new ArrayList<>();
+ private Bundle mExtras;
+
+ /**
+ * Creates a new builder.
+ *
+ * @param name Name used to identify the dataset in the UI. Typically it's the same value as
+ * the first field in the dataset (like username or email address) or an user-provided name
+ * (like "My Work Address").
+ */
+ public Builder(CharSequence name) {
+ mName = Preconditions.checkStringNotEmpty(name, "name cannot be empty or null");
+ }
+
+ /**
+ * Sets the value of a field.
+ *
+ * @param id id returned by {@link ViewNode#getAutoFillId()}.
+ * @param value value to be auto filled.
+ */
+ public Dataset.Builder setValue(AutoFillId id, AutoFillValue value) {
+ putField(new DatasetField(id, value));
+ return this;
+ }
+
+ /**
+ * Creates a new {@link Dataset} instance.
+ */
+ public Dataset build() {
+ return new Dataset(this);
+ }
+
+ /**
+ * Sets a {@link Bundle} that will be passed to subsequent calls to {@link AutoFillService}
+ * methods such as
+ * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
+ * android.os.CancellationSignal, android.service.autofill.SaveCallback)}, using
+ * {@link AutoFillService#EXTRA_DATASET_EXTRAS} as the key.
+ *
+ * <p>It can be used to keep service state in between calls.
+ */
+ public Builder setExtras(Bundle extras) {
+ mExtras = Objects.requireNonNull(extras, "extras cannot be null");
+ return this;
+ }
+
+ /**
+ * Emulates {@code Map.put()} by adding a new field to the list if its id is not the yet,
+ * or replacing the existing one.
+ */
+ private void putField(DatasetField field) {
+ // TODO(b/33197203): check if already exists and replaces it if so
+ mFields.add(field);
+ }
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeCharSequence(mName);
+ parcel.writeList(mFields);
+ parcel.writeBundle(mExtras);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Dataset(Parcel parcel) {
+ mName = parcel.readCharSequence();
+ mFields = parcel.readArrayList(null);
+ mExtras = parcel.readBundle();
+ }
+
+ public static final Parcelable.Creator<Dataset> CREATOR = new Parcelable.Creator<Dataset>() {
+ @Override
+ public Dataset createFromParcel(Parcel source) {
+ return new Dataset(source);
+ }
+
+ @Override
+ public Dataset[] newArray(int size) {
+ return new Dataset[size];
+ }
+ };
+}
diff --git a/core/java/android/view/autofill/DatasetField.java b/core/java/android/view/autofill/DatasetField.java
new file mode 100644
index 000000000000..c6b92acd5759
--- /dev/null
+++ b/core/java/android/view/autofill/DatasetField.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 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.autofill;
+
+import static android.view.autofill.Helper.DEBUG;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public final class DatasetField implements Parcelable {
+
+ private final AutoFillId mId;
+ private final AutoFillValue mValue;
+
+ DatasetField(AutoFillId id, AutoFillValue value) {
+ mId = id;
+ mValue = value;
+ }
+
+ public AutoFillId getId() {
+ return mId;
+ }
+
+ public AutoFillValue getValue() {
+ return mValue;
+ }
+
+ /////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////
+
+ @Override
+ public String toString() {
+ if (!DEBUG) return super.toString();
+
+ return "DatasetField [id=" + mId + ", value=" + mValue + "]";
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelable(mId, 0);
+ parcel.writeParcelable(mValue, 0);
+ }
+
+ private DatasetField(Parcel parcel) {
+ mId = parcel.readParcelable(null);
+ mValue = parcel.readParcelable(null);
+ }
+
+ public static final Parcelable.Creator<DatasetField> CREATOR =
+ new Parcelable.Creator<DatasetField>() {
+ @Override
+ public DatasetField createFromParcel(Parcel source) {
+ return new DatasetField(source);
+ }
+
+ @Override
+ public DatasetField[] newArray(int size) {
+ return new DatasetField[size];
+ }
+ };
+}
diff --git a/core/java/android/view/autofill/FieldId.aidl b/core/java/android/view/autofill/FieldId.aidl
new file mode 100644
index 000000000000..35af6453dbd1
--- /dev/null
+++ b/core/java/android/view/autofill/FieldId.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, 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.autofill;
+
+parcelable FieldId; \ No newline at end of file
diff --git a/core/java/android/view/autofill/FillResponse.aidl b/core/java/android/view/autofill/FillResponse.aidl
new file mode 100644
index 000000000000..b018f15923ec
--- /dev/null
+++ b/core/java/android/view/autofill/FillResponse.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, 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.autofill;
+
+parcelable FillResponse; \ No newline at end of file
diff --git a/core/java/android/view/autofill/FillResponse.java b/core/java/android/view/autofill/FillResponse.java
new file mode 100644
index 000000000000..3a147674965e
--- /dev/null
+++ b/core/java/android/view/autofill/FillResponse.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2016 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.autofill;
+
+import static android.view.autofill.Helper.DEBUG;
+import static android.view.autofill.Helper.append;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.autofill.AutoFillService;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Response for a
+ * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle,
+ * android.os.CancellationSignal, android.service.autofill.FillCallback)}
+ * request.
+ *
+ * <p>The response typically contains one or more {@link Dataset}s, each representing a set of
+ * fields that can be auto-filled together. For example, for a login page with username/password
+ * where the user only have one account in the service, the response could be:
+ *
+ * <pre class="prettyprint">
+ * new FillResponse.Builder()
+ * .add(new Dataset.Builder("homer")
+ * .setTextFieldValue(id1, "homer")
+ * .setTextFieldValue(id2, "D'OH!")
+ * .build())
+ * .build();
+ * </pre>
+ *
+ * <p>If the user had 2 accounts, each with its own user-provided names, the response could be:
+ *
+ * <pre class="prettyprint">
+ * new FillResponse.Builder()
+ * .add(new Dataset.Builder("Homer's Account")
+ * .setTextFieldValue(id1, "homer")
+ * .setTextFieldValue(id2, "D'OH!")
+ * .build())
+ * .add(new Dataset.Builder("Bart's Account")
+ * .setTextFieldValue(id1, "elbarto")
+ * .setTextFieldValue(id2, "cowabonga")
+ * .build())
+ * .build();
+ * </pre>
+ *
+ * <p>If the user does not have any data associated with this {@link Activity} but the service
+ * wants to offer the user the option to save the data that was entered, then the service could
+ * populate the response with {@code savableIds} instead of {@link Dataset}s:
+ *
+ * <pre class="prettyprint">
+ * new FillResponse.Builder()
+ * .addSavableFields(id1, id2)
+ * .build();
+ * </pre>
+ *
+ * <p>Similarly, there might be cases where the user data on the service is enough to populate some
+ * fields but not all, and the service would still be interested on saving the other fields. In this
+ * scenario, the service could populate the response with both {@link Dataset}s and
+ * {@code savableIds}:
+ *
+ * <pre class="prettyprint">
+ * new FillResponse.Builder()
+ * .add(new Dataset.Builder("Homer")
+ * .setTextFieldValue(id1, "Homer") // first name
+ * .setTextFieldValue(id2, "Simpson") // last name
+ * .setTextFieldValue(id3, "742 Evergreen Terrace") // street
+ * .setTextFieldValue(id4, "Springfield") // city
+ * .build())
+ * .addSavableFields(id5, id6) // state and zipcode
+ * .build();
+ *
+ * </pre>
+ *
+ * <p>Notice that the ids that are part of a dataset (ids 1 to 4, in this example) are automatically
+ * added to the {@code savableIds} list.
+ *
+ * <p>If the service has multiple {@link Dataset}s with multiple options for some fields on each
+ * dataset (for example, multiple accounts with both a home and work address), then it should
+ * "partition" the {@link Activity} in sections and populate the response with just a subset of the
+ * data that would fulfill the first section; then once the user fills the first section and taps
+ * a field from the next section, the Android system would issue another request for that section,
+ * and so on. For example, the first response could be:
+ *
+ * <pre class="prettyprint">
+ * new FillResponse.Builder()
+ * .add(new Dataset.Builder("Homer")
+ * .setTextFieldValue(id1, "Homer")
+ * .setTextFieldValue(id2, "Simpson")
+ * .build())
+ * .add(new Dataset.Builder("Bart")
+ * .setTextFieldValue(id1, "Bart")
+ * .setTextFieldValue(id2, "Simpson")
+ * .build())
+ * .build();
+ * </pre>
+ *
+ * <p>Then after the user picks the {@code Homer} dataset and taps the {@code Street} field to
+ * trigger another auto-fill request, the second response could be:
+ *
+ * <pre class="prettyprint">
+ * new FillResponse.Builder()
+ * .add(new Dataset.Builder("Home")
+ * .setTextFieldValue(id3, "742 Evergreen Terrace")
+ * .setTextFieldValue(id4, "Springfield")
+ * .build())
+ * .add(new Dataset.Builder("Work")
+ * .setTextFieldValue(id3, "Springfield Nuclear Power Plant")
+ * .setTextFieldValue(id4, "Springfield")
+ * .build())
+ * .build();
+ * </pre>
+ *
+ * <p>Finally, the service can use the {@link FillResponse.Builder#setExtras(Bundle)} and/or
+ * {@link Dataset.Builder#setExtras(Bundle)} methods to pass
+ * a {@link Bundle} with service-specific data use to identify this response on future calls (like
+ * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
+ * android.os.CancellationSignal, android.service.autofill.SaveCallback)}) - such bundle will be
+ * available as the {@link AutoFillService#EXTRA_RESPONSE_EXTRAS} extra in
+ * that method's {@code extras} argument.
+ */
+public final class FillResponse implements Parcelable {
+
+ private final List<Dataset> mDatasets;
+ private final AutoFillId[] mSavableIds;
+ private final Bundle mExtras;
+
+ private FillResponse(Builder builder) {
+ // TODO(b/33197203): make it immutable?
+ mDatasets = builder.mDatasets;
+ final int size = builder.mSavableIds.size();
+ mSavableIds = new AutoFillId[size];
+ int i = 0;
+ for (AutoFillId id : builder.mSavableIds) {
+ mSavableIds[i++] = id;
+ }
+ mExtras = builder.mExtras;
+ }
+
+ /** @hide */
+ public List<Dataset> getDatasets() {
+ return mDatasets;
+ }
+
+ /** @hide */
+ public AutoFillId[] getSavableIds() {
+ return mSavableIds;
+ }
+
+ /** @hide */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Builder for {@link FillResponse} objects.
+ */
+ public static final class Builder {
+ private final List<Dataset> mDatasets = new ArrayList<>();
+ private final Set<AutoFillId> mSavableIds = new HashSet<>();
+ private Bundle mExtras;
+
+ /**
+ * Adds a new {@link Dataset} to this response.
+ *
+ * @throws IllegalArgumentException if a dataset with same {@code name} already exists.
+ */
+ public Builder addDataset(Dataset dataset) {
+ Preconditions.checkNotNull(dataset, "dataset cannot be null");
+ // TODO(b/33197203): check if name already exists
+ // TODO(b/33197203): check if authId already exists (and update javadoc)
+ mDatasets.add(dataset);
+ for (DatasetField field : dataset.getFields()) {
+ mSavableIds.add(field.getId());
+ }
+ return this;
+ }
+
+ /**
+ * Adds ids of additional fields that the service would be interested to save (through
+ * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
+ * android.os.CancellationSignal, android.service.autofill.SaveCallback)}) but were not
+ * indirectly set through {@link #addDataset(Dataset)}.
+ *
+ * <p>See {@link FillResponse} for examples.
+ */
+ public Builder addSavableFields(AutoFillId...ids) {
+ for (AutoFillId id : ids) {
+ mSavableIds.add(id);
+ }
+ return this;
+ }
+
+ /**
+ * Sets a {@link Bundle} that will be passed to subsequent calls to {@link AutoFillService}
+ * methods such as
+ * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
+ * android.os.CancellationSignal, android.service.autofill.SaveCallback)}, using
+ * {@link AutoFillService#EXTRA_RESPONSE_EXTRAS} as the key.
+ *
+ * <p>It can be used when to keep service state in between calls.
+ */
+ public Builder setExtras(Bundle extras) {
+ mExtras = Objects.requireNonNull(extras, "extras cannot be null");
+ return this;
+ }
+
+ /**
+ * Builds a new {@link FillResponse} instance.
+ */
+ public FillResponse build() {
+ return new FillResponse(this);
+ }
+ }
+
+ /////////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////////
+ @Override
+ public String toString() {
+ if (!DEBUG) return super.toString();
+
+ final StringBuilder builder = new StringBuilder("FillResponse: [datasets=")
+ .append(mDatasets).append(", savableIds=").append(Arrays.toString(mSavableIds))
+ .append(", extras=");
+ append(builder, mExtras);
+ return builder.append(']').toString();
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeList(mDatasets);
+ parcel.writeParcelableArray(mSavableIds, 0);
+ parcel.writeBundle(mExtras);
+ }
+
+ private FillResponse(Parcel parcel) {
+ mDatasets = new ArrayList<>();
+ parcel.readList(mDatasets, null);
+ mSavableIds = parcel.readParcelableArray(null, AutoFillId.class);
+ mExtras = parcel.readBundle();
+ }
+
+ public static final Parcelable.Creator<FillResponse> CREATOR =
+ new Parcelable.Creator<FillResponse>() {
+ @Override
+ public FillResponse createFromParcel(Parcel source) {
+ return new FillResponse(source);
+ }
+
+ @Override
+ public FillResponse[] newArray(int size) {
+ return new FillResponse[size];
+ }
+ };
+}
diff --git a/core/java/android/view/autofill/Helper.java b/core/java/android/view/autofill/Helper.java
new file mode 100644
index 000000000000..772710e31b88
--- /dev/null
+++ b/core/java/android/view/autofill/Helper.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 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.autofill;
+
+import android.os.Bundle;
+
+import java.util.Set;
+
+/** @hide */
+public final class Helper {
+
+ // TODO(b/33197203): set to false when stable
+ static final boolean DEBUG = true;
+ static final String REDACTED = "[REDACTED]";
+
+ static void append(StringBuilder builder, Bundle bundle) {
+ if (bundle == null) {
+ builder.append("N/A");
+ } else if (!DEBUG) {
+ builder.append(REDACTED);
+ } else {
+ final Set<String> keySet = bundle.keySet();
+ builder.append("[bundle with ").append(keySet.size()).append(" extras:");
+ for (String key : keySet) {
+ builder.append(' ').append(key).append('=').append(bundle.get(key)).append(',');
+ }
+ builder.append(']');
+ }
+ }
+
+ private Helper() {
+ throw new UnsupportedOperationException("contains static members only");
+ }
+}
diff --git a/core/java/android/view/autofill/VirtualViewDelegate.java b/core/java/android/view/autofill/VirtualViewDelegate.java
new file mode 100644
index 000000000000..a19b4e53ba52
--- /dev/null
+++ b/core/java/android/view/autofill/VirtualViewDelegate.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2016 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.autofill;
+
+import android.util.Log;
+import android.view.View;
+import android.view.ViewStructure;
+
+/**
+ * This class is the contract a client should implement to enable support of a
+ * virtual view hierarchy rooted at a given view for auto-fill purposes.
+ *
+ * <p>The view hierarchy is typically created through the
+ * {@link View#onProvideAutoFillVirtualStructure(android.view.ViewStructure, int)} call and client
+ * add virtual children by calling {@link ViewStructure#newChild(int, int)} or
+ * {@link ViewStructure#asyncNewChild(int, int)}, where the client provides the {@code virtualId}
+ * of the children - the same {@code virtualId} is used in the methods of this class.
+ *
+ * <p>Objects of this class are typically created by overriding
+ * {@link View#getAutoFillVirtualViewDelegate(Callback)} and saving the passed callback, which must
+ * be notified upon changes on the hierarchy.
+ *
+ * <p>The main use case of these API is to enable custom views that draws its content - such as
+ * {@link android.webkit.WebView} providers - to support the AutoFill Framework:
+ *
+ * <ol>
+ * <li>Client populates the virtual hierarchy on
+ * {@link View#onProvideAutoFillVirtualStructure(android.view.ViewStructure, int)}
+ * <li>Android System generates the proper {@link AutoFillId} - encapsulating the view and the
+ * virtual node ids - and pass it to the {@link android.service.autofill.AutoFillService}.
+ * <li>The service uses the {@link AutoFillId} to populate the auto-fill {@link Dataset}s and pass
+ * it back to the Android System.
+ * <li>Android System uses the {@link AutoFillId} to find the proper custom view and calls
+ * {@link #autoFill(int, AutoFillValue)} on that view passing the virtual id.
+ * <li>This provider than finds the node in the hierarchy and auto-fills it.
+ * </ol>
+ *
+ */
+public abstract class VirtualViewDelegate {
+
+ // TODO(b/33197203): set to false once stable
+ private static final boolean DEBUG = true;
+
+ private static final String TAG = "VirtualViewDelegate";
+
+ /**
+ * Auto-fills a virtual view with the {@code value}.
+ *
+ * @param virtualId id identifying the virtual node inside the custom view.
+ * @param value value to be auto-filled.
+ */
+ public abstract void autoFill(int virtualId, AutoFillValue value);
+
+ /**
+ * Callback used to notify the AutoFill Framework of changes made on the view hierarchy while
+ * an {@link android.app.Activity} is being auto filled.
+ */
+ public abstract static class Callback {
+
+ /**
+ * Sent when the focus inside the hierarchy changed.
+ *
+ * <p>Typically callled twice - for the nodes that lost and gained focus.
+ *
+ * <p>This method should only be called when the change was not caused by the AutoFill
+ * Framework itselft (i.e, through {@link VirtualViewDelegate#autoFill(int, AutoFillValue)},
+ * but by external causes (for example, when the user changed the value through the view's
+ * UI).
+ *
+ * @param virtualId id of the node whose focus changed.
+ * @param hasFocus {@code true} when focus was gained, {@code false} when it was lost.
+ */
+ public void onFocusChanged(int virtualId, boolean hasFocus) {
+ if (DEBUG) Log.d(TAG, "onFocusChanged() for " + virtualId + ": " + hasFocus);
+ }
+
+ /**
+ * Sent when the value of a node was changed.
+ *
+ * <p>This method should only be called when the change was not caused by the AutoFill
+ * Framework itselft (i.e, through {@link VirtualViewDelegate#autoFill(int, AutoFillValue)},
+ * but by external causes (for example, when the user changed the value through the view's
+ * UI).
+ *
+ * @param virtualId id of the node whose value changed.
+ */
+ public void onValueChanged(int virtualId) {
+ if (DEBUG) Log.d(TAG, "onValueChanged() for" + virtualId);
+ }
+
+ /**
+ * Sent when nodes were removed (or had their ids changed) after the hierarchy has been
+ * committed to
+ * {@link View#onProvideAutoFillVirtualStructure(android.view.ViewStructure, int)}.
+ *
+ * <p>For example, when the view is rendering an {@code HTML} page, it should call this
+ * method when:
+ * <ul>
+ * <li>User navigated to another page and some (or all) nodes are gone.
+ * <li>The page's {@code DOM} was changed by {@code JavaScript} and some nodes moved (and
+ * are now identified by different ids).
+ * </ul>
+ *
+ * @param virtualIds id of the nodes that were removed.
+ */
+ public void onNodeRemoved(int... virtualIds) {
+ if (DEBUG) Log.d(TAG, "onNodeRemoved(): " + virtualIds);
+ }
+ }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 8ecc42de12df..f98c099f053f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2515,8 +2515,8 @@ public class WebView extends AbsoluteLayout
}
@Override
- public void onProvideVirtualStructure(ViewStructure structure, int flags) {
- mProvider.getViewDelegate().onProvideVirtualStructure(structure, flags);
+ public void onProvideAutoFillVirtualStructure(ViewStructure structure, int flags) {
+ mProvider.getViewDelegate().onProvideAutoFillVirtualStructure(structure, flags);
}
/** @hide */
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index eb0c44c3a07d..d7a49e46a236 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -59,8 +59,8 @@ public final class WebViewFactory {
// visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
/** @hide */
- public static final String CHROMIUM_WEBVIEW_FACTORY =
- "com.android.webview.chromium.WebViewChromiumFactoryProvider";
+ private static final String CHROMIUM_WEBVIEW_FACTORY =
+ "com.android.webview.chromium.WebViewChromiumFactoryProviderForO";
private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
@@ -143,6 +143,23 @@ public final class WebViewFactory {
}
/**
+ * @hide
+ */
+ public static Class<WebViewFactoryProvider> getWebViewProviderClass( ClassLoader clazzLoader)
+ throws ClassNotFoundException{
+ try {
+ return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,
+ true, clazzLoader);
+ } catch (ClassNotFoundException e) {
+ // TODO: This loads the provider which is not built for O, should be removed
+ // before the release.
+ return (Class<WebViewFactoryProvider>) Class.forName(
+ "com.android.webview.chromium.WebViewChromiumFactoryProvider",
+ true, clazzLoader);
+ }
+ }
+
+ /**
* Load the native library for the given package name iff that package
* name is the same as the one providing the webview.
*/
@@ -366,9 +383,9 @@ public final class WebViewFactory {
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
try {
- return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,
- true, clazzLoader);
- } finally {
+ return getWebViewProviderClass(clazzLoader);
+ }
+ finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (ClassNotFoundException e) {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 7b951806f097..dd1b0d2d17a4 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -312,7 +312,7 @@ public interface WebViewProvider {
public void onProvideVirtualStructure(android.view.ViewStructure structure);
@SuppressWarnings("unused")
- public default void onProvideVirtualStructure(android.view.ViewStructure structure,
+ public default void onProvideAutoFillVirtualStructure(android.view.ViewStructure structure,
int flags) {
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 3213a34e28f1..718070d0d49a 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -34,6 +34,8 @@ import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.autofill.AutoFillType;
+import android.view.autofill.AutoFillValue;
import com.android.internal.R;
@@ -52,6 +54,7 @@ import com.android.internal.R;
* </p>
*/
public abstract class CompoundButton extends Button implements Checkable {
+
private boolean mChecked;
private boolean mBroadcasting;
@@ -111,6 +114,7 @@ public abstract class CompoundButton extends Button implements Checkable {
applyButtonTint();
}
+ @Override
public void toggle() {
setChecked(!mChecked);
}
@@ -130,6 +134,7 @@ public abstract class CompoundButton extends Button implements Checkable {
}
@ViewDebug.ExportedProperty
+ @Override
public boolean isChecked() {
return mChecked;
}
@@ -139,6 +144,7 @@ public abstract class CompoundButton extends Button implements Checkable {
*
* @param checked true to check the button, false to uncheck it
*/
+ @Override
public void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
@@ -514,12 +520,15 @@ public abstract class CompoundButton extends Button implements Checkable {
+ " checked=" + checked + "}";
}
- public static final Parcelable.Creator<SavedState> CREATOR
- = new Parcelable.Creator<SavedState>() {
+ @SuppressWarnings("hiding")
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ @Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
+ @Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
@@ -551,4 +560,16 @@ public abstract class CompoundButton extends Button implements Checkable {
super.encodeProperties(stream);
stream.addProperty("checked", isChecked());
}
+
+ // TODO(b/33197203): add unit/CTS tests for auto-fill methods
+
+ @Override
+ public void autoFill(AutoFillValue value) {
+ setChecked(value.getToggleValue());
+ }
+
+ @Override
+ public AutoFillType getAutoFillType() {
+ return AutoFillType.forToggle();
+ }
}
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 043eb348a706..af5c8426a44a 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -24,8 +24,10 @@ import android.text.TextUtils;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.accessibility.AccessibilityNodeInfo;
-
+import android.view.autofill.AutoFillType;
+import android.view.autofill.AutoFillValue;
/*
* This is supposed to be a *very* thin veneer over TextView.
@@ -154,4 +156,26 @@ public class EditText extends TextView {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT);
}
}
+
+ // TODO(b/33197203): add unit/CTS tests for auto-fill methods
+
+ @Override
+ public void autoFill(AutoFillValue value) {
+ final CharSequence text = value.getTextValue();
+
+ if (text == null) {
+ Log.w(VIEW_LOG_TAG, "EditText.autoFill(): no text on AutoFillValue");
+ return;
+ }
+ // TODO(b/33197203): once auto-fill is triggered by the IME, we'll need a new setText()
+ // or setAutoFillText() method on TextView to avoid re-triggering it.
+ setText(text);
+ }
+
+ @Override
+ public AutoFillType getAutoFillType() {
+ // TODO(b/33197203): ideally it should return a constant, but value returned by
+ // getInputType() can change.
+ return AutoFillType.forText(getInputType());
+ }
}
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 54b57631366f..45fd9e6ef5a7 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -16,12 +16,16 @@
package android.widget;
+
import android.annotation.IdRes;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import android.view.autofill.AutoFillType;
+import android.view.autofill.AutoFillValue;
import com.android.internal.R;
@@ -51,6 +55,7 @@ import com.android.internal.R;
*
*/
public class RadioGroup extends LinearLayout {
+
// holds the checked id; the selection is empty by default
private int mCheckedId = -1;
// tracks children radio buttons checked state
@@ -335,6 +340,7 @@ public class RadioGroup extends LinearLayout {
}
private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {
+ @Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// prevents from infinite recursion
if (mProtectFromCheckedChange) {
@@ -364,6 +370,7 @@ public class RadioGroup extends LinearLayout {
/**
* {@inheritDoc}
*/
+ @Override
public void onChildViewAdded(View parent, View child) {
if (parent == RadioGroup.this && child instanceof RadioButton) {
int id = child.getId();
@@ -384,6 +391,7 @@ public class RadioGroup extends LinearLayout {
/**
* {@inheritDoc}
*/
+ @Override
public void onChildViewRemoved(View parent, View child) {
if (parent == RadioGroup.this && child instanceof RadioButton) {
((RadioButton) child).setOnCheckedChangeWidgetListener(null);
@@ -394,4 +402,22 @@ public class RadioGroup extends LinearLayout {
}
}
}
+
+ // TODO(b/33197203): add unit/CTS tests for auto-fill methods
+
+ @Override
+ public void autoFill(AutoFillValue value) {
+ final int index = value.getListValue();
+ final View child = getChildAt(index);
+ if (child == null) {
+ Log.w(VIEW_LOG_TAG, "RadioGroup.autoFill(): no child with index " + index);
+ return;
+ }
+ check(child.getId());
+ }
+
+ @Override
+ public AutoFillType getAutoFillType() {
+ return AutoFillType.forList();
+ }
}
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index e629df9bd3df..a9257e6014cf 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -1404,10 +1404,19 @@ public class Switch extends CompoundButton {
}
@Override
- public void onProvideStructure(ViewStructure structure, int flags) {
- super.onProvideStructure(structure, flags);
+ public void onProvideStructure(ViewStructure structure) {
+ super.onProvideStructure(structure);
+ onProvideAutoFillStructureForAssistOrAutoFill(structure);
+ }
+
+ @Override
+ public void onProvideAutoFillStructure(ViewStructure structure, int flags) {
+ super.onProvideAutoFillStructure(structure, flags);
+ onProvideAutoFillStructureForAssistOrAutoFill(structure);
+ }
- // NOTE: current there is no difference for Assist (flags=0) or AutoFill (flags>0);
+ // NOTE: currently there is no difference for Assist or AutoFill, so it doesn't take flags
+ private void onProvideAutoFillStructureForAssistOrAutoFill(ViewStructure structure) {
CharSequence switchText = isChecked() ? mTextOn : mTextOff;
if (!TextUtils.isEmpty(switchText)) {
CharSequence oldText = structure.getText();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1961bf6b7e15..1ddf53d8b19a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -596,6 +596,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private int mBreakStrategy;
private int mHyphenationFrequency;
+ private boolean mJustify;
private int mMaximum = Integer.MAX_VALUE;
private int mMaxMode = LINES;
@@ -769,6 +770,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
String fontFeatureSettings = null;
mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
+ mJustify = false;
final Resources.Theme theme = context.getTheme();
@@ -3298,6 +3300,29 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Enables or disables full justification. The default value is false.
+ *
+ * @see #getJustify()
+ */
+ public void setJustify(boolean justify) {
+ mJustify = justify;
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
+ }
+
+ /**
+ * @return true if currently paragraph justification is enabled.
+ *
+ * @see #setJustify(boolean)
+ */
+ public boolean getJustify() {
+ return mJustify;
+ }
+
+ /**
* Sets font feature settings. The format is the same as the CSS
* font-feature-settings attribute:
* <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
@@ -7170,6 +7195,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy)
.setHyphenationFrequency(mHyphenationFrequency)
+ .setJustify(mJustify)
.setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
if (shouldEllipsize) {
builder.setEllipsize(mEllipsize)
@@ -7211,7 +7237,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mText instanceof Spannable) {
result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth,
alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad,
- mBreakStrategy, mHyphenationFrequency,
+ mBreakStrategy, mHyphenationFrequency, mJustify,
getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth);
} else {
if (boring == UNKNOWN_BORING) {
@@ -7261,6 +7287,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy)
.setHyphenationFrequency(mHyphenationFrequency)
+ .setJustify(mJustify)
.setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
if (shouldEllipsize) {
builder.setEllipsize(effectiveEllipsize)
@@ -9391,11 +9418,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
@Override
- public void onProvideStructure(ViewStructure structure, int flags) {
- super.onProvideStructure(structure, flags);
+ public void onProvideStructure(ViewStructure structure) {
+ super.onProvideStructure(structure);
+ onProvideAutoStructureForAssistOrAutoFill(structure, 0);
+ }
+ @Override
+ public void onProvideAutoFillStructure(ViewStructure structure, int flags) {
+ super.onProvideAutoFillStructure(structure, flags);
+ onProvideAutoStructureForAssistOrAutoFill(structure, flags);
+ }
+
+ private void onProvideAutoStructureForAssistOrAutoFill(ViewStructure structure, int flags) {
+ // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well,
+ // this method should take a boolean with the type of request.
final boolean forAutoFillSave =
- (flags & ASSIST_FLAG_NON_SANITIZED_TEXT) != 0;
+ (flags & AUTO_FILL_FLAG_TYPE_SAVE) != 0;
+
final boolean isPassword = hasPasswordTransformationMethod()
|| isPasswordInputType(getInputType());
if (!isPassword || forAutoFillSave) {
diff --git a/core/java/com/android/internal/logging/LogBuilder.java b/core/java/com/android/internal/logging/LogBuilder.java
index 634d061e79d1..8e2e114be454 100644
--- a/core/java/com/android/internal/logging/LogBuilder.java
+++ b/core/java/com/android/internal/logging/LogBuilder.java
@@ -17,8 +17,9 @@ public class LogBuilder {
private SparseArray<Object> entries = new SparseArray();
- public LogBuilder() {}
-
+ public LogBuilder(int mainCategory) {
+ setCategory(mainCategory);
+ }
public LogBuilder setView(View view) {
entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_VIEW, view.getId());
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index 12d699d2ae15..d82a211446d3 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -24,6 +24,7 @@ import android.system.Os;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.WebViewFactory;
+import android.webkit.WebViewFactoryProvider;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
@@ -74,8 +75,8 @@ class WebViewZygoteInit {
// call preloadInZygote() on it to give it the opportunity to preload the native library
// and perform any other initialisation work that should be shared among the children.
try {
- Class providerClass = Class.forName(WebViewFactory.CHROMIUM_WEBVIEW_FACTORY, true,
- loader);
+ Class<WebViewFactoryProvider> providerClass =
+ WebViewFactory.getWebViewProviderClass(loader);
Object result = providerClass.getMethod("preloadInZygote").invoke(null);
if (!((Boolean)result).booleanValue()) {
Log.e(TAG, "preloadInZygote returned false");
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 293de3d71332..e1e0a21eb7f5 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -85,6 +85,9 @@ public final class Zygote {
* file descriptor numbers that are to be closed by the child
* (and replaced by /dev/null) after forking. An integer value
* of -1 in any entry in the array means "ignore this one".
+ * @param fdsToIgnore null-ok an array of ints, either null or holding
+ * one or more POSIX file descriptor numbers that are to be ignored
+ * in the file descriptor table check.
* @param instructionSet null-ok the instruction set to use.
* @param appDataDir null-ok the data directory of the app.
*
@@ -93,11 +96,11 @@ public final class Zygote {
*/
public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- String instructionSet, String appDataDir) {
+ int[] fdsToIgnore, String instructionSet, String appDataDir) {
VM_HOOKS.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
- instructionSet, appDataDir);
+ fdsToIgnore, instructionSet, appDataDir);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
Trace.setTracingEnabled(true);
@@ -111,7 +114,7 @@ public final class Zygote {
native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int debugFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- String instructionSet, String appDataDir);
+ int[] fdsToIgnore, String instructionSet, String appDataDir);
/**
* Special method to start the system server process. In addition to the
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 345350cc45fb..83e3cff360f4 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -196,11 +196,14 @@ class ZygoteConnection {
rlimits = parsedArgs.rlimits.toArray(intArray2d);
}
+ int[] fdsToIgnore = null;
+
if (parsedArgs.invokeWith != null) {
FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
childPipeFd = pipeFds[1];
serverPipeFd = pipeFds[0];
Os.fcntlInt(childPipeFd, F_SETFD, 0);
+ fdsToIgnore = new int[] { childPipeFd.getInt$(), serverPipeFd.getInt$() };
}
/**
@@ -233,7 +236,7 @@ class ZygoteConnection {
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
- parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
+ parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
parsedArgs.appDataDir);
} catch (ErrnoException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
diff --git a/core/java/com/android/internal/view/BaseSurfaceHolder.java b/core/java/com/android/internal/view/BaseSurfaceHolder.java
index b41ef297a5be..32ce0fe1282b 100644
--- a/core/java/com/android/internal/view/BaseSurfaceHolder.java
+++ b/core/java/com/android/internal/view/BaseSurfaceHolder.java
@@ -86,7 +86,7 @@ public abstract class BaseSurfaceHolder implements SurfaceHolder {
mCallbacks.remove(callback);
}
}
-
+
public SurfaceHolder.Callback[] getCallbacks() {
if (mHaveGottenCallbacks) {
return mGottenCallbacks;
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 4f7b106a83fe..0185e306118f 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -562,8 +562,6 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
}
case DO_COMMIT_CONTENT: {
final int flags = msg.arg1;
- final boolean grantUriPermission =
- (flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0;
SomeArgs args = (SomeArgs) msg.obj;
try {
InputConnection ic = getInputConnection();
@@ -579,22 +577,8 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
args.callback.setCommitContentResult(false, args.seq);
return;
}
- if (grantUriPermission) {
- try {
- inputContentInfo.requestPermission();
- } catch (Exception e) {
- Log.e(TAG, "InputConnectionInfo.requestPermission() failed", e);
- args.callback.setCommitContentResult(false, args.seq);
- return;
- }
- }
final boolean result =
ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2);
- // If this request is not handled, then there is no reason to keep the URI
- // permission.
- if (grantUriPermission && !result) {
- inputContentInfo.releasePermission();
- }
args.callback.setCommitContentResult(result, args.seq);
} catch (RemoteException e) {
Log.w(TAG, "Got RemoteException calling commitContent", e);
diff --git a/core/java/com/android/internal/view/SurfaceCallbackHelper.java b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
new file mode 100644
index 000000000000..5b6a82cf1c43
--- /dev/null
+++ b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 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.view;
+
+import android.os.RemoteException;
+import android.view.IWindow;
+import android.view.IWindowSession;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+public class SurfaceCallbackHelper {
+ IWindowSession mSession;
+ IWindow.Stub mWindow;
+
+ int mFinishDrawingCollected = 0;
+ int mFinishDrawingExpected = 0;
+
+ private Runnable mFinishDrawingRunnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (SurfaceCallbackHelper.this) {
+ mFinishDrawingCollected++;
+ if (mFinishDrawingCollected < mFinishDrawingExpected) {
+ return;
+ }
+ try {
+ mSession.finishDrawing(mWindow);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ };
+
+ public SurfaceCallbackHelper(IWindowSession session,
+ IWindow.Stub window) {
+ mSession = session;
+ mWindow = window;
+ }
+
+ public void dispatchSurfaceRedrawNeededAsync(SurfaceHolder holder, SurfaceHolder.Callback callbacks[]) {
+ if (callbacks == null || callbacks.length == 0) {
+ try {
+ mSession.finishDrawing(mWindow);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+
+ synchronized (this) {
+ mFinishDrawingExpected = callbacks.length;
+ mFinishDrawingCollected = 0;
+ }
+
+ for (SurfaceHolder.Callback c : callbacks) {
+ if (c instanceof SurfaceHolder.Callback2) {
+ ((SurfaceHolder.Callback2) c).surfaceRedrawNeededAsync(
+ holder, mFinishDrawingRunnable);
+ } else {
+ mFinishDrawingRunnable.run();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
index aa6348ee2a31..dcb787462a13 100644
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
@@ -33,7 +33,9 @@
#include "HarfBuzzNGFaceSkia.h"
#include <stdlib.h>
-#include <cutils/log.h>
+
+#include <log/log.h>
+
#include <SkPaint.h>
#include <SkPath.h>
#include <SkPoint.h>
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index aa4570ff8d70..14d7e812d583 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -16,32 +16,32 @@
#define LOG_TAG "android.os.Debug"
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
-#include <time.h>
#include <sys/time.h>
-#include <errno.h>
-#include <assert.h>
-#include <ctype.h>
-#include <malloc.h>
+#include <time.h>
+#include <unistd.h>
#include <iomanip>
#include <string>
-#include "jni.h"
+#include <android-base/stringprintf.h>
+#include <cutils/debugger.h>
+#include <log/log.h>
+#include <utils/misc.h>
+#include <utils/String8.h>
-#include "android-base/stringprintf.h"
-#include "cutils/debugger.h"
-#include "cutils/log.h"
#include "JNIHelp.h"
-#include "memtrack/memtrack.h"
-#include "memunreachable/memunreachable.h"
-#include "utils/misc.h"
-#include "utils/String8.h"
+#include "jni.h"
+#include <memtrack/memtrack.h>
+#include <memunreachable/memunreachable.h>
namespace android
{
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index ea893f0dd1bb..de91f70ee8f5 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -19,15 +19,14 @@
#include <inttypes.h>
+#include <cutils/trace.h>
+#include <utils/String8.h>
+#include <log/log.h>
+
#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) {
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 02fa8720d5d2..c05ef26fd5ac 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -54,7 +54,8 @@ static JLineBreaksID gLineBreaks_fieldID;
// hyphenFrequency)
static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length,
jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
- jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency) {
+ jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency,
+ jboolean isJustified) {
minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
b->resize(length);
env->GetCharArrayRegion(text, 0, length, b->buffer());
@@ -68,6 +69,7 @@ static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray tex
}
b->setStrategy(static_cast<minikin::BreakStrategy>(strategy));
b->setHyphenationFrequency(static_cast<minikin::HyphenationFrequency>(hyphenFrequency));
+ b->setJustified(isJustified);
}
static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
@@ -190,7 +192,7 @@ static const JNINativeMethod gMethods[] = {
{"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
{"nLoadHyphenator", "(Ljava/nio/ByteBuffer;I)J", (void*) nLoadHyphenator},
{"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
- {"nSetupParagraph", "(J[CIFIF[IIII)V", (void*) nSetupParagraph},
+ {"nSetupParagraph", "(J[CIFIF[IIIIZ)V", (void*) nSetupParagraph},
{"nSetIndents", "(J[I)V", (void*) nSetIndents},
{"nAddStyleRun", "(JJJIIZ)F", (void*) nAddStyleRun},
{"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun},
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 5559d48a58b6..abcd1e7049ef 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -29,19 +29,19 @@
#include <sys/types.h>
#include <unistd.h>
-#include <utils/Atomic.h>
#include <binder/IInterface.h>
+#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
-#include <utils/Log.h>
-#include <utils/SystemClock.h>
-#include <utils/List.h>
-#include <utils/KeyedVector.h>
-#include <log/logger.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <utils/threads.h>
+#include <log/log.h>
+#include <utils/Atomic.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/Log.h>
#include <utils/String8.h>
+#include <utils/SystemClock.h>
+#include <utils/threads.h>
#include <ScopedUtfChars.h>
#include <ScopedLocalRef.h>
diff --git a/core/jni/android_util_Log.cpp b/core/jni/android_util_Log.cpp
index 7719e31f4240..20dfe7809728 100644
--- a/core/jni/android_util_Log.cpp
+++ b/core/jni/android_util_Log.cpp
@@ -21,7 +21,7 @@
#include <android-base/macros.h>
#include <assert.h>
#include <cutils/properties.h>
-#include <log/logger.h> // For LOGGER_ENTRY_MAX_PAYLOAD.
+#include <log/log.h> // For LOGGER_ENTRY_MAX_PAYLOAD.
#include <utils/Log.h>
#include <utils/String8.h>
diff --git a/core/jni/android_util_jar_StrictJarFile.cpp b/core/jni/android_util_jar_StrictJarFile.cpp
index 2e31c8b2fc8f..4f1f926c8a22 100644
--- a/core/jni/android_util_jar_StrictJarFile.cpp
+++ b/core/jni/android_util_jar_StrictJarFile.cpp
@@ -20,13 +20,14 @@
#include <memory>
#include <string>
+#include <log/log.h>
+
#include "JNIHelp.h"
#include "JniConstants.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
#include "jni.h"
#include "ziparchive/zip_archive.h"
-#include "cutils/log.h"
namespace android {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index cc7b95894a1f..070a2d96cd40 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -43,6 +43,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include "android-base/logging.h"
#include <cutils/fs.h>
#include <cutils/multiuser.h>
#include <cutils/sched_policy.h>
@@ -440,6 +441,22 @@ void SetThreadName(const char* thread_name) {
// The list of open zygote file descriptors.
static FileDescriptorTable* gOpenFdTable = NULL;
+static void FillFileDescriptorVector(JNIEnv* env,
+ jintArray java_fds,
+ std::vector<int>* fds) {
+ CHECK(fds != nullptr);
+ if (java_fds != nullptr) {
+ ScopedIntArrayRO ar(env, java_fds);
+ if (ar.get() == nullptr) {
+ RuntimeAbort(env, __LINE__, "Bad fd array");
+ }
+ fds->reserve(ar.size());
+ for (size_t i = 0; i < ar.size(); ++i) {
+ fds->push_back(ar[i]);
+ }
+ }
+}
+
// Utility routine to fork zygote and specialize the child process.
static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
jint debug_flags, jobjectArray javaRlimits,
@@ -447,6 +464,7 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
jint mount_external,
jstring java_se_info, jstring java_se_name,
bool is_system_server, jintArray fdsToClose,
+ jintArray fdsToIgnore,
jstring instructionSet, jstring dataDir) {
SetSigChldHandler();
@@ -471,12 +489,14 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
// If this is the first fork for this zygote, create the open FD table.
// If it isn't, we just need to check whether the list of open files has
// changed (and it shouldn't in the normal case).
+ std::vector<int> fds_to_ignore;
+ FillFileDescriptorVector(env, fdsToIgnore, &fds_to_ignore);
if (gOpenFdTable == NULL) {
- gOpenFdTable = FileDescriptorTable::Create();
+ gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore);
if (gOpenFdTable == NULL) {
RuntimeAbort(env, __LINE__, "Unable to construct file descriptor table.");
}
- } else if (!gOpenFdTable->Restat()) {
+ } else if (!gOpenFdTable->Restat(fds_to_ignore)) {
RuntimeAbort(env, __LINE__, "Unable to restat file descriptor table.");
}
@@ -646,7 +666,9 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint debug_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring se_name,
- jintArray fdsToClose, jstring instructionSet, jstring appDataDir) {
+ jintArray fdsToClose,
+ jintArray fdsToIgnore,
+ jstring instructionSet, jstring appDataDir) {
jlong capabilities = 0;
// Grant CAP_WAKE_ALARM to the Bluetooth process.
@@ -681,7 +703,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags,
rlimits, capabilities, capabilities, mount_external, se_info,
- se_name, false, fdsToClose, instructionSet, appDataDir);
+ se_name, false, fdsToClose, fdsToIgnore, instructionSet, appDataDir);
}
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
@@ -692,7 +714,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
debug_flags, rlimits,
permittedCapabilities, effectiveCapabilities,
MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
- NULL, NULL);
+ NULL, NULL, NULL);
if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -759,7 +781,7 @@ static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* en
static const JNINativeMethod gMethods[] = {
{ "nativeForkAndSpecialize",
- "(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",
+ "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I",
(void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
{ "nativeForkSystemServer", "(II[II[[IJJ)I",
(void *) com_android_internal_os_Zygote_nativeForkSystemServer },
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 969d336f3cad..59a536b1b001 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -381,7 +381,7 @@ bool FileDescriptorInfo::DetachSocket() const {
}
// static
-FileDescriptorTable* FileDescriptorTable::Create() {
+FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore) {
DIR* d = opendir(kFdPath);
if (d == NULL) {
ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
@@ -396,6 +396,10 @@ FileDescriptorTable* FileDescriptorTable::Create() {
if (fd == -1) {
continue;
}
+ if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
+ ALOGI("Ignoring open file descriptor %d", fd);
+ continue;
+ }
FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
if (info == NULL) {
@@ -414,7 +418,7 @@ FileDescriptorTable* FileDescriptorTable::Create() {
return new FileDescriptorTable(open_fd_map);
}
-bool FileDescriptorTable::Restat() {
+bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore) {
std::set<int> open_fds;
// First get the list of open descriptors.
@@ -431,6 +435,10 @@ bool FileDescriptorTable::Restat() {
if (fd == -1) {
continue;
}
+ if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
+ ALOGI("Ignoring open file descriptor %d", fd);
+ continue;
+ }
open_fds.insert(fd);
}
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
index 9e3afd910914..03298c38dca9 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -122,9 +122,9 @@ class FileDescriptorTable {
// Creates a new FileDescriptorTable. This function scans
// /proc/self/fd for the list of open file descriptors and collects
// information about them. Returns NULL if an error occurs.
- static FileDescriptorTable* Create();
+ static FileDescriptorTable* Create(const std::vector<int>& fds_to_ignore);
- bool Restat();
+ bool Restat(const std::vector<int>& fds_to_ignore);
// Reopens all file descriptors that are contained in the table. Returns true
// if all descriptors were successfully re-opened or detached, and false if an
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9cbb8c3b0167..89581bbb3ac4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1049,6 +1049,16 @@
Must be in the range specified by minimum and maximum. -->
<integer name="config_screenBrightnessSettingDefault">102</integer>
+ <!-- Default screen brightness for VR setting. -->
+ <integer name="config_screenBrightnessForVrSettingDefault">86</integer>
+
+ <!-- Minimum screen brightness setting allowed for VR. Device panels start increasing pulse
+ width as brightness decreases below this theshold. -->
+ <integer name="config_screenBrightnessForVrSettingMinimum">79</integer>
+
+ <!-- Maximum screen brightness setting allowed for VR. -->
+ <integer name="config_screenBrightnessForVrSettingMaximum">255</integer>
+
<!-- Screen brightness used to dim the screen while dozing in a very low power state.
May be less than the minimum allowed brightness setting
that can be set by the user. -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 5c165e6709ee..55477063a71a 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -129,4 +129,6 @@
<item type="id" name="remote_input_tag" />
<item type="id" name="cross_task_transition" />
+
+ <item type="id" name="accessibilityActionClickOnClickableSpan" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b7f5a9bb3a13..b0d6457720f4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2868,17 +2868,6 @@
<!-- If there is ever a ringtone set for some setting, but that ringtone can no longer be resolved, t his is shown instead. For example, if the ringtone was on a SD card and it had been removed, this woudl be shown for ringtones on that SD card. -->
<string name="ringtone_unknown">Unknown</string>
- <!-- A notification is shown when there are open wireless networks nearby. This is the notification's title. -->
- <plurals name="wifi_available">
- <item quantity="one">Wi-Fi network available</item>
- <item quantity="other">Wi-Fi networks available</item>
- </plurals>
- <!-- A notification is shown when there are open wireless networks nearby. This is the notification's message. -->
- <plurals name="wifi_available_detailed">
- <item quantity="one">Open Wi-Fi network available</item>
- <item quantity="other">Open Wi-Fi networks available</item>
- </plurals>
-
<!-- A notification is shown when a wifi captive portal network is detected. This is the notification's title. -->
<string name="wifi_available_sign_in">Sign in to Wi-Fi network</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cd3c0e358cb5..f44b0397322b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -213,6 +213,7 @@
<java-symbol type="id" name="selection_start_handle" />
<java-symbol type="id" name="selection_end_handle" />
<java-symbol type="id" name="insertion_handle" />
+ <java-symbol type="id" name="accessibilityActionClickOnClickableSpan" />
<java-symbol type="attr" name="actionModeShareDrawable" />
<java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -1803,6 +1804,9 @@
<java-symbol type="integer" name="config_screenBrightnessSettingMinimum" />
<java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
<java-symbol type="integer" name="config_screenBrightnessSettingDefault" />
+ <java-symbol type="integer" name="config_screenBrightnessForVrSettingDefault" />
+ <java-symbol type="integer" name="config_screenBrightnessForVrSettingMaximum" />
+ <java-symbol type="integer" name="config_screenBrightnessForVrSettingMinimum" />
<java-symbol type="integer" name="config_screenBrightnessDark" />
<java-symbol type="integer" name="config_screenBrightnessDim" />
<java-symbol type="integer" name="config_screenBrightnessDoze" />
@@ -1816,8 +1820,6 @@
<java-symbol type="layout" name="safe_mode" />
<java-symbol type="layout" name="simple_list_item_2_single_choice" />
<java-symbol type="layout" name="app_error_dialog" />
- <java-symbol type="plurals" name="wifi_available" />
- <java-symbol type="plurals" name="wifi_available_detailed" />
<java-symbol type="string" name="accessibility_binding_label" />
<java-symbol type="string" name="adb_active_notification_message" />
<java-symbol type="string" name="adb_active_notification_title" />
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index e3a85b562068..504541d9522b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -55,6 +55,8 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.INJECT_EVENTS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.READ_DREAM_STATE" />
+ <uses-permission android:name="android.permission.WRITE_DREAM_STATE" />
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_SMS"/>
@@ -831,14 +833,14 @@
</intent-filter>
</activity>
- <activity android:name="android.widget.listview.ListManagedCursor" android:label="ListManagedCursor">
+ <activity android:name="android.widget.listview.ListManagedCursor" android:label="ListManagedCursor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
</intent-filter>
</activity>
- <activity android:name="android.widget.listview.ListWithEmptyView" android:label="ListWithEmptyView">
+ <activity android:name="android.widget.listview.ListWithEmptyView" android:label="ListWithEmptyView">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
@@ -1331,6 +1333,27 @@
</meta-data>
</service>
+ <activity
+ android:name="android.print.mockservice.SettingsActivity"
+ android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+ android:exported="true">
+ </activity>
+
+ <activity
+ android:name="android.os.TestVrActivity"
+ android:enableVrMode="com.android.frameworks.coretests/android.os.TestVrActivity$TestVrListenerService">
+ </activity>
+
+ <service android:name="android.os.TestVrActivity$TestVrListenerService"
+ android:exported="true"
+ android:enabled="true"
+ android:label="Test Vr Listener Service"
+ android:permission="android.permission.BIND_VR_LISTENER_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.vr.VrListenerService" />
+ </intent-filter>
+ </service>
+
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/core/tests/coretests/src/android/os/PowerManagerVrTest.java b/core/tests/coretests/src/android/os/PowerManagerVrTest.java
new file mode 100644
index 000000000000..e01e5fa4faee
--- /dev/null
+++ b/core/tests/coretests/src/android/os/PowerManagerVrTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017 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.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.service.dreams.IDreamManager;
+import android.service.dreams.DreamService;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * Tests dream aspects of PowerManager.
+ */
+@MediumTest
+public class PowerManagerVrTest extends ActivityInstrumentationTestCase2<TestVrActivity> {
+ private PowerManager mPm;
+ private IDreamManager mDm;
+ private String mOldVrListener;
+
+ public PowerManagerVrTest() {
+ super(TestVrActivity.class);
+ }
+
+ /**
+ * Setup any common data for the upcoming tests.
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ Context context = getInstrumentation().getTargetContext();
+ mPm = (PowerManager) getInstrumentation().getTargetContext().getSystemService(
+ Context.POWER_SERVICE);
+ mDm = IDreamManager.Stub.asInterface(
+ ServiceManager.getService(DreamService.DREAM_SERVICE));
+
+ mOldVrListener = setTestVrListener(new ComponentName(
+ context, TestVrActivity.TestVrListenerService.class).flattenToString());
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (mDm != null) {
+ mDm.awaken(); // Don't leave the device in the dream state.
+ }
+
+ setTestVrListener(mOldVrListener);
+ }
+
+ /**
+ * Confirm that the setup is good.
+ *
+ * @throws Exception
+ */
+ @SmallTest
+ public void testPreconditions() throws Exception {
+ assertNotNull(mPm);
+ assertNotNull(mDm);
+ }
+
+ /**
+ * Confirm that the system prevents napping while in VR.
+ * Dreaming is controlled by PowerManager, but we use dreamManager to access those features
+ * in order to not require DEVICE_POWER permissions which other tests expect not to have.
+ *
+ * @throws Exception
+ */
+ @SmallTest
+ public void testNap() throws Exception {
+ // For dream to work, we need to wake up the system
+ wakeUpDevice();
+
+ mDm.dream();
+ waitForDreamState(true);
+ assertTrue(mDm.isDreaming());
+ mDm.awaken();
+
+ // awaken() is not immediate so we have to wait for dreaming to stop
+ // before continuing with the test.
+ waitForDreamState(false);
+
+ // set VR Mode to true by starting our VR Activity, then retest the dream.
+ TestVrActivity activity = getActivity();
+ assertTrue(activity.waitForActivityStart());
+
+ try {
+ mDm.dream();
+ waitForDreamState(true); // wait for dream to turn true with a timeout
+ assertFalse(mDm.isDreaming()); // ensure dream is still false after waiting.
+ mDm.awaken();
+ } finally {
+ activity.finish();
+ }
+ }
+
+ /**
+ * Waits synchronously for the system to be set to the specified dream state.
+ */
+ private void waitForDreamState(boolean isDreaming) throws Exception {
+ final int MAX_ATTEMPTS = 10;
+ final int WAIT_TIME_PER_ATTEMPT_MILLIS = 100;
+ for (int i = 0; i < MAX_ATTEMPTS; i++) {
+ if (mDm.isDreaming() == isDreaming) {
+ break;
+ }
+ Thread.sleep(WAIT_TIME_PER_ATTEMPT_MILLIS);
+ }
+ }
+
+ private void wakeUpDevice() {
+ PowerManager.WakeLock wl = mPm.newWakeLock(
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP
+ | PowerManager.ON_AFTER_RELEASE, "FULL_WAKE_LOCK");
+ wl.acquire();
+ wl.release();
+ }
+
+ /**
+ * Sets a new value for the enabled VrListenerService and returns the previous value.
+ */
+ private String setTestVrListener(String newValue) {
+ final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
+ Context context = getInstrumentation().getTargetContext();
+ ContentResolver cr = context.getContentResolver();
+ String oldVrListeners = Settings.Secure.getString(cr, ENABLED_VR_LISTENERS);
+ Settings.Secure.putString(cr, ENABLED_VR_LISTENERS, newValue);
+ return oldVrListeners;
+ }
+}
diff --git a/core/tests/coretests/src/android/os/TestVrActivity.java b/core/tests/coretests/src/android/os/TestVrActivity.java
new file mode 100644
index 000000000000..33ff1645b37b
--- /dev/null
+++ b/core/tests/coretests/src/android/os/TestVrActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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.app.Activity;
+import android.os.Bundle;
+import android.service.vr.VrListenerService;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An activity for enabling/disabling VrMode.
+ */
+public class TestVrActivity extends Activity {
+ private CountDownLatch mLatch;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mLatch = new CountDownLatch(1);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mLatch.countDown();
+ }
+
+ public static class TestVrListenerService extends VrListenerService {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ }
+ }
+
+ public boolean waitForActivityStart() {
+ boolean result = false;
+ try {
+ result = mLatch.await(2L, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ }
+ return result;
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java
new file mode 100644
index 000000000000..1e55fb182b4a
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2017 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.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.view.ViewGroup.OnHierarchyChangeListener;
+
+import com.android.frameworks.coretests.R;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for AppWidgetHostView
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AppWidgetHostViewTest {
+
+ @Rule
+ public final ExpectedException exception = ExpectedException.none();
+
+ private Context mContext;
+ private String mPackage;
+ private AppWidgetHostView mHostView;
+
+ private ViewAddListener mViewAddListener;
+ private RemoteViews mViews;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getContext();
+ mPackage = mContext.getPackageName();
+ mHostView = new AppWidgetHostView(mContext);
+ mHostView.setAppWidget(0, AppWidgetManager.getInstance(
+ mContext).getInstalledProviders().get(0));
+
+ mViewAddListener = new ViewAddListener();
+ mHostView.setOnHierarchyChangeListener(mViewAddListener);
+
+ mViews = new RemoteViews(mPackage, R.layout.remote_views_test);
+ }
+
+ @Test
+ public void syncInflation() {
+ mHostView.updateAppWidget(mViews);
+ assertNotNull(mHostView.findViewById(R.id.image));
+ }
+
+ @Test
+ public void asyncInflation() throws Exception {
+ RunnableList executor = new RunnableList();
+ mHostView.setAsyncExecutor(executor);
+
+ mHostView.updateAppWidget(mViews);
+ assertNull(mHostView.findViewById(R.id.image));
+
+ // Task queued.
+ assertEquals(1, executor.size());
+
+ // Execute the pending task
+ executor.get(0).run();
+ mViewAddListener.addLatch.await();
+ assertNotNull(mHostView.findViewById(R.id.image));
+ }
+
+ @Test
+ public void asyncInflation_cancelled() throws Exception {
+ RunnableList executor = new RunnableList();
+ mHostView.setAsyncExecutor(executor);
+
+ mHostView.updateAppWidget(mViews.clone());
+ mHostView.updateAppWidget(mViews.clone());
+ assertNull(mHostView.findViewById(R.id.image));
+
+ // Tasks queued.
+ assertEquals(2, executor.size());
+ // First task cancelled
+ assertTrue(((Future) executor.get(0)).isCancelled());
+
+ // Execute the pending task
+ executor.get(0).run();
+ executor.get(1).run();
+ mViewAddListener.addLatch.await();
+ assertNotNull(mHostView.findViewById(R.id.image));
+ }
+
+ private static class RunnableList extends ArrayList<Runnable> implements Executor {
+
+ @Override
+ public void execute(Runnable runnable) {
+ add(runnable);
+ }
+ }
+
+ private class ViewAddListener implements OnHierarchyChangeListener {
+
+ public final CountDownLatch addLatch = new CountDownLatch(1);
+
+
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ addLatch.countDown();
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java b/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java
index e7d23a8edb9c..e3f754ca8925 100644
--- a/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java
+++ b/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java
@@ -5,7 +5,7 @@ import junit.framework.TestCase;
public class LogBuilderTest extends TestCase {
public void testSerialize() {
- LogBuilder builder = new LogBuilder();
+ LogBuilder builder = new LogBuilder(0);
builder.addTaggedData(1, "one");
builder.addTaggedData(2, "two");
Object[] out = builder.serialize();
@@ -16,7 +16,7 @@ public class LogBuilderTest extends TestCase {
}
public void testInvalidInputThrows() {
- LogBuilder builder = new LogBuilder();
+ LogBuilder builder = new LogBuilder(0);
boolean threw = false;
try {
builder.addTaggedData(0, new Object());
@@ -28,7 +28,7 @@ public class LogBuilderTest extends TestCase {
}
public void testValidInputTypes() {
- LogBuilder builder = new LogBuilder();
+ LogBuilder builder = new LogBuilder(0);
builder.addTaggedData(1, "onetwothree");
builder.addTaggedData(2, 123);
builder.addTaggedData(3, 123L);
diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp
index ba4a4ff4fe63..76a430ee9f0e 100644
--- a/libs/androidfw/BackupData.cpp
+++ b/libs/androidfw/BackupData.cpp
@@ -16,14 +16,13 @@
#define LOG_TAG "backup_data"
-#include <androidfw/BackupHelpers.h>
-#include <utils/ByteOrder.h>
-
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include <cutils/log.h>
+#include <androidfw/BackupHelpers.h>
+#include <log/log.h>
+#include <utils/ByteOrder.h>
namespace android {
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index 78e9d91c4d67..8bfe2b6a259a 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -18,23 +18,22 @@
#include <androidfw/BackupHelpers.h>
-#include <utils/KeyedVector.h>
-#include <utils/ByteOrder.h>
-#include <utils/String8.h>
-
#include <errno.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/stat.h>
-#include <sys/time.h> // for utimes
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h> // for utimes
+#include <sys/uio.h>
#include <unistd.h>
#include <utime.h>
-#include <fcntl.h>
#include <zlib.h>
-#include <cutils/log.h>
+#include <log/log.h>
+#include <utils/ByteOrder.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
namespace android {
diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h
index 8a2979a3756d..f54356576551 100644
--- a/libs/androidfw/include/androidfw/CursorWindow.h
+++ b/libs/androidfw/include/androidfw/CursorWindow.h
@@ -17,11 +17,11 @@
#ifndef _ANDROID__DATABASE_WINDOW_H
#define _ANDROID__DATABASE_WINDOW_H
-#include <cutils/log.h>
#include <stddef.h>
#include <stdint.h>
#include <binder/Parcel.h>
+#include <log/log.h>
#include <utils/String8.h>
#if LOG_NDEBUG
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index 9d5860ca0d1a..f5bb821f4e23 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -142,7 +142,7 @@ void AnimatorManager::animateNoDamage(TreeInfo& info) {
}
uint32_t AnimatorManager::animateCommon(TreeInfo& info) {
- uint32_t dirtyMask;
+ uint32_t dirtyMask = 0;
AnimateFunctor functor(info, mAnimationHandle->context(), &dirtyMask);
auto newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
mAnimators.erase(newEnd, mAnimators.end());
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 6d5833b3be86..2b4fe17d424d 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -16,7 +16,7 @@
#include "DamageAccumulator.h"
-#include <cutils/log.h>
+#include <log/log.h>
#include "RenderNode.h"
#include "utils/MathUtils.h"
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 700642ed7334..d180ba51b304 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -13,16 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#include <DeviceInfo.h>
#include "Extensions.h"
-#include <GLES2/gl2.h>
-#include <log/log.h>
-
#include <thread>
#include <mutex>
+#include <log/log.h>
+
+#include <GLES2/gl2.h>
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h
index f94fe2862a31..18e2330668b0 100644
--- a/libs/hwui/GpuMemoryTracker.h
+++ b/libs/hwui/GpuMemoryTracker.h
@@ -15,10 +15,11 @@
*/
#pragma once
-#include <cutils/log.h>
#include <pthread.h>
#include <ostream>
+#include <log/log.h>
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index f94a22d51d9f..d740c038f36d 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -16,10 +16,11 @@
#include "Interpolator.h"
-#include "utils/MathUtils.h"
-
#include <algorithm>
-#include <cutils/log.h>
+
+#include <log/log.h>
+
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index ed6b211eef1b..0a9bf5450091 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -15,19 +15,21 @@
*/
#include "JankTracker.h"
-#include "Properties.h"
-#include "utils/TimeUtils.h"
+#include <errno.h>
+#include <inttypes.h>
#include <algorithm>
-#include <cutils/ashmem.h>
-#include <cutils/log.h>
+#include <cmath>
#include <cstdio>
-#include <errno.h>
-#include <inttypes.h>
#include <limits>
-#include <cmath>
#include <sys/mman.h>
+#include <cutils/ashmem.h>
+#include <log/log.h>
+
+#include "Properties.h"
+#include "utils/TimeUtils.h"
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h
index 9536bc807fbc..77d5e413cb36 100644
--- a/libs/hwui/PixelBuffer.h
+++ b/libs/hwui/PixelBuffer.h
@@ -18,7 +18,8 @@
#define ANDROID_HWUI_PIXEL_BUFFER_H
#include <GLES3/gl3.h>
-#include <cutils/log.h>
+
+#include <log/log.h>
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 848161e44604..a7663817721d 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "Properties.h"
+#include "Properties.h"
#include "Debug.h"
-#include <cutils/compiler.h>
-#include <cutils/log.h>
-#include <cutils/properties.h>
-
#include <algorithm>
#include <cstdlib>
+#include <log/log.h>
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index c5156cfd6844..f32612d21319 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -16,9 +16,11 @@
#include "SkiaCanvasProxy.h"
-#include "hwui/Bitmap.h"
+#include <memory>
+
+#include <log/log.h>
-#include <cutils/log.h>
+#include "hwui/Bitmap.h"
#include <SkLatticeIter.h>
#include <SkPatchUtils.h>
#include <SkPaint.h>
@@ -30,8 +32,6 @@
#include <SkSurface.h>
#include <SkTextBlobRunIterator.h>
-#include <memory>
-
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index f172473d1652..956f66ed22cb 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -16,9 +16,10 @@
#include "MinikinSkia.h"
+#include <log/log.h>
+
#include <SkPaint.h>
#include <SkTypeface.h>
-#include <cutils/log.h>
namespace android {
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 8dd165c46d21..713e5099da26 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -13,15 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#include "MinikinUtils.h"
+#include <string>
+
+#include <log/log.h>
+
#include "Paint.h"
#include "SkPathMeasure.h"
#include "Typeface.h"
-#include <cutils/log.h>
-#include <string>
-
namespace android {
minikin::FontStyle MinikinUtils::prepareMinikinPaint(minikin::MinikinPaint* minikinPaint,
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
index 94818b2dc44f..c12747805293 100644
--- a/libs/hwui/utils/GLUtils.h
+++ b/libs/hwui/utils/GLUtils.h
@@ -18,7 +18,7 @@
#include "Debug.h"
-#include <cutils/log.h>
+#include <log/log.h>
namespace android {
namespace uirenderer {
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 0b22ad5ba470..7c6046789cdc 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -15,7 +15,6 @@
*/
#define LOG_TAG "PointerController"
-
//#define LOG_NDEBUG 0
// Log debug messages about pointer updates
@@ -23,8 +22,9 @@
#include "PointerController.h"
-#include <cutils/log.h>
+#include <log/log.h>
+// ToDo: Fix code to be warning free
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <SkBitmap.h>
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 18ebd47558bc..4991f0434bc2 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -15,15 +15,15 @@
*/
#define LOG_TAG "Sprites"
-
//#define LOG_NDEBUG 0
#include "SpriteController.h"
-#include <cutils/log.h>
+#include <log/log.h>
#include <utils/String8.h>
#include <gui/Surface.h>
+// ToDo: Fix code to be warning free
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <SkBitmap.h>
diff --git a/media/mca/filterpacks/native/base/geometry.cpp b/media/mca/filterpacks/native/base/geometry.cpp
index 7812d5020c3f..44b13e4fff15 100644
--- a/media/mca/filterpacks/native/base/geometry.cpp
+++ b/media/mca/filterpacks/native/base/geometry.cpp
@@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "geometry"
-#include <cutils/log.h>
#include <cmath>
+#include <log/log.h>
+
#include "geometry.h"
namespace android {
diff --git a/media/mca/filterpacks/native/base/time_util.cpp b/media/mca/filterpacks/native/base/time_util.cpp
index 1a78a953de69..7d383df5aea2 100644
--- a/media/mca/filterpacks/native/base/time_util.cpp
+++ b/media/mca/filterpacks/native/base/time_util.cpp
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "time_util"
#include "time_util.h"
#include "utilities.h"
-#include <cutils/log.h>
#include <sys/time.h>
#include <map>
+#include <log/log.h>
+
namespace android {
namespace filterfw {
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 4154ef02772b..329514c0b7fc 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -542,16 +542,27 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
}
/**
- * This method is part of the SurfaceHolder.Callback interface, and is
+ * This method is part of the SurfaceHolder.Callback2 interface, and is
* not normally called or subclassed by clients of GLSurfaceView.
*/
@Override
- public void surfaceRedrawNeeded(SurfaceHolder holder) {
+ public void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable finishDrawing) {
if (mGLThread != null) {
- mGLThread.requestRenderAndWait();
+ mGLThread.requestRenderAndNotify(finishDrawing);
}
}
+ /**
+ * This method is part of the SurfaceHolder.Callback2 interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ @Deprecated
+ @Override
+ public void surfaceRedrawNeeded(SurfaceHolder holder) {
+ // Since we are part of the framework we know only surfaceRedrawNeededAsync
+ // will be called.
+ }
+
/**
* Pause the rendering thread, optionally tearing down the EGL context
@@ -1305,6 +1316,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
int w = 0;
int h = 0;
Runnable event = null;
+ Runnable finishDrawingRunnable = null;
while (true) {
synchronized (sGLThreadManager) {
@@ -1400,6 +1412,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
sGLThreadManager.notifyAll();
}
+ if (mFinishDrawingRunnable != null) {
+ finishDrawingRunnable = mFinishDrawingRunnable;
+ mFinishDrawingRunnable = null;
+ }
+
// Ready to draw?
if (readyToDraw()) {
@@ -1453,7 +1470,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
break;
}
}
-
// By design, this is the only place in a GLThread thread where we wait().
if (LOG_THREADS) {
Log.i("GLThread", "waiting tid=" + getId()
@@ -1546,6 +1562,10 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame");
view.mRenderer.onDrawFrame(gl);
+ if (finishDrawingRunnable != null) {
+ finishDrawingRunnable.run();
+ finishDrawingRunnable = null;
+ }
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
@@ -1625,7 +1645,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
}
}
- public void requestRenderAndWait() {
+ public void requestRenderAndNotify(Runnable finishDrawing) {
synchronized(sGLThreadManager) {
// If we are already on the GL thread, this means a client callback
// has caused reentrancy, for example via updating the SurfaceView parameters.
@@ -1638,17 +1658,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
mWantRenderNotification = true;
mRequestRender = true;
mRenderComplete = false;
+ mFinishDrawingRunnable = finishDrawing;
sGLThreadManager.notifyAll();
-
- while (!mExited && !mPaused && !mRenderComplete && ableToDraw()) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
- }
-
}
}
@@ -1821,6 +1833,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
private boolean mRenderComplete;
private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
private boolean mSizeChanged = true;
+ private Runnable mFinishDrawingRunnable = null;
// End of member variables protected by the sGLThreadManager monitor.
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 8cf375a47ae6..cbd295e1f85e 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -287,26 +287,23 @@ public class ExternalStorageProvider extends DocumentsProvider {
String path = file.getAbsolutePath();
// Find the most-specific root path
- String mostSpecificId = null;
- String mostSpecificPath = null;
- synchronized (mRootsLock) {
- for (int i = 0; i < mRoots.size(); i++) {
- final String rootId = mRoots.keyAt(i);
- final String rootPath = mRoots.valueAt(i).path.getAbsolutePath();
- if (path.startsWith(rootPath) && (mostSpecificPath == null
- || rootPath.length() > mostSpecificPath.length())) {
- mostSpecificId = rootId;
- mostSpecificPath = rootPath;
- }
- }
+ boolean visiblePath = false;
+ RootInfo mostSpecificRoot = getMostSpecificRootForPath(path, false);
+
+ if (mostSpecificRoot == null) {
+ // Try visible path if no internal path matches. MediaStore uses visible paths.
+ visiblePath = true;
+ mostSpecificRoot = getMostSpecificRootForPath(path, true);
}
- if (mostSpecificPath == null) {
+ if (mostSpecificRoot == null) {
throw new FileNotFoundException("Failed to find root that contains " + path);
}
// Start at first char of path under root
- final String rootPath = mostSpecificPath;
+ final String rootPath = visiblePath
+ ? mostSpecificRoot.visiblePath.getAbsolutePath()
+ : mostSpecificRoot.path.getAbsolutePath();
if (rootPath.equals(path)) {
path = "";
} else if (rootPath.endsWith("/")) {
@@ -322,7 +319,29 @@ public class ExternalStorageProvider extends DocumentsProvider {
}
}
- return mostSpecificId + ':' + path;
+ return mostSpecificRoot.rootId + ':' + path;
+ }
+
+ private RootInfo getMostSpecificRootForPath(String path, boolean visible) {
+ // Find the most-specific root path
+ RootInfo mostSpecificRoot = null;
+ String mostSpecificPath = null;
+ synchronized (mRootsLock) {
+ for (int i = 0; i < mRoots.size(); i++) {
+ final RootInfo root = mRoots.valueAt(i);
+ final File rootFile = visible ? root.visiblePath : root.path;
+ if (rootFile != null) {
+ final String rootPath = rootFile.getAbsolutePath();
+ if (path.startsWith(rootPath) && (mostSpecificPath == null
+ || rootPath.length() > mostSpecificPath.length())) {
+ mostSpecificRoot = root;
+ mostSpecificPath = rootPath;
+ }
+ }
+ }
+ }
+
+ return mostSpecificRoot;
}
private File getFileForDocId(String docId) throws FileNotFoundException {
@@ -519,15 +538,13 @@ public class ExternalStorageProvider extends DocumentsProvider {
boolean matchesRequestedDoc = false;
if (DocumentsContract.isTreeUri(uri)) {
final String parentDocId = DocumentsContract.getTreeDocumentId(uri);
- File parentFile = getFileForDocId(parentDocId);
- if (FileUtils.contains(parentFile, doc)) {
+ if (isChildDocument(parentDocId, docId)) {
treeUriPermission = uriPermission;
matchesRequestedDoc = true;
}
} else {
final String candidateDocId = DocumentsContract.getDocumentId(uri);
- final File candidateDoc = getFileForDocId(candidateDocId);
- if (Objects.equals(doc.getAbsolutePath(), candidateDoc.getAbsolutePath())) {
+ if (Objects.equals(docId, candidateDocId)) {
docUriPermission = uriPermission;
matchesRequestedDoc = true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
index de762791a0a9..320494c68faf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
@@ -41,4 +41,9 @@ public class StorageManagerVolumeProvider implements StorageVolumeProvider {
public List<VolumeInfo> getVolumes() {
return mStorageManager.getVolumes();
}
+
+ @Override
+ public VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume) {
+ return mStorageManager.findEmulatedForPrivate(privateVolume);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
index 95bb18d19188..646c42f05a8e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
@@ -34,4 +34,9 @@ public interface StorageVolumeProvider {
* Returns a list of VolumeInfos for the device.
*/
List<VolumeInfo> getVolumes();
+
+ /**
+ * Returns the emulated volume for a given private volume.
+ */
+ VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume);
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5574753bee6c..62f6136026df 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -483,6 +483,21 @@
android:exported="true"
android:enabled="@bool/config_enableKeyguardService" />
+ <activity android:name=".keyguard.WorkLockActivity"
+ android:label="@string/accessibility_desc_work_lock"
+ android:permission="android.permission.MANAGE_USERS"
+ android:exported="false"
+ android:launchMode="singleTop"
+ android:excludeFromRecents="true"
+ android:stateNotNeeded="true"
+ android:resumeWhilePausing="true"
+ android:theme="@android:style/Theme.Black.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.app.action.CONFIRM_DEVICE_CREDENTIAL_WITH_USER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".Somnambulator"
android:label="@string/start_dreams"
android:icon="@mipmap/ic_launcher_dreams"
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw900dp-hdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-ldrtl-sw900dp-hdpi/ic_sysbar_back_dark.png
new file mode 100644
index 000000000000..c3aea469d1cf
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-sw900dp-hdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw900dp-mdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-ldrtl-sw900dp-mdpi/ic_sysbar_back_dark.png
new file mode 100644
index 000000000000..470b3e2c21df
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-sw900dp-mdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back.png
index 94cb03237ac9..965d2f5f4063 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back_dark.png
new file mode 100644
index 000000000000..e7137c68f752
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw900dp-xxhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xxhdpi/ic_sysbar_back_dark.png
new file mode 100644
index 000000000000..03dec1566ff9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xxhdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw900dp-xxxhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xxxhdpi/ic_sysbar_back_dark.png
new file mode 100644
index 000000000000..e1ca853959ca
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xxxhdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_dark.png
new file mode 100644
index 000000000000..0520c493af27
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_ime_dark.png b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_ime_dark.png
new file mode 100644
index 000000000000..3dc2b8804447
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_ime_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 000000000000..2ba5d5b150fb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_home_dark.png
new file mode 100644
index 000000000000..62dc2d9a9eff
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_home_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_menu_dark.png
new file mode 100644
index 000000000000..e718b7ad1848
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_menu_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_recent_dark.png
new file mode 100644
index 000000000000..469800a34e12
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_recent_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-land-hdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-land-hdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 000000000000..42863c634ade
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-land-hdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-land-mdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-land-mdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 000000000000..d9ec9d5d71b7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-land-mdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked.png b/packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked.png
new file mode 100644
index 000000000000..ea7ac9361471
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 000000000000..8ee4e43ab78f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-land-xxhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-land-xxhdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 000000000000..dfa8a974f50f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-land-xxhdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-land-xxxhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-land-xxxhdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 000000000000..d23abc7f4971
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-land-xxxhdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_dark.png
new file mode 100644
index 000000000000..8e56e9cf7fa4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_ime_dark.png b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_ime_dark.png
new file mode 100644
index 000000000000..28a1465efa18
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_ime_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 000000000000..0fc9677c0301
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_home_dark.png
new file mode 100644
index 000000000000..7b81eeea67f8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_home_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_menu_dark.png
new file mode 100644
index 000000000000..a6483452601e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_menu_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_recent_dark.png
new file mode 100644
index 000000000000..7f7452107d94
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_recent_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back.png
index fbdc93c17ee3..3b831a3cd619 100644
--- a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_dark.png
new file mode 100644
index 000000000000..8c9945518464
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_ime.png
index 419518c099f3..3c377827f516 100644
--- a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked.png
new file mode 100644
index 000000000000..b2baa099aca6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 000000000000..b78dc9cb395f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home.png
index a2406b1408c1..37590a5381b6 100644
--- a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home_dark.png
new file mode 100644
index 000000000000..3a208aa0ea46
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_menu_dark.png
new file mode 100644
index 000000000000..625ba45235b3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_menu_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent.png
index c449449541f4..7ad2a29cce6a 100644
--- a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent_dark.png
new file mode 100644
index 000000000000..604397e9728e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_back_dark.png
new file mode 100644
index 000000000000..274e5df76fb4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 000000000000..ac1689524020
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_home_dark.png
new file mode 100644
index 000000000000..e69a03790bbc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_home_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_ime_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_ime_dark.png
new file mode 100644
index 000000000000..65a43544732b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_ime_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_menu_dark.png
new file mode 100644
index 000000000000..8bddcd9e754e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_menu_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_recent_dark.png
new file mode 100644
index 000000000000..38c5959566f3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_recent_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_dark.png
new file mode 100644
index 000000000000..c6d7c98a5e1e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_ime_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_ime_dark.png
new file mode 100644
index 000000000000..06c52aa43d71
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_ime_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 000000000000..766099774e52
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_home_dark.png
new file mode 100644
index 000000000000..0d9ecc2a73e2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_home_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_menu_dark.png
new file mode 100644
index 000000000000..d6bb8b151f0d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_menu_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_recent_dark.png
new file mode 100644
index 000000000000..298ef3ac7193
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_recent_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index ca0248e066eb..04d0e6524eac 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -27,7 +27,7 @@
<android.support.v7.widget.RecyclerView
android:id="@android:id/list"
- android:layout_width="@dimen/notification_panel_width"
+ android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollIndicators="top"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 34a0397eca8b..05963ac9ff30 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -460,6 +460,8 @@
<string name="accessibility_desc_settings">Settings</string>
<!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_recent_apps">Overview.</string>
+ <!-- Content description for the graphic shown instead of an activity window while the activity is locked (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_work_lock">Work lock screen</string>
<!-- Content description for the close button in the zen mode panel introduction message. [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_close">Close</string>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8e5db97dd27d..ce89aab30911 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -326,6 +326,12 @@ public class KeyguardViewMediator extends SystemUI {
*/
private boolean mPendingLock;
+ /**
+ * Controller for showing individual "work challenge" lock screen windows inside managed profile
+ * tasks when the current user has been unlocked but the profile is still locked.
+ */
+ private WorkLockActivityController mWorkLockController;
+
private boolean mLockLater;
private boolean mWakeAndUnlocking;
@@ -708,6 +714,8 @@ public class KeyguardViewMediator extends SystemUI {
mHideAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.lock_screen_behind_enter);
+
+ mWorkLockController = new WorkLockActivityController(mContext);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
new file mode 100644
index 000000000000..12cf6fa2db20
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 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.keyguard;
+
+import static android.app.ActivityManager.TaskDescription;
+
+import android.annotation.ColorInt;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.View;
+
+/**
+ * Bouncer between work activities and the activity used to confirm credentials before unlocking
+ * a managed profile.
+ * <p>
+ * Shows a solid color when started, based on the organization color of the user it is supposed to
+ * be blocking. Once focused, it switches to a screen to confirm credentials and auto-dismisses if
+ * credentials are accepted.
+ */
+public class WorkLockActivity extends Activity {
+ private static final String TAG = "WorkLockActivity";
+
+ /**
+ * ID of the locked user that this activity blocks access to.
+ */
+ @UserIdInt
+ private int mUserId;
+
+ /**
+ * {@see KeyguardManager}
+ */
+ private KeyguardManager mKgm;
+
+ /**
+ * {@see DevicePolicyManager}
+ */
+ private DevicePolicyManager mDpm;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
+ mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mKgm = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+
+ final IntentFilter lockFilter = new IntentFilter();
+ lockFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
+ registerReceiverAsUser(mLockEventReceiver, UserHandle.ALL, lockFilter,
+ /* permission */ null, /* scheduler */ null);
+
+ // Once the receiver is registered, check whether anything happened between now and the time
+ // when this activity was launched. If it did and the user is unlocked now, just quit.
+ if (!mKgm.isDeviceLocked(mUserId)) {
+ finish();
+ return;
+ }
+
+ // Get the organization color; this is a 24-bit integer provided by a DPC, guaranteed to
+ // be completely opaque.
+ final @ColorInt int color = mDpm.getOrganizationColorForUser(mUserId);
+
+ // Draw captions overlaid on the content view, so the whole window is one solid color.
+ setOverlayWithDecorCaptionEnabled(true);
+
+ // Match task description to the task stack we are replacing so it's still recognizably the
+ // original task stack with the same icon and title text.
+ setTaskDescription(new TaskDescription(null, null, color));
+
+ // Blank out the activity. When it is on-screen it will look like a Recents thumbnail with
+ // redaction switched on.
+ final View blankView = new View(this);
+ blankView.setBackgroundColor(color);
+ setContentView(blankView);
+
+ // Respond to input events by showing the prompt to confirm credentials.
+ blankView.setOnClickListener((View v) -> {
+ showConfirmCredentialActivity();
+ });
+ }
+
+ @Override
+ public void onDestroy() {
+ unregisterReceiver(mLockEventReceiver);
+ super.onDestroy();
+ }
+
+ @Override
+ public void onBackPressed() {
+ // Ignore back presses.
+ return;
+ }
+
+ private final BroadcastReceiver mLockEventReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, mUserId);
+ if (userId == mUserId && !mKgm.isDeviceLocked(mUserId)) {
+ finish();
+ }
+ }
+ };
+
+ private void showConfirmCredentialActivity() {
+ if (isFinishing() || !mKgm.isDeviceLocked(mUserId)) {
+ // Don't show the confirm credentials screen if we are already unlocked / unlocking.
+ return;
+ }
+
+ final Intent credential = mKgm.createConfirmDeviceCredentialIntent(null, null, mUserId);
+ if (credential == null) {
+ return;
+ }
+
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchTaskId(getTaskId());
+
+ // Bring this activity back to the foreground after confirming credentials.
+ final PendingIntent target = PendingIntent.getActivity(this, /* request */ -1, getIntent(),
+ PendingIntent.FLAG_CANCEL_CURRENT |
+ PendingIntent.FLAG_ONE_SHOT |
+ PendingIntent.FLAG_IMMUTABLE, options.toBundle());
+
+ credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender());
+ try {
+ ActivityManager.getService().startConfirmDeviceCredentialIntent(credential);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to start confirm credential intent", e);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
new file mode 100644
index 000000000000..22fceffd127f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 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.keyguard;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.KeyguardManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
+
+public class WorkLockActivityController {
+ private final Context mContext;
+
+ public WorkLockActivityController(Context context) {
+ mContext = context;
+ EventBus.getDefault().register(this);
+ SystemServicesProxy.getInstance(context).registerTaskStackListener(mLockListener);
+ }
+
+ private void startWorkChallengeInTask(int taskId, int userId) {
+ Intent intent = new Intent(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER)
+ .setComponent(new ComponentName(mContext, WorkLockActivity.class))
+ .putExtra(Intent.EXTRA_USER_ID, userId)
+ .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchTaskId(taskId);
+ options.setTaskOverlay(true);
+ mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
+ }
+
+ private final TaskStackListener mLockListener = new TaskStackListener() {
+ @Override
+ public void onTaskProfileLocked(int taskId, int userId) {
+ startWorkChallengeInTask(taskId, userId);
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index d86aebfd0255..416c7cef8e5d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -47,13 +47,11 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements
public FlashlightTile(Host host) {
super(host);
mFlashlightController = host.getFlashlightController();
- mFlashlightController.addCallback(this);
}
@Override
protected void handleDestroy() {
super.handleDestroy();
- mFlashlightController.removeCallback(this);
}
@Override
@@ -63,6 +61,11 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements
@Override
public void setListening(boolean listening) {
+ if (listening) {
+ mFlashlightController.addCallback(this);
+ } else {
+ mFlashlightController.removeCallback(this);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index ddffea2446a4..05df63467c02 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -154,6 +154,7 @@ public class SystemServicesProxy {
public void onPinnedStackAnimationEnded() { }
public void onActivityForcedResizable(String packageName, int taskId) { }
public void onActivityDismissingDockedStack() { }
+ public void onTaskProfileLocked(int taskId, int userId) { }
}
/**
@@ -197,6 +198,11 @@ public class SystemServicesProxy {
public void onActivityDismissingDockedStack() throws RemoteException {
mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
}
+
+ @Override
+ public void onTaskProfileLocked(int taskId, int userId) {
+ mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
+ }
};
/**
@@ -1155,6 +1161,7 @@ public class SystemServicesProxy {
private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4;
private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5;
private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 6;
+ private static final int ON_TASK_PROFILE_LOCKED = 7;
@Override
public void handleMessage(Message msg) {
@@ -1196,6 +1203,12 @@ public class SystemServicesProxy {
}
break;
}
+ case ON_TASK_PROFILE_LOCKED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2);
+ }
+ break;
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index e8039c35bb4f..3059a0537b75 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -30,6 +30,9 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
+import android.util.Log;
import android.widget.ImageView;
import com.android.internal.logging.MetricsLogger;
@@ -52,9 +55,12 @@ public class BrightnessController implements ToggleSlider.Listener {
private static final int MSG_SET_CHECKED = 2;
private static final int MSG_ATTACH_LISTENER = 3;
private static final int MSG_DETACH_LISTENER = 4;
+ private static final int MSG_VR_MODE_CHANGED = 5;
private final int mMinimumBacklight;
private final int mMaximumBacklight;
+ private final int mMinimumBacklightForVr;
+ private final int mMaximumBacklightForVr;
private final Context mContext;
private final ImageView mIcon;
@@ -62,6 +68,7 @@ public class BrightnessController implements ToggleSlider.Listener {
private final boolean mAutomaticAvailable;
private final IPowerManager mPower;
private final CurrentUserTracker mUserTracker;
+ private final IVrManager mVrManager;
private Handler mBackgroundHandler;
private final BrightnessObserver mBrightnessObserver;
@@ -69,7 +76,8 @@ public class BrightnessController implements ToggleSlider.Listener {
private ArrayList<BrightnessStateChangeCallback> mChangeCallbacks =
new ArrayList<BrightnessStateChangeCallback>();
- private volatile boolean mAutomatic;
+ private volatile boolean mAutomatic; // Brightness adjusted automatically using ambient light.
+ private volatile boolean mIsVrModeEnabled;
private boolean mListening;
private boolean mExternalChange;
@@ -84,6 +92,8 @@ public class BrightnessController implements ToggleSlider.Listener {
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
private final Uri BRIGHTNESS_URI =
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
+ private final Uri BRIGHTNESS_FOR_VR_URI =
+ Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR);
private final Uri BRIGHTNESS_ADJ_URI =
Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ);
@@ -105,6 +115,8 @@ public class BrightnessController implements ToggleSlider.Listener {
mBackgroundHandler.post(mUpdateSliderRunnable);
} else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) {
mBackgroundHandler.post(mUpdateSliderRunnable);
+ } else if (BRIGHTNESS_FOR_VR_URI.equals(uri)) {
+ mBackgroundHandler.post(mUpdateSliderRunnable);
} else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {
mBackgroundHandler.post(mUpdateSliderRunnable);
} else {
@@ -126,6 +138,9 @@ public class BrightnessController implements ToggleSlider.Listener {
BRIGHTNESS_URI,
false, this, UserHandle.USER_ALL);
cr.registerContentObserver(
+ BRIGHTNESS_FOR_VR_URI,
+ false, this, UserHandle.USER_ALL);
+ cr.registerContentObserver(
BRIGHTNESS_ADJ_URI,
false, this, UserHandle.USER_ALL);
}
@@ -191,7 +206,14 @@ public class BrightnessController implements ToggleSlider.Listener {
private final Runnable mUpdateSliderRunnable = new Runnable() {
@Override
public void run() {
- if (mAutomatic) {
+ if (mIsVrModeEnabled) {
+ int value = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mMaximumBacklight,
+ UserHandle.USER_CURRENT);
+ mHandler.obtainMessage(MSG_UPDATE_SLIDER,
+ mMaximumBacklightForVr - mMinimumBacklightForVr,
+ value - mMinimumBacklightForVr).sendToTarget();
+ } else if (mAutomatic) {
float value = Settings.System.getFloatForUser(mContext.getContentResolver(),
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0,
UserHandle.USER_CURRENT);
@@ -208,6 +230,14 @@ public class BrightnessController implements ToggleSlider.Listener {
}
};
+ private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+ @Override
+ public void onVrStateChanged(boolean enabled) {
+ mHandler.obtainMessage(MSG_VR_MODE_CHANGED, enabled ? 1 : 0, 0)
+ .sendToTarget();
+ }
+ };
+
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -230,6 +260,9 @@ public class BrightnessController implements ToggleSlider.Listener {
case MSG_DETACH_LISTENER:
mControl.setOnChangedListener(null);
break;
+ case MSG_VR_MODE_CHANGED:
+ updateVrMode(msg.arg1 != 0);
+ break;
default:
super.handleMessage(msg);
}
@@ -256,10 +289,13 @@ public class BrightnessController implements ToggleSlider.Listener {
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mMinimumBacklight = pm.getMinimumScreenBrightnessSetting();
mMaximumBacklight = pm.getMaximumScreenBrightnessSetting();
+ mMinimumBacklightForVr = pm.getMinimumScreenBrightnessForVrSetting();
+ mMaximumBacklightForVr = pm.getMaximumScreenBrightnessForVrSetting();
mAutomaticAvailable = context.getResources().getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);
mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
+ mVrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager"));
}
public void setBackgroundLooper(Looper backgroundLooper) {
@@ -284,6 +320,15 @@ public class BrightnessController implements ToggleSlider.Listener {
return;
}
+ if (mVrManager != null) {
+ try {
+ mVrManager.registerListener(mVrStateCallbacks);
+ mIsVrModeEnabled = mVrManager.getVrModeState();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register VR mode state listener: ", e);
+ }
+ }
+
mBackgroundHandler.post(mStartListeningRunnable);
mListening = true;
}
@@ -294,6 +339,14 @@ public class BrightnessController implements ToggleSlider.Listener {
return;
}
+ if (mVrManager != null) {
+ try {
+ mVrManager.unregisterListener(mVrStateCallbacks);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to unregister VR mode state listener: ", e);
+ }
+ }
+
mBackgroundHandler.post(mStopListeningRunnable);
mListening = false;
}
@@ -304,7 +357,22 @@ public class BrightnessController implements ToggleSlider.Listener {
updateIcon(mAutomatic);
if (mExternalChange) return;
- if (!mAutomatic) {
+ if (mIsVrModeEnabled) {
+ final int val = value + mMinimumBacklightForVr;
+ if (stopTracking) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_BRIGHTNESS_FOR_VR, val);
+ }
+ setBrightness(val);
+ if (!tracking) {
+ AsyncTask.execute(new Runnable() {
+ public void run() {
+ Settings.System.putIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_VR, val,
+ UserHandle.USER_CURRENT);
+ }
+ });
+ }
+ } else if (!mAutomatic) {
final int val = value + mMinimumBacklight;
if (stopTracking) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_BRIGHTNESS, val);
@@ -368,4 +436,11 @@ public class BrightnessController implements ToggleSlider.Listener {
com.android.systemui.R.drawable.ic_qs_brightness_auto_off);
}
}
+
+ private void updateVrMode(boolean isEnabled) {
+ if (mIsVrModeEnabled != isEnabled) {
+ mIsVrModeEnabled = isEnabled;
+ mBackgroundHandler.post(mUpdateSliderRunnable);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
index 008d837da51c..f0cfa2c40931 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
@@ -121,6 +121,8 @@ public class FlashlightControllerImpl implements FlashlightController {
}
cleanUpListenersLocked(l);
mListeners.add(new WeakReference<>(l));
+ l.onFlashlightAvailabilityChanged(mTorchAvailable);
+ l.onFlashlightChanged(mFlashlightEnabled);
}
}
diff --git a/proto/src/ipconnectivity.proto b/proto/src/ipconnectivity.proto
index cf372bc90c74..b5afc402b7ab 100644
--- a/proto/src/ipconnectivity.proto
+++ b/proto/src/ipconnectivity.proto
@@ -17,14 +17,26 @@ message NetworkId {
optional int32 network_id = 1;
};
-// Transport describes a physical technology used by a network. It is a subset
-// of the TRANSPORT_* constants defined in android.net.NetworkCapabilities.
-enum Transport {
+// LinkLayer describes a physical link layer technology used by a network.
+// It is not intended to map one to one to the TRANSPORT_* constants defined in
+// android.net.NetworkCapabilities. Instead it is intended to be used as
+// a dimension field for metrics events and aggregated metrics.
+// Next tag: 7
+enum LinkLayer {
+ // An unknown link layer technology.
UNKNOWN = 0;
+
BLUETOOTH = 1;
CELLULAR = 2;
ETHERNET = 3;
WIFI = 4;
+
+ // Indicates that the link layer dimension is not relevant for the metrics or
+ // event considered.
+ NONE = 5;
+
+ // Indicates that the metrics or event considered may involve several links.
+ MULTIPLE = 6;
};
// A pair of (key, value) integers for describing histogram-like statistics.
@@ -65,7 +77,7 @@ message DefaultNetworkEvent {
// This message is associated to android.net.metrics.IpReachabilityEvent.
message IpReachabilityEvent {
// The interface name (wlan, rmnet, lo, ...) on which the probe was sent.
- // Deprecated since version 2, replaced by transport field.
+ // Deprecated since version 2, to be replaced by link_layer field.
optional string if_name = 1 [deprecated = true];
// The event type code of the probe, represented by constants defined in
@@ -179,7 +191,7 @@ message ConnectStatistics {
// android.net.metrics.DhcpErrorEvent.
message DHCPEvent {
// The interface name (wlan, rmnet, lo, ...) on which the event happened.
- // Deprecated since version 2, replaced by transport field.
+ // Deprecated since version 2, to be replaced by link_layer field.
optional string if_name = 1 [deprecated = true];
oneof value {
@@ -284,7 +296,7 @@ message RaEvent {
// This message is associated to android.net.metrics.IpManagerEvent.
message IpProvisioningEvent {
// The interface name (wlan, rmnet, lo, ...) on which the probe was sent.
- // Deprecated since version 2, replaced by transport field.
+ // Deprecated since version 2, to be replaced by link_layer field.
optional string if_name = 1 [deprecated = true];
// The code of the IP provisioning event, represented by constants defined in
@@ -296,19 +308,21 @@ message IpProvisioningEvent {
}
// Represents one of the IP connectivity event defined in this file.
-// Next tag: 15
+// Next tag: 16
message IpConnectivityEvent {
// Time in ms when the event was recorded.
optional int64 time_ms = 1;
- // Physical transport of the network on which the event happened.
+ // Physical link layer of the network on which the event happened.
+ // Acts as a dimension key.
// Since version 2.
- optional Transport transport = 12;
+ optional LinkLayer link_layer = 15;
// Event type.
oneof event {
// An event about the system default network.
+ // The link_layer field is not relevant for this event and set to NONE.
DefaultNetworkEvent default_network_event = 2;
// An IP reachability probe event.
@@ -318,7 +332,8 @@ message IpConnectivityEvent {
NetworkEvent network_event = 4;
// A batch of DNS lookups.
- // Deprecated in the nyc-mr2 release since version 2, and replaced by dns_latencies.
+ // Deprecated in the nyc-mr2 release since version 2,and replaced by
+ // dns_latencies.
DNSLookupBatch dns_lookup_batch = 5 [deprecated = true];
// DNS lookup latency statistics.
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 57c45654da9d..21139da7900f 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3105,6 +3105,12 @@ message MetricsEvent {
// PACKAGE: The package name of the app the permission was revoked for
ACTION_PERMISSION_REVOKE_READ_PHONE_NUMBER = 739;
+ // ACTION: QS Brightness Slider (with auto brightness disabled, and VR enabled)
+ // SUBTYPE: slider value
+ // CATEGORY: QUICK_SETTINGS
+ // OS: 6.0
+ ACTION_BRIGHTNESS_FOR_VR = 498;
+
// ACTION: A captive portal was detected during network validation
// CATEGORY: NOTIFICATION
// OS: N-MR2
@@ -3175,6 +3181,11 @@ message MetricsEvent {
// user accepted
ACTION_SKIP_DISCLAIMER_SELECTED = 760;
+ // Enclosing category for group of APP_TRANSITION_FOO events,
+ // logged when we execute an app transition.
+ APP_TRANSITION = 761;
+
+
// ---- End O Constants, all O constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index 6a1613113c10..87eaf29fb306 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -18,28 +18,23 @@ package com.android.server.autofill;
import static android.Manifest.permission.MANAGE_AUTO_FILL;
import static android.content.Context.AUTO_FILL_MANAGER_SERVICE;
-import static android.view.View.ASSIST_FLAG_SANITIZED_TEXT;
-import static android.view.View.ASSIST_FLAG_NON_SANITIZED_TEXT;
+import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
+import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
+
+import static com.android.server.autofill.AutoFillUI.MSG_SHOW_ALL_NOTIFICATIONS;
+import static com.android.server.autofill.AutoFillUI.SHOW_ALL_NOTIFICATIONS_DELAY_MS;
import android.Manifest;
import android.app.AppGlobals;
-import android.app.Notification;
-import android.app.Notification.Action;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-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.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -48,7 +43,6 @@ import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.Settings;
import android.service.autofill.IAutoFillManagerService;
import android.text.TextUtils;
@@ -65,7 +59,6 @@ import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.List;
/**
* Entry point service for auto-fill management.
@@ -86,6 +79,7 @@ public final class AutoFillManagerService extends SystemService {
protected static final int MSG_UNBIND = 1;
private final AutoFillManagerServiceStub mServiceStub;
+ private final AutoFillUI mUi;
private final Context mContext;
private final ContentResolver mResolver;
@@ -99,7 +93,7 @@ public final class AutoFillManagerService extends SystemService {
removeStaleServiceForUser(msg.arg1);
return;
case MSG_SHOW_ALL_NOTIFICATIONS:
- showAllNotifications();
+ mUi.showAllNotifications();
return;
default:
Slog.w(TAG, "Invalid message: " + msg);
@@ -129,6 +123,7 @@ public final class AutoFillManagerService extends SystemService {
super(context);
mContext = context;
+ mUi = new AutoFillUI(context, this, mLock);
mResolver = context.getContentResolver();
mServiceStub = new AutoFillManagerServiceStub();
}
@@ -176,8 +171,9 @@ public final class AutoFillManagerService extends SystemService {
Slog.w(TAG, "no service info for " + serviceComponent);
return null;
}
- return new AutoFillManagerServiceImpl(this, mContext, mLock, FgThread.getHandler(), userId,
- serviceInfo.applicationInfo.uid, serviceComponent, SERVICE_BINDING_LIFETIME_MS);
+ return new AutoFillManagerServiceImpl(this, mUi, mContext, mLock, FgThread.getHandler(),
+ userId, serviceInfo.applicationInfo.uid, serviceComponent,
+ SERVICE_BINDING_LIFETIME_MS);
}
/**
@@ -186,7 +182,8 @@ public final class AutoFillManagerService extends SystemService {
* <p>First it tries to return the existing instance from the cache; if it's not cached, it
* creates a new instance and caches it.
*/
- private AutoFillManagerServiceImpl getServiceForUserLocked(int userId) {
+ // TODO(b/33197203): make private once AutoFillUi does not uses notifications
+ AutoFillManagerServiceImpl getServiceForUserLocked(int userId) {
AutoFillManagerServiceImpl service = mServicesCache.get(userId);
if (service != null) {
if (DEBUG) Log.d(TAG, "reusing cached service for userId " + userId);
@@ -251,14 +248,14 @@ public final class AutoFillManagerService extends SystemService {
final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub {
@Override
- public void requestAutoFill(IBinder activityToken, int userId, int flags) {
+ public void requestAutoFill(IBinder activityToken, int userId, Bundle extras, int flags) {
if (DEBUG) Slog.d(TAG, "requestAutoFill: flags=" + flags + ", userId=" + userId);
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
synchronized (mLock) {
final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
if (service != null) {
- service.requestAutoFill(activityToken, flags);
+ service.requestAutoFill(activityToken, extras, flags);
}
}
}
@@ -310,147 +307,8 @@ public final class AutoFillManagerService extends SystemService {
if (DEBUG) Slog.d(TAG, "settings (" + uri + " changed for " + userId);
synchronized (mLock) {
removeCachedServiceForUserLocked(userId);
- final ComponentName serviceComponent = getProviderForUser(userId);
- if (serviceComponent == null) {
- cancelNotificationLocked(userId);
- } else {
- showNotification(serviceComponent, userId);
- }
- }
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // TODO: temporary code using a notification to request auto-fill. //
- // Will be removed once UX decide the right way to present it to the user //
- ////////////////////////////////////////////////////////////////////////////
-
- // TODO: remove from frameworks/base/core/res/AndroidManifest.xml once it's not used anymore
- private static final String NOTIFICATION_AUTO_FILL_INTENT =
- "com.android.internal.autofill.action.REQUEST_AUTOFILL";
- private static final String EXTRA_USER_ID = "user_id";
- private static final String EXTRA_FLAGS = "flags";
-
- private static final int MSG_SHOW_ALL_NOTIFICATIONS = 42;
- private static final int SHOW_ALL_NOTIFICATIONS_DELAY_MS = 5000;
-
- private BroadcastReceiver mNotificationReceiver;
-
- final class NotificationReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- final int userId = intent.getIntExtra(EXTRA_USER_ID, -1);
- final int flags = intent.getIntExtra(EXTRA_FLAGS, 0);
- if (DEBUG) Slog.d(TAG, "Requesting autofill by notification for user " + userId);
- synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
- if (service == null) {
- Slog.w(TAG, "no auto-fill service for user " + userId);
- } else {
- service.requestAutoFill(null, flags);
- }
- }
- }
- }
-
- private ComponentName getProviderForUser(int userId) {
- ComponentName serviceComponent = null;
- ServiceInfo serviceInfo = null;
- final String componentName = Settings.Secure.getStringForUser(
- mResolver, Settings.Secure.AUTO_FILL_SERVICE, userId);
- if (!TextUtils.isEmpty(componentName)) {
- try {
- serviceComponent = ComponentName.unflattenFromString(componentName);
- serviceInfo =
- AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, userId);
- } catch (RuntimeException | RemoteException e) {
- Slog.wtf(TAG, "Bad auto-fill service name " + componentName, e);
- return null;
+ mUi.updateNotification(userId);
}
}
-
- if (DEBUG) Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component="
- + serviceComponent + ", info: " + serviceInfo);
- if (serviceInfo == null) {
- Slog.w(TAG, "no service info for " + serviceComponent);
- return null;
- }
- return serviceComponent;
}
-
- private void showAllNotifications() {
- final UserManager userManager =
- (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-
- final List<UserInfo> allUsers = userManager.getUsers(true);
-
- for (UserInfo user : allUsers) {
- final ComponentName serviceComponent = getProviderForUser(user.id);
- if (serviceComponent != null) {
- showNotification(serviceComponent, user.id);
- }
- }
- }
-
- private void showNotification(ComponentName serviceComponent, int userId) {
- if (DEBUG) Log.d(TAG, "showNotification() for " + userId + ": " + serviceComponent);
-
- synchronized (mLock) {
- if (mNotificationReceiver == null) {
- mNotificationReceiver = new NotificationReceiver();
- mContext.registerReceiver(mNotificationReceiver,
- new IntentFilter(NOTIFICATION_AUTO_FILL_INTENT));
- }
- }
-
- final Intent fillIntent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
- fillIntent.putExtra(EXTRA_USER_ID, userId);
- fillIntent.putExtra(EXTRA_FLAGS, ASSIST_FLAG_SANITIZED_TEXT);
- final PendingIntent fillPendingIntent = PendingIntent.getBroadcast(mContext,
- ASSIST_FLAG_SANITIZED_TEXT, fillIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- final Action fillAction = new Action.Builder(null, "FILL", fillPendingIntent).build();
-
- final Intent saveIntent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
- saveIntent.putExtra(EXTRA_USER_ID, userId);
- saveIntent.putExtra(EXTRA_FLAGS, ASSIST_FLAG_NON_SANITIZED_TEXT);
- final PendingIntent savePendingIntent = PendingIntent.getBroadcast(mContext,
- ASSIST_FLAG_NON_SANITIZED_TEXT, saveIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- final Action saveAction = new Action.Builder(null, "SAVE", savePendingIntent).build();
-
- final String packageName = serviceComponent.getPackageName();
- String providerName = null;
- final PackageManager pm = mContext.getPackageManager();
- try {
- final ApplicationInfo info = pm.getApplicationInfoAsUser(packageName, 0, userId);
- if (info != null) {
- providerName = pm.getApplicationLabel(info).toString();
- }
- } catch (Exception e) {
- providerName = packageName;
- }
- final String title = "AutoFill actions";
- final String subTitle = "Provider: " + providerName + "\n" + "User: " + userId;
-
- final Notification notification = new Notification.Builder(mContext)
- .setCategory(Notification.CATEGORY_SYSTEM)
- .setOngoing(true)
- .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
- .setLocalOnly(true)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setContentTitle(title)
- .setStyle(new Notification.BigTextStyle().bigText(subTitle))
- .setActions(fillAction, saveAction)
- .build();
- NotificationManager.from(mContext).notify(userId, notification);
- }
-
- private void cancelNotificationLocked(int userId) {
- if (DEBUG) Log.d(TAG, "cancelNotificationLocked(): " + userId);
- NotificationManager.from(mContext).cancel(userId);
- }
-
- /////////////////////////////////////////
- // End of temporary notification code. //
- /////////////////////////////////////////
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index 82356c8458b3..3de8a8bac511 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -18,9 +18,12 @@ package com.android.server.autofill;
import static com.android.server.autofill.AutoFillManagerService.DEBUG;
+import android.annotation.Nullable;
+import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.IActivityManager;
+import android.app.assist.AssistStructure;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -29,6 +32,7 @@ import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.icu.text.DateFormat;
+import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
@@ -37,16 +41,24 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.service.autofill.AutoFillService;
import android.service.autofill.AutoFillServiceInfo;
+import android.service.autofill.IAutoFillAppCallback;
+import android.service.autofill.IAutoFillServerCallback;
import android.service.autofill.IAutoFillService;
-import android.util.Log;
+import android.service.voice.VoiceInteractionSession;
import android.util.PrintWriterPrinter;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
+import android.view.autofill.AutoFillId;
+import android.view.autofill.Dataset;
+import android.view.autofill.FillResponse;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
import com.android.server.LocalServices;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
@@ -60,6 +72,9 @@ final class AutoFillManagerServiceImpl {
private static final String TAG = "AutoFillManagerServiceImpl";
+ /** Used do assign ids to new ServerCallback instances. */
+ private static int sServerCallbackCounter = 0;
+
private final int mUserId;
private final int mUid;
private final ComponentName mComponent;
@@ -68,6 +83,7 @@ final class AutoFillManagerServiceImpl {
private final Object mLock;
private final AutoFillServiceInfo mInfo;
private final AutoFillManagerService mManagerService;
+ private final AutoFillUI mUi;
// TODO(b/33197203): improve its usage
// - set maximum number of entries
@@ -89,10 +105,19 @@ final class AutoFillManagerServiceImpl {
}
};
+ /**
+ * Cache of pending ServerCallbacks, keyed by {@link ServerCallback#id}.
+ *
+ * <p>They're kept until the AutoFillService handles a request, or an error occurs.
+ */
+ // TODO(b/33197203): need to make sure service is bound while callback is pending
+ @GuardedBy("mLock")
+ private static final SparseArray<ServerCallback> mServerCallbacks = new SparseArray<>();
+
private final ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) Log.d(TAG, "onServiceConnected():" + name);
+ if (DEBUG) Slog.d(TAG, "onServiceConnected():" + name);
synchronized (mLock) {
mService = IAutoFillService.Stub.asInterface(service);
try {
@@ -102,17 +127,18 @@ final class AutoFillManagerServiceImpl {
return;
}
if (!mQueuedRequests.isEmpty()) {
- if (DEBUG) Log.d(TAG, "queued requests:" + mQueuedRequests.size());
+ if (DEBUG) Slog.d(TAG, "queued requests:" + mQueuedRequests.size());
}
for (final QueuedRequest request: mQueuedRequests) {
- requestAutoFillLocked(request.activityToken, request.flags, false);
+ requestAutoFillLocked(request.activityToken, request.extras, request.flags,
+ false);
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) Log.d(TAG, name + " disconnected");
+ if (DEBUG) Slog.d(TAG, name + " disconnected");
synchronized (mLock) {
mService = null;
mManagerService.removeCachedServiceForUserLocked(mUserId);
@@ -120,6 +146,39 @@ final class AutoFillManagerServiceImpl {
}
};
+
+ /**
+ * Receiver of assist data from the app's {@link Activity}, uses the {@code resultData} as
+ * the {@link ServerCallback#id}.
+ */
+ private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) throws RemoteException {
+ if (DEBUG) Slog.d(TAG, "resultCode on mAssistReceiver: " + resultCode);
+
+ final IBinder appBinder = resultData.getBinder(AutoFillService.KEY_CALLBACK);
+ if (appBinder == null) {
+ Slog.w(TAG, "no app callback on mAssistReceiver's resultData");
+ return;
+ }
+ final AssistStructure structure = resultData
+ .getParcelable(VoiceInteractionSession.KEY_STRUCTURE);
+ final Bundle data = resultData.getBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS);
+ final int flags = resultData.getInt(VoiceInteractionSession.KEY_FLAGS, 0);
+
+ final ServerCallback serverCallback;
+ synchronized (mLock) {
+ serverCallback = mServerCallbacks.get(resultCode);
+ if (serverCallback == null) {
+ Slog.w(TAG, "no server callback for id " + resultCode);
+ return;
+ }
+ serverCallback.appCallback = IAutoFillAppCallback.Stub.asInterface(appBinder);
+ }
+ mService.autoFill(structure, serverCallback, serverCallback.extras, flags);
+ }
+ };
+
@GuardedBy("mLock")
private IAutoFillService mService;
private boolean mBound;
@@ -128,9 +187,11 @@ final class AutoFillManagerServiceImpl {
// Estimated time when the service will be evicted from the cache.
long mEstimateTimeOfDeath;
- AutoFillManagerServiceImpl(AutoFillManagerService managerService, Context context, Object lock,
- Handler handler, int userId, int uid,ComponentName component, long ttl) {
+ AutoFillManagerServiceImpl(AutoFillManagerService managerService, AutoFillUI ui,
+ Context context, Object lock, Handler handler, int userId, int uid,
+ ComponentName component, long ttl) {
mManagerService = managerService;
+ mUi = ui;
mContext = context;
mLock = lock;
mUserId = userId;
@@ -180,7 +241,14 @@ final class AutoFillManagerServiceImpl {
if (DEBUG) Slog.d(TAG, "Bound to " + mComponent);
}
- void requestAutoFill(IBinder activityToken, int flags) {
+ /**
+ * Asks service to auto-fill an activity.
+ *
+ * @param activityToken activity token
+ * @param extras bundle to be passed to the {@link AutoFillService} method.
+ * @param flags optional flags.
+ */
+ void requestAutoFill(@Nullable IBinder activityToken, @Nullable Bundle extras, int flags) {
synchronized (mLock) {
if (!mBound) {
Slog.w(TAG, "requestAutoFill() failed because it's not bound to service");
@@ -211,21 +279,26 @@ final class AutoFillManagerServiceImpl {
DateFormat.getDateTimeInstance().format(new Date()) + " - " + activityToken;
synchronized (mLock) {
mRequestHistory.add(historyItem);
- requestAutoFillLocked(activityToken, flags, true);
+ requestAutoFillLocked(activityToken, extras, flags, true);
}
}
- private void requestAutoFillLocked(IBinder activityToken, int flags, boolean queueIfNecessary) {
+ private void requestAutoFillLocked(IBinder activityToken, @Nullable Bundle extras, int flags,
+ boolean queueIfNecessary) {
if (mService == null) {
if (!queueIfNecessary) {
Slog.w(TAG, "requestAutoFillLocked(): service is null");
return;
}
if (DEBUG) Slog.d(TAG, "requestAutoFill(): service not set yet, queuing it");
- mQueuedRequests.add(new QueuedRequest(activityToken, flags));
+ mQueuedRequests.add(new QueuedRequest(activityToken, extras, flags));
return;
}
+ final int callbackId = ++sServerCallbackCounter;
+ final ServerCallback serverCallback = new ServerCallback(callbackId, extras);
+ mServerCallbacks.put(callbackId, serverCallback);
+
/*
* TODO(b/33197203): apply security checks below:
* - checks if disabled by secure settings / device policy
@@ -235,8 +308,7 @@ final class AutoFillManagerServiceImpl {
*/
try {
// TODO(b/33197203): add MetricsLogger call
- if (!mAm.requestAutoFillData(mService.getAssistReceiver(), null, activityToken,
- flags)) {
+ if (!mAm.requestAutoFillData(mAssistReceiver, null, callbackId, activityToken, flags)) {
// TODO(b/33197203): might need a way to warn user (perhaps a new method on
// AutoFillService).
Slog.w(TAG, "failed to request auto-fill data for " + activityToken);
@@ -251,7 +323,7 @@ final class AutoFillManagerServiceImpl {
// Sanity check.
if (mService == null) {
- Log.w(TAG, "service already null on shutdown");
+ Slog.w(TAG, "service already null on shutdown");
return;
}
try {
@@ -273,6 +345,44 @@ final class AutoFillManagerServiceImpl {
}
}
+ /**
+ * Called by {@link AutoFillUI} to fill an activity after the user selected a dataset.
+ */
+ void autoFillApp(int callbackId, Dataset dataset) {
+ // TODO(b/33197203): add MetricsLogger call
+
+ if (dataset == null) {
+ Slog.w(TAG, "autoFillApp(): no dataset for callback id " + callbackId);
+ return;
+ }
+
+ final ServerCallback serverCallback;
+ synchronized (mLock) {
+ serverCallback = mServerCallbacks.get(callbackId);
+ if (serverCallback == null) {
+ Slog.w(TAG, "autoFillApp(): no server callback with id " + callbackId);
+ return;
+ }
+ if (serverCallback.appCallback == null) {
+ Slog.w(TAG, "autoFillApp(): no app callback for server callback " + callbackId);
+ return;
+ }
+ // TODO(b/33197203): use a handler?
+ try {
+ if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
+ serverCallback.appCallback.autoFill(dataset);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error auto-filling activity: " + e);
+ }
+ removeServerCallbackLocked(callbackId);
+ }
+ }
+
+ void removeServerCallbackLocked(int id) {
+ if (DEBUG) Slog.d(TAG, "Removing " + id + " from server callbacks");
+ mServerCallbacks.remove(id);
+ }
+
void dumpLocked(String prefix, PrintWriter pw) {
if (!mValid) {
pw.print(" NOT VALID: ");
@@ -284,6 +394,8 @@ final class AutoFillManagerServiceImpl {
return;
}
+ final String prefix2 = prefix + " ";
+
pw.print(prefix); pw.print("mUserId="); pw.println(mUserId);
pw.print(prefix); pw.print("mUid="); pw.println(mUid);
pw.print(prefix); pw.print("mComponent="); pw.println(mComponent.flattenToShortString());
@@ -303,7 +415,6 @@ final class AutoFillManagerServiceImpl {
pw.print(prefix); pw.println("No history");
} else {
pw.print(prefix); pw.println("History:");
- final String prefix2 = prefix + prefix;
for (int i = 0; i < mRequestHistory.size(); i++) {
pw.print(prefix2); pw.print(i); pw.print(": "); pw.println(mRequestHistory.get(i));
}
@@ -312,11 +423,28 @@ final class AutoFillManagerServiceImpl {
pw.print(prefix); pw.println("No queued requests");
} else {
pw.print(prefix); pw.println("Queued requests:");
- final String prefix2 = prefix + prefix;
for (int i = 0; i < mQueuedRequests.size(); i++) {
pw.print(prefix2); pw.print(i); pw.print(": "); pw.println(mQueuedRequests.get(i));
}
}
+
+ pw.print(prefix); pw.print("sServerCallbackCounter="); pw.println(sServerCallbackCounter);
+ final int size = mServerCallbacks.size();
+ if (size == 0) {
+ pw.print(prefix); pw.println("No server callbacks");
+ } else {
+ pw.print(prefix); pw.print(size); pw.println(" server callbacks:");
+ for (int i = 0; i < size; i++) {
+ pw.print(prefix2); pw.print(mServerCallbacks.keyAt(i));
+ final ServerCallback callback = mServerCallbacks.valueAt(i);
+ if (callback.appCallback == null) {
+ pw.println("(no appCallback)");
+ } else {
+ pw.print(" (app callback: "); pw.print(callback.appCallback) ; pw.println(")");
+ }
+ }
+ pw.println();
+ }
}
@Override
@@ -327,10 +455,12 @@ final class AutoFillManagerServiceImpl {
private static final class QueuedRequest {
final IBinder activityToken;
+ final Bundle extras;
final int flags;
- QueuedRequest(IBinder activityToken, int flags) {
+ QueuedRequest(IBinder activityToken, Bundle extras, int flags) {
this.activityToken = activityToken;
+ this.extras = extras;
this.flags = flags;
}
@@ -339,4 +469,54 @@ final class AutoFillManagerServiceImpl {
return "flags: " + flags + " token: " + activityToken;
}
}
+
+ /**
+ * A bridge between the {@link AutoFillService} implementation and the activity being
+ * auto-filled (represented through the {@link IAutoFillAppCallback}).
+ */
+ private final class ServerCallback extends IAutoFillServerCallback.Stub {
+
+ private final int id;
+ private final Bundle extras;
+ private IAutoFillAppCallback appCallback;
+
+ private ServerCallback(int id, Bundle extras) {
+ this.id = id;
+ this.extras = extras;
+ }
+
+ @Override
+ public void showResponse(FillResponse response) {
+ // TODO(b/33197203): add MetricsLogger call
+ if (DEBUG) Slog.d(TAG, "showResponse(): " + response);
+
+ mUi.showOptions(mUserId, id, response);
+ }
+
+ @Override
+ public void showError(String message) {
+ // TODO(b/33197203): add MetricsLogger call
+ if (DEBUG) Slog.d(TAG, "showError(): " + message);
+
+ mUi.showError(message);
+
+ removeSelf();
+ }
+
+ @Override
+ public void highlightSavedFields(AutoFillId[] ids) {
+ // TODO(b/33197203): add MetricsLogger call
+ if (DEBUG) Slog.d(TAG, "showSaved(): " + Arrays.toString(ids));
+
+ mUi.highlightSavedFields(ids);
+
+ removeSelf();
+ }
+
+ private void removeSelf() {
+ synchronized (mLock) {
+ removeServerCallbackLocked(id);
+ }
+ }
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
index aa3503b9a6d3..26f24519ffc8 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
@@ -16,10 +16,11 @@
package com.android.server.autofill;
-import static android.view.View.ASSIST_FLAG_SANITIZED_TEXT;
-import static android.view.View.ASSIST_FLAG_NON_SANITIZED_TEXT;
+import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
+import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
import android.app.ActivityManager;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
@@ -44,9 +45,9 @@ public final class AutoFillManagerServiceShellCommand extends ShellCommand {
try {
switch (cmd) {
case "fill":
- return requestAutoFill(ASSIST_FLAG_SANITIZED_TEXT);
+ return requestAutoFill(AUTO_FILL_FLAG_TYPE_FILL);
case "save":
- return requestAutoFill(ASSIST_FLAG_NON_SANITIZED_TEXT);
+ return requestAutoFill(AUTO_FILL_FLAG_TYPE_SAVE);
default:
return handleDefaultCommands(cmd);
}
@@ -73,7 +74,7 @@ public final class AutoFillManagerServiceShellCommand extends ShellCommand {
private int requestAutoFill(int flags) throws RemoteException {
final int userId = getUserIdFromArgs();
- mService.requestAutoFill(null, userId, flags);
+ mService.requestAutoFill(null, userId, null, flags);
return 0;
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
new file mode 100644
index 000000000000..08e81d36f9cf
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2016 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.autofill;
+
+import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
+import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
+
+import static com.android.server.autofill.AutoFillManagerService.DEBUG;
+
+import android.app.Activity;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.app.Notification.Action;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+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.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.service.autofill.AutoFillService;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.view.autofill.Dataset;
+import android.view.autofill.AutoFillId;
+import android.view.autofill.FillResponse;
+import android.widget.Toast;
+
+import com.android.server.UiThread;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Handles all auto-fill related UI tasks.
+ */
+// TODO(b/33197203): document exactly what once the auto-fill bar is implemented
+final class AutoFillUI {
+
+ private static final String TAG = "AutoFillUI";
+
+ private final Context mContext;
+
+ AutoFillUI(Context context, AutoFillManagerService service, Object lock) {
+ mContext = context;
+ mResolver = context.getContentResolver();
+ mService = service;
+ mLock = lock;
+ }
+
+ /**
+ * Displays an error message to the user.
+ */
+ void showError(String message) {
+ // TODO(b/33197203): proper implementation
+ UiThread.getHandler().runWithScissors(() -> {
+ Toast.makeText(mContext, "AutoFill error: " + message, Toast.LENGTH_LONG).show();
+ }, 0);
+ }
+
+ /**
+ * Highlights in the {@link Activity} the fields saved by the service.
+ */
+ void highlightSavedFields(AutoFillId[] ids) {
+ // TODO(b/33197203): proper implementation (must be handled by activity)
+ UiThread.getHandler().runWithScissors(() -> {
+ Toast.makeText(mContext, "AutoFill: service saved ids " + Arrays.toString(ids),
+ Toast.LENGTH_LONG).show();
+ }, 0);
+ }
+
+ /**
+ * Shows the options from a {@link FillResponse} so the user can pick up the proper
+ * {@link Dataset} (when the response has one).
+ */
+ void showOptions(int userId, int callbackId, FillResponse response) {
+ // TODO(b/33197203): proper implementation
+ // TODO(b/33197203): make sure if removes the callback from cache
+ showOptionsNotification(userId, callbackId, response);
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // TODO(b/33197203): temporary code using a notification to request auto-fill. //
+ // Will be removed once UX decide the right way to present it to the user. //
+ /////////////////////////////////////////////////////////////////////////////////
+
+ // TODO(b/33197203): remove from frameworks/base/core/res/AndroidManifest.xml once not used
+ private static final String NOTIFICATION_AUTO_FILL_INTENT =
+ "com.android.internal.autofill.action.REQUEST_AUTOFILL";
+
+ // Extras used in the notification intents
+ private static final String EXTRA_USER_ID = "user_id";
+ private static final String EXTRA_NOTIFICATION_TYPE = "notification_type";
+ private static final String EXTRA_CALLBACK_ID = "callback_id";
+ private static final String EXTRA_FILL_RESPONSE = "fill_response";
+ private static final String EXTRA_DATASET = "dataset";
+
+ private static final String TYPE_EMULATE = "emulate";
+ private static final String TYPE_OPTIONS = "options";
+ private static final String TYPE_DELETE_CALLBACK = "delete_callback";
+ private static final String TYPE_PICK_DATASET = "pick_dataset";
+ private static final String TYPE_SAVE = "save";
+
+ static final int MSG_SHOW_ALL_NOTIFICATIONS = 42;
+ static final int SHOW_ALL_NOTIFICATIONS_DELAY_MS = 5000;
+
+ private BroadcastReceiver mNotificationReceiver;
+ private final ContentResolver mResolver;
+ private final AutoFillManagerService mService;
+ private final Object mLock;
+
+ // Hack used to generate unique pending intents
+ static int sResultCode = 0;
+
+ final class NotificationReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(EXTRA_USER_ID, -1);
+
+ final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId);
+ if (service == null) {
+ Slog.w(TAG, "no auto-fill service for user " + userId);
+ return;
+ }
+
+ final int callbackId = intent.getIntExtra(EXTRA_CALLBACK_ID, -1);
+ final String type = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE);
+ if (type == null) {
+ Slog.wtf(TAG, "No extra " + EXTRA_NOTIFICATION_TYPE + " on intent " + intent);
+ return;
+ }
+ final FillResponse fillData = intent.getParcelableExtra(EXTRA_FILL_RESPONSE);
+ final Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET);
+ final Bundle datasetArgs = dataset == null ? null : dataset.getExtras();
+ final Bundle fillDataArgs = fillData == null ? null : fillData.getExtras();
+
+ // Bundle sent on AutoFillService methods - only set if service provided a bundle
+ final Bundle extras = (datasetArgs == null && fillDataArgs == null)
+ ? null : new Bundle();
+
+ if (DEBUG) Slog.d(TAG, "Notification received: type=" + type + ", userId=" + userId
+ + ", callbackId=" + callbackId);
+ synchronized (mLock) {
+ switch (type) {
+ case TYPE_EMULATE:
+ service.requestAutoFill(null, extras, AUTO_FILL_FLAG_TYPE_FILL);
+ break;
+ case TYPE_SAVE:
+ if (datasetArgs != null) {
+ if (DEBUG) Log.d(TAG, "filldata args on save notificataion: " +
+ bundleToString(fillDataArgs));
+ extras.putBundle(AutoFillService.EXTRA_RESPONSE_EXTRAS, fillDataArgs);
+ }
+ if (dataset != null) {
+ if (DEBUG) Log.d(TAG, "dataset args on save notificataion: " +
+ bundleToString(datasetArgs));
+ extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetArgs);
+ }
+ service.requestAutoFill(null, extras, AUTO_FILL_FLAG_TYPE_SAVE);
+ break;
+ case TYPE_DELETE_CALLBACK:
+ service.removeServerCallbackLocked(callbackId);
+ break;
+ case TYPE_PICK_DATASET:
+ service.autoFillApp(callbackId, dataset);
+ // Must cancel notification because it might be comming from action
+ if (DEBUG) Log.d(TAG, "Cancelling notification");
+ NotificationManager.from(mContext).cancel(TYPE_OPTIONS, userId);
+
+ if (datasetArgs != null) {
+ if (DEBUG) Log.d(TAG, "adding dataset's extra_data on save intent: "
+ + bundleToString(datasetArgs));
+ extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetArgs);
+ }
+
+ // Also show notification with option to save the data
+ showSaveNotification(userId, fillData, dataset);
+ break;
+ default: {
+ Slog.w(TAG, "Unknown notification type: " + type);
+ }
+ }
+ }
+ }
+ }
+
+ private ComponentName getProviderForUser(int userId) {
+ ComponentName serviceComponent = null;
+ ServiceInfo serviceInfo = null;
+ final String componentName = Settings.Secure.getStringForUser(
+ mResolver, Settings.Secure.AUTO_FILL_SERVICE, userId);
+ if (!TextUtils.isEmpty(componentName)) {
+ try {
+ serviceComponent = ComponentName.unflattenFromString(componentName);
+ serviceInfo =
+ AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, userId);
+ } catch (RuntimeException | RemoteException e) {
+ Slog.wtf(TAG, "Bad auto-fill service name " + componentName, e);
+ return null;
+ }
+ }
+
+ if (DEBUG) Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component="
+ + serviceComponent + ", info: " + serviceInfo);
+ if (serviceInfo == null) {
+ Slog.w(TAG, "no service info for " + serviceComponent);
+ return null;
+ }
+ return serviceComponent;
+ }
+
+ void showAllNotifications() {
+ final UserManager userManager =
+ (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+
+ final List<UserInfo> allUsers = userManager.getUsers(true);
+
+ for (UserInfo user : allUsers) {
+ final ComponentName serviceComponent = getProviderForUser(user.id);
+ if (serviceComponent != null) {
+ showMainNotification(serviceComponent, user.id);
+ }
+ }
+ }
+
+ void updateNotification(int userId) {
+ final ComponentName serviceComponent = getProviderForUser(userId);
+ if (serviceComponent == null) {
+ cancelMainNotification(userId);
+ } else {
+ showMainNotification(serviceComponent, userId);
+ }
+ }
+
+ private static Intent newNotificationIntent(int userId, String type) {
+ final Intent intent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
+ intent.putExtra(EXTRA_USER_ID, userId);
+ intent.putExtra(EXTRA_NOTIFICATION_TYPE, type);
+ return intent;
+ }
+
+ private PendingIntent newPickDatasetPI(int userId, int callbackId, FillResponse response,
+ Dataset dataset) {
+ final int resultCode = ++ sResultCode;
+ if (DEBUG) Log.d(TAG, "newPickDatasetPI: userId=" + userId + ", callback=" + callbackId
+ + ", resultCode=" + resultCode);
+
+ final Intent intent = newNotificationIntent(userId, TYPE_PICK_DATASET);
+ intent.putExtra(EXTRA_CALLBACK_ID, callbackId);
+ intent.putExtra(EXTRA_FILL_RESPONSE, response);
+ intent.putExtra(EXTRA_DATASET, dataset);
+ return PendingIntent.getBroadcast(mContext, resultCode, intent,
+ PendingIntent.FLAG_ONE_SHOT);
+ }
+
+ private static String bundleToString(Bundle bundle) {
+ if (bundle == null) {
+ return "null";
+ }
+ final Set<String> keySet = bundle.keySet();
+ final StringBuilder builder = new StringBuilder("[Bundle with ").append(keySet.size())
+ .append(" keys:");
+ for (String key : keySet) {
+ final Object value = bundle.get(key);
+ builder.append(' ').append(key).append('=');
+ builder.append((value instanceof Object[])
+ ? Arrays.toString((Objects[]) value) : value);
+ }
+ return builder.append(']').toString();
+ }
+
+ /**
+ * Shows a permanent notification that triggers the auto-fill workflow for the given user.
+ *
+ * <p>It emulates calling the auto-fill service when the IME is shown.
+ */
+ private void showMainNotification(ComponentName serviceComponent, int userId) {
+ if (DEBUG) Log.d(TAG, "showNotification() for " + userId + ": " + serviceComponent);
+
+ synchronized (mLock) {
+ if (mNotificationReceiver == null) {
+ mNotificationReceiver = new NotificationReceiver();
+ mContext.registerReceiver(mNotificationReceiver,
+ new IntentFilter(NOTIFICATION_AUTO_FILL_INTENT));
+ }
+ }
+
+ final Intent fillIntent = newNotificationIntent(userId, TYPE_EMULATE);
+ final PendingIntent fillPendingIntent = PendingIntent.getBroadcast(mContext,
+ -1, fillIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ final String packageName = serviceComponent.getPackageName();
+ String providerName = null;
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ final ApplicationInfo info = pm.getApplicationInfoAsUser(packageName, 0, userId);
+ if (info != null) {
+ providerName = pm.getApplicationLabel(info).toString();
+ }
+ } catch (Exception e) {
+ providerName = packageName;
+ }
+ final String title = "AutoFill IME Emulation";
+ final String subTitle = "Tap notification to start auto-fill workflow (by '" + providerName
+ + "' on top activity on user " + userId + ".\n"
+ + "Once provider replies, a new notification will show your options.";
+
+ final Notification notification = new Notification.Builder(mContext)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setOngoing(true)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setLocalOnly(true)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(title)
+ .setStyle(new Notification.BigTextStyle().bigText(subTitle))
+ .setContentIntent(fillPendingIntent)
+ .build();
+ NotificationManager.from(mContext).notify(TYPE_EMULATE, userId, notification);
+ }
+
+ /**
+ * Cancels the permament notification created by
+ * {@link #showMainNotification(ComponentName, int)}.
+ */
+ private void cancelMainNotification(int userId) {
+ if (DEBUG) Log.d(TAG, "cancelNotificationLocked(): " + userId);
+ NotificationManager.from(mContext).cancel(TYPE_EMULATE, userId);
+ }
+
+ /**
+ * Shows a notification with the results of an auto-fill request, using notications actions
+ * to emulate the auto-fill bar buttons displaying the dataset names.
+ */
+ private void showOptionsNotification(int userId, int callbackId, FillResponse response) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ showOptionsNotificationAsSystem(userId, callbackId, response);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void showOptionsNotificationAsSystem(int userId, int callbackId,
+ FillResponse response) {
+ // Make sure server callback is removed from cache if user cancels the notification.
+ final Intent deleteIntent = newNotificationIntent(userId, TYPE_DELETE_CALLBACK);
+ deleteIntent.putExtra(EXTRA_CALLBACK_ID, callbackId);
+ final PendingIntent deletePendingIntent = PendingIntent.getBroadcast(mContext,
+ ++sResultCode, deleteIntent, PendingIntent.FLAG_ONE_SHOT);
+
+ final String title = "AutoFill Options";
+
+ final Notification.Builder notification = new Notification.Builder(mContext)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setOngoing(false)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setLocalOnly(true)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setDeleteIntent(deletePendingIntent)
+ .setContentTitle(title);
+
+ boolean autoCancel = true;
+ final String subTitle;
+ final List<Dataset> datasets;
+ final AutoFillId[] savableIds;
+ if (response != null) {
+ datasets = response.getDatasets();
+ savableIds = response.getSavableIds();
+ } else {
+ datasets = null;
+ savableIds = null;
+ }
+ boolean showSave = false;
+ if (datasets == null ) {
+ subTitle = "No options to auto-fill this activity.";
+ } else if (datasets.isEmpty()) {
+ if (savableIds.length == 0) {
+ subTitle = "No options to auto-fill this activity.";
+ } else {
+ subTitle = "No options to auto-fill this activity, but provider can save ids:\n"
+ + Arrays.toString(savableIds);
+ showSave = true;
+ }
+ } else {
+ final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId);
+ if (service == null) {
+ subTitle = "No auto-fill service for user " + userId;
+ Slog.w(TAG, subTitle);
+ } else {
+ autoCancel = false;
+ final int size = datasets.size();
+ subTitle = "There are " + size + " option(s).\n"
+ + "Use the notification action(s) to select the proper one.";
+ for (Dataset dataset : datasets) {
+ final CharSequence name = dataset.getName();
+ final PendingIntent pi = newPickDatasetPI(userId, callbackId, response, dataset);
+ notification.addAction(new Action.Builder(null, name, pi).build());
+ }
+ }
+ }
+
+ notification.setAutoCancel(autoCancel);
+ notification.setStyle(new Notification.BigTextStyle().bigText(subTitle));
+
+ NotificationManager.from(mContext).notify(TYPE_OPTIONS, userId, notification.build());
+
+ if (showSave) {
+ showSaveNotification(userId, response, null);
+ }
+ }
+
+ private void showSaveNotification(int userId, FillResponse response, Dataset dataset) {
+ final Intent saveIntent = newNotificationIntent(userId, TYPE_SAVE);
+ saveIntent.putExtra(EXTRA_FILL_RESPONSE, response);
+ if (dataset != null) {
+ saveIntent.putExtra(EXTRA_DATASET, dataset);
+ }
+ final PendingIntent savePendingIntent = PendingIntent.getBroadcast(mContext,
+ ++sResultCode, saveIntent, PendingIntent.FLAG_ONE_SHOT);
+
+ final String title = "AutoFill Save";
+ final String subTitle = "Tap notification to ask provider to save fields: \n"
+ + Arrays.toString(response.getSavableIds());
+
+ final Notification notification = new Notification.Builder(mContext)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setAutoCancel(true)
+ .setOngoing(false)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setLocalOnly(true)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentIntent(savePendingIntent)
+ .setStyle(new Notification.BigTextStyle().bigText(subTitle))
+ .build();
+ NotificationManager.from(mContext).notify(TYPE_SAVE, userId, notification);
+ }
+
+ /////////////////////////////////////////
+ // End of temporary notification code. //
+ /////////////////////////////////////////
+}
diff --git a/services/core/Android.mk b/services/core/Android.mk
index efadbefa417a..1366b3b2d694 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -24,7 +24,7 @@ LOCAL_JAVA_LIBRARIES := \
android.hardware.power@1.0-java \
android.hardware.tv.cec@1.0-java
-LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
+LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update2
ifneq ($(INCREMENTAL_BUILDS),)
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index e7f1d16fcc61..2ca1b4eddb49 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -218,6 +218,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
@Override
public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
Bundle prevRestrictions) {
+ if (!newRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)
+ && !prevRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)) {
+ // The relevant restriction has not changed - do nothing.
+ return;
+ }
final boolean bluetoothDisallowed =
newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH);
if ((mEnable || mEnableExternal) && bluetoothDisallowed) {
@@ -228,6 +233,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
e);
}
}
+ updateOppLauncherComponentState(bluetoothDisallowed);
}
};
@@ -953,7 +959,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
UserManagerInternal userManagerInternal =
LocalServices.getService(UserManagerInternal.class);
userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
- if (isBluetoothDisallowed()) {
+ final boolean isBluetoothDisallowed = isBluetoothDisallowed();
+ updateOppLauncherComponentState(isBluetoothDisallowed);
+ if (isBluetoothDisallowed) {
return;
}
if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
@@ -2011,6 +2019,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
}
+ /**
+ * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not
+ * offered to the user if Bluetooth is disallowed. Puts the component to its default state if
+ * Bluetooth is not disallowed.
+ *
+ * @param bluetoothDisallowed whether the {@link UserManager.DISALLOW_BLUETOOTH} user
+ * restriction was set.
+ */
+ private void updateOppLauncherComponentState(boolean bluetoothDisallowed) {
+ final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth",
+ "com.android.bluetooth.opp.BluetoothOppLauncherActivity");
+ final int newState = bluetoothDisallowed
+ ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ mContext.getPackageManager()
+ .setComponentEnabledSetting(oppLauncherComponent, newState, 0);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index cef459a3ae39..e23844c3e633 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -301,7 +301,8 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
// If we're not connected at all then create a new connection.
if (mServiceConnection == null) {
- mServiceConnection = new ScoringServiceConnection(componentName);
+ mServiceConnection = new ScoringServiceConnection(componentName,
+ scorerData.packageUid);
}
// Make sure the connection is connected (idempotent)
@@ -325,7 +326,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
@Override
public boolean updateScores(ScoredNetwork[] networks) {
- if (!mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid())) {
+ if (!isCallerActiveScorer(getCallingUid())) {
throw new SecurityException("Caller with UID " + getCallingUid() +
" is not the active scorer.");
}
@@ -389,7 +390,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
@Override
public boolean clearScores() {
// Only the active scorer or the system should be allowed to flush all scores.
- if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) || isCallerSystemUid()) {
+ if (isCallerActiveScorer(getCallingUid()) || isCallerSystemUid()) {
final long token = Binder.clearCallingIdentity();
try {
clearInternal();
@@ -418,10 +419,39 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
return false;
}
+ /**
+ * Determine whether the application with the given UID is the enabled scorer.
+ *
+ * @param callingUid the UID to check
+ * @return true if the provided UID is the active scorer, false otherwise.
+ */
+ @Override
+ public boolean isCallerActiveScorer(int callingUid) {
+ synchronized (mServiceConnectionLock) {
+ return mServiceConnection != null && mServiceConnection.mScoringAppUid == callingUid;
+ }
+ }
+
+ /**
+ * Obtain the package name of the current active network scorer.
+ *
+ * @return the full package name of the current active scorer, or null if there is no active
+ * scorer.
+ */
+ @Override
+ public String getActiveScorerPackage() {
+ synchronized (mServiceConnectionLock) {
+ if (mServiceConnection != null) {
+ return mServiceConnection.mComponentName.getPackageName();
+ }
+ }
+ return null;
+ }
+
@Override
public void disableScoring() {
// Only the active scorer or the system should be allowed to disable scoring.
- if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) || isCallerSystemUid()) {
+ if (isCallerActiveScorer(getCallingUid()) || isCallerSystemUid()) {
// no-op for now but we could write to the setting if needed.
} else {
throw new SecurityException(
@@ -623,12 +653,14 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
private static class ScoringServiceConnection implements ServiceConnection {
private final ComponentName mComponentName;
+ private final int mScoringAppUid;
private volatile boolean mBound = false;
private volatile boolean mConnected = false;
private volatile INetworkRecommendationProvider mRecommendationProvider;
- ScoringServiceConnection(ComponentName componentName) {
+ ScoringServiceConnection(ComponentName componentName, int scoringAppUid) {
mComponentName = componentName;
+ mScoringAppUid = scoringAppUid;
}
void connect(Context context) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 49c4995bf4dd..3b2041e499ab 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -690,19 +690,21 @@ public class ActivityManagerService extends IActivityManager.Stub
public AssistStructure structure = null;
public AssistContent content = null;
public Bundle receiverExtras;
+ public int resultCode;
public int flags;
public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent,
- String _hint, IResultReceiver _receiver, Bundle _receiverExtras, int _flags,
- int _userHandle) {
+ String _hint, IResultReceiver _receiver, Bundle _receiverExtras, int _resultCode,
+ int _userHandle, int _flags) {
activity = _activity;
extras = _extras;
intent = _intent;
hint = _hint;
receiver = _receiver;
receiverExtras = _receiverExtras;
- flags = _flags;
+ resultCode = _resultCode;
userHandle = _userHandle;
+ flags = _flags;
}
@Override
public void run() {
@@ -11837,20 +11839,20 @@ public class ActivityManagerService extends IActivityManager.Stub
}
synchronized (this) {
- if (mStackSupervisor.isUserLockedProfile(userId)) {
- final long ident = Binder.clearCallingIdentity();
- try {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mUserController.shouldConfirmCredentials(userId)) {
final int currentUserId = mUserController.getCurrentUserIdLocked();
- if (mUserController.isLockScreenDisabled(currentUserId)) {
- // If there is no device lock, we will show the profile's credential page.
- mActivityStarter.showConfirmDeviceCredential(userId);
+ if (!mKeyguardController.isKeyguardLocked()) {
+ // If the device is not locked, we will prompt for credentials immediately.
+ mStackSupervisor.lockAllProfileTasks(userId);
} else {
// Showing launcher to avoid user entering credential twice.
startHomeActivityLocked(currentUserId, "notifyLockedProfile");
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
}
@@ -12190,7 +12192,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public Bundle getAssistContextExtras(int requestType) {
PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null,
- null, null, true /* focused */, true /* newSessionId */,
+ null, 0, null, true /* focused */, true /* newSessionId */,
UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT, 0);
if (pae == null) {
return null;
@@ -12258,22 +12260,37 @@ public class ActivityManagerService extends IActivityManager.Stub
Bundle receiverExtras,
IBinder activityToken, boolean focused, boolean newSessionId) {
return enqueueAssistContext(requestType, null, null, receiver, receiverExtras,
- activityToken, focused, newSessionId,
- UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0)
- != null;
+ 0, activityToken, focused, newSessionId, UserHandle.getCallingUserId(), null,
+ PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) != null;
}
@Override
public boolean requestAutoFillData(IResultReceiver receiver, Bundle receiverExtras,
- IBinder activityToken, int flags) {
- return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_FULL, null, null, receiver,
- receiverExtras, activityToken, true, true,
- UserHandle.getCallingUserId(), null, PENDING_AUTO_FILL_ASSIST_STRUCTURE_TIMEOUT,
- flags) != null;
+ int resultCode, IBinder activityToken, int flags) {
+ final boolean forFill = (flags & View.AUTO_FILL_FLAG_TYPE_FILL) != 0;
+ final boolean forSave = (flags & View.AUTO_FILL_FLAG_TYPE_SAVE) != 0;
+ if ((forFill && forSave) || (!forFill) && !(forSave)) {
+ // There can be only one!
+ Slog.w(TAG, "requestAutoFillData(): invalid flags (" + flags + ")");
+ return false;
+ }
+
+ // NOTE: we could always use ActivityManager.ASSIST_CONTEXT_FULL and let ActivityThread
+ // rely on the flags to decide whether the handleRequestAssistContextExtras() is for
+ // auto-fill, but it's safer to explicitly use new AutoFill types, in case the Assist
+ // requests use flags in the future as well (since their flags value might collide with the
+ // auto-fill flag values).
+ final int type = forFill?
+ ActivityManager.ASSIST_CONTEXT_AUTO_FILL :
+ ActivityManager.ASSIST_CONTEXT_AUTO_FILL_SAVE;
+
+ return enqueueAssistContext(type, null, null, receiver, receiverExtras, resultCode,
+ activityToken, true, true, UserHandle.getCallingUserId(), null,
+ PENDING_AUTO_FILL_ASSIST_STRUCTURE_TIMEOUT, flags) != null;
}
private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
- IResultReceiver receiver, Bundle receiverExtras, IBinder activityToken,
+ IResultReceiver receiver, Bundle receiverExtras, int resultCode, IBinder activityToken,
boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
int flags) {
enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
@@ -12314,7 +12331,7 @@ public class ActivityManagerService extends IActivityManager.Stub
extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName);
extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.uid);
pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras,
- flags, userHandle);
+ resultCode, userHandle, flags);
// Increment the sessionId if necessary
if (newSessionId) {
mViSessionId++;
@@ -12400,17 +12417,15 @@ public class ActivityManagerService extends IActivityManager.Stub
if (pae.flags > 0) {
sendBundle.putInt(VoiceInteractionSession.KEY_FLAGS, pae.flags);
}
- IBinder autoFillCallback =
- extras.getBinder(AutoFillService.KEY_CALLBACK);
- if (autoFillCallback != null) {
- sendBundle.putBinder(AutoFillService.KEY_CALLBACK,
- autoFillCallback);
+ IBinder cb = extras.getBinder(AutoFillService.KEY_CALLBACK);
+ if (cb != null) {
+ sendBundle.putBinder(AutoFillService.KEY_CALLBACK, cb);
}
}
}
if (sendReceiver != null) {
try {
- sendReceiver.send(0, sendBundle);
+ sendReceiver.send(pae.resultCode, sendBundle);
} catch (RemoteException e) {
}
return;
@@ -12435,9 +12450,9 @@ public class ActivityManagerService extends IActivityManager.Stub
public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle,
Bundle args) {
- return enqueueAssistContext(requestType, intent, hint, null, null, null,
- true /* focused */, true /* newSessionId */,
- userHandle, args, PENDING_ASSIST_EXTRAS_TIMEOUT, 0) != null;
+ return enqueueAssistContext(requestType, intent, hint, null, null, 0, null,
+ true /* focused */, true /* newSessionId */, userHandle, args,
+ PENDING_ASSIST_EXTRAS_TIMEOUT, 0) != null;
}
public void registerProcessObserver(IProcessObserver observer) {
@@ -17875,6 +17890,11 @@ public class ActivityManagerService extends IActivityManager.Stub
private void checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp,
String callerPackage, int callingUid, boolean isProtectedBroadcast, List receivers) {
+ if ((intent.getFlags() & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) {
+ // Don't yell about broadcasts sent via shell
+ return;
+ }
+
final String action = intent.getAction();
if (isProtectedBroadcast
|| Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
@@ -18018,11 +18038,7 @@ public class ActivityManagerService extends IActivityManager.Stub
case Process.PHONE_UID:
case Process.BLUETOOTH_UID:
case Process.NFC_UID:
- if ((intent.getFlags() & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) {
- isCallerSystem = false;
- } else {
- isCallerSystem = true;
- }
+ isCallerSystem = true;
break;
default:
isCallerSystem = (callerApp != null) && callerApp.persistent;
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 3f166fe4ddfb..e46d20457706 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -172,7 +172,7 @@ class ActivityMetricsLogger {
MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS,
(int) (SystemClock.uptimeMillis() / 1000));
- LogBuilder builder = new LogBuilder();
+ LogBuilder builder = new LogBuilder(MetricsEvent.APP_TRANSITION);
builder.addTaggedData(MetricsEvent.APP_TRANSITION_COMPONENT_NAME, componentName);
builder.addTaggedData(MetricsEvent.APP_TRANSITION_PROCESS_RUNNING, processRunning ? 1 : 0);
builder.addTaggedData(MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8bd7c9010181..a93524903b29 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -763,43 +763,52 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
/**
- * TODO: Handle freefom mode.
- * @return true when credential confirmation is needed for the user and there is any
- * activity started by the user in any visible stack.
+ * Detects whether we should show a lock screen in front of this task for a locked user.
+ * <p>
+ * We'll do this if either of the following holds:
+ * <ul>
+ * <li>The top activity explicitly belongs to {@param userId}.</li>
+ * <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
+ * </ul>
+ *
+ * @return {@code true} if the top activity looks like it belongs to {@param userId}.
*/
- boolean isUserLockedProfile(@UserIdInt int userId) {
- if (!mService.mUserController.shouldConfirmCredentials(userId)) {
- return false;
- }
- final ActivityStack fullScreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
- final ActivityStack dockedStack = getStack(DOCKED_STACK_ID);
- final ActivityStack[] activityStacks = new ActivityStack[] {fullScreenStack, dockedStack};
- for (final ActivityStack activityStack : activityStacks) {
- if (activityStack == null) {
- continue;
- }
- if (activityStack.topRunningActivityLocked() == null) {
- continue;
- }
- if (activityStack.getStackVisibilityLocked(null) == STACK_INVISIBLE) {
- continue;
- }
- if (activityStack.isDockedStack() && mIsDockMinimized) {
- continue;
- }
- final TaskRecord topTask = activityStack.topTask();
- if (topTask == null) {
- continue;
- }
- // To handle the case that work app is in the task but just is not the top one.
- for (int i = topTask.mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord activityRecord = topTask.mActivities.get(i);
- if (activityRecord.userId == userId) {
- return true;
+ private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) {
+ // To handle the case that work app is in the task but just is not the top one.
+ final ActivityRecord activityRecord = task.getTopActivity();
+ final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
+
+ return (activityRecord != null && activityRecord.userId == userId)
+ || (resultTo != null && resultTo.userId == userId);
+ }
+
+ /**
+ * Find all visible task stacks containing {@param userId} and intercept them with an activity
+ * to block out the contents and possibly start a credential-confirming intent.
+ *
+ * @param userId user handle for the locked managed profile.
+ */
+ void lockAllProfileTasks(@UserIdInt int userId) {
+ mWindowManager.deferSurfaceLayout();
+ try {
+ final List<ActivityStack> stacks = getStacks();
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; stackNdx--) {
+ final List<TaskRecord> tasks = stacks.get(stackNdx).getAllTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
+ final TaskRecord task = tasks.get(taskNdx);
+
+ // Check the task for a top activity belonging to userId, or returning a result
+ // to an activity belonging to userId. Example case: a document picker for
+ // personal files, opened by a work app, should still get locked.
+ if (taskTopActivityIsUser(task, userId)) {
+ mService.mTaskChangeNotificationController.notifyTaskProfileLocked(
+ task.taskId, userId);
+ }
}
}
+ } finally {
+ mWindowManager.continueSurfaceLayout();
}
- return false;
}
void setNextTaskIdForUserLocked(int taskId, int userId) {
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index fd248c6c807a..fbdbb1b2443a 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -39,6 +39,7 @@ class TaskChangeNotificationController {
static final int NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG = 11;
static final int NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS = 12;
static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
+ static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14;
// Delay in notifying task stack change listeners (in millis)
static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -110,6 +111,9 @@ class TaskChangeNotificationController {
case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG:
forAllListeners((listener) -> listener.onActivityDismissingDockedStack());
break;
+ case NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG:
+ forAllListeners((listener) -> listener.onTaskProfileLocked(msg.arg1, msg.arg2));
+ break;
}
}
}
@@ -228,4 +232,13 @@ class TaskChangeNotificationController {
mHandler.obtainMessage(NOTIFY_TASK_REMOVAL_STARTED_LISTENERS, taskId, 0 /* unused */)
.sendToTarget();
}
+
+ /**
+ * Notify listeners that the task has been put in a locked state because one or more of the
+ * activities inside it belong to a managed profile user that has been locked.
+ */
+ void notifyTaskProfileLocked(int taskId, int userId) {
+ mHandler.obtainMessage(NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG, taskId, userId)
+ .sendToTarget();
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 6d96a1015aa2..79567d50c31c 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -199,7 +199,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
mTetherMasterSM.start();
- mUpstreamNetworkMonitor = new UpstreamNetworkMonitor();
+ mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
+ mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
mStateReceiver = new StateReceiver();
IntentFilter filter = new IntentFilter();
@@ -1027,38 +1028,6 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
}
/**
- * A NetworkCallback class that relays information of interest to the
- * tethering master state machine thread for subsequent processing.
- */
- class UpstreamNetworkCallback extends NetworkCallback {
- @Override
- public void onAvailable(Network network) {
- mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
- UpstreamNetworkMonitor.EVENT_ON_AVAILABLE, 0, network);
- }
-
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
- mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
- UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES, 0,
- new NetworkState(null, null, newNc, network, null, null));
- }
-
- @Override
- public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
- mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
- UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, 0,
- new NetworkState(null, newLp, null, network, null, null));
- }
-
- @Override
- public void onLost(Network network) {
- mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
- UpstreamNetworkMonitor.EVENT_ON_LOST, 0, network);
- }
- }
-
- /**
* A class to centralize all the network and link properties information
* pertaining to the current and any potential upstream network.
*
@@ -1072,21 +1041,31 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
* TODO: Investigate whether more "upstream-specific" logic/functionality
* could/should be moved here.
*/
- class UpstreamNetworkMonitor {
- static final int EVENT_ON_AVAILABLE = 1;
- static final int EVENT_ON_CAPABILITIES = 2;
- static final int EVENT_ON_LINKPROPERTIES = 3;
- static final int EVENT_ON_LOST = 4;
-
- final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
- NetworkCallback mDefaultNetworkCallback;
- NetworkCallback mDunTetheringCallback;
-
- void start() {
+ public class UpstreamNetworkMonitor {
+ public static final int EVENT_ON_AVAILABLE = 1;
+ public static final int EVENT_ON_CAPABILITIES = 2;
+ public static final int EVENT_ON_LINKPROPERTIES = 3;
+ public static final int EVENT_ON_LOST = 4;
+
+ private final Context mContext;
+ private final StateMachine mTarget;
+ private final int mWhat;
+ private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
+ private ConnectivityManager mCM;
+ private NetworkCallback mDefaultNetworkCallback;
+ private NetworkCallback mDunTetheringCallback;
+
+ public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) {
+ mContext = ctx;
+ mTarget = tgt;
+ mWhat = what;
+ }
+
+ public void start() {
stop();
mDefaultNetworkCallback = new UpstreamNetworkCallback();
- getConnectivityManager().registerDefaultNetworkCallback(mDefaultNetworkCallback);
+ cm().registerDefaultNetworkCallback(mDefaultNetworkCallback);
final NetworkRequest dunTetheringRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
@@ -1094,29 +1073,28 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
.build();
mDunTetheringCallback = new UpstreamNetworkCallback();
- getConnectivityManager().registerNetworkCallback(
- dunTetheringRequest, mDunTetheringCallback);
+ cm().registerNetworkCallback(dunTetheringRequest, mDunTetheringCallback);
}
- void stop() {
+ public void stop() {
if (mDefaultNetworkCallback != null) {
- getConnectivityManager().unregisterNetworkCallback(mDefaultNetworkCallback);
+ cm().unregisterNetworkCallback(mDefaultNetworkCallback);
mDefaultNetworkCallback = null;
}
if (mDunTetheringCallback != null) {
- getConnectivityManager().unregisterNetworkCallback(mDunTetheringCallback);
+ cm().unregisterNetworkCallback(mDunTetheringCallback);
mDunTetheringCallback = null;
}
mNetworkMap.clear();
}
- NetworkState lookup(Network network) {
+ public NetworkState lookup(Network network) {
return (network != null) ? mNetworkMap.get(network) : null;
}
- NetworkState processCallback(int arg1, Object obj) {
+ public NetworkState processCallback(int arg1, Object obj) {
switch (arg1) {
case EVENT_ON_AVAILABLE: {
final Network network = (Network) obj;
@@ -1128,7 +1106,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
new NetworkState(null, null, null, network, null, null));
}
- final ConnectivityManager cm = getConnectivityManager();
+ final ConnectivityManager cm = cm();
if (mDefaultNetworkCallback != null) {
cm.requestNetworkCapabilities(mDefaultNetworkCallback);
@@ -1199,6 +1177,42 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
return null;
}
}
+
+ // Fetch (and cache) a ConnectivityManager only if and when we need one.
+ private ConnectivityManager cm() {
+ if (mCM == null) {
+ mCM = mContext.getSystemService(ConnectivityManager.class);
+ }
+ return mCM;
+ }
+
+ /**
+ * A NetworkCallback class that relays information of interest to the
+ * tethering master state machine thread for subsequent processing.
+ */
+ private class UpstreamNetworkCallback extends NetworkCallback {
+ @Override
+ public void onAvailable(Network network) {
+ mTarget.sendMessage(mWhat, EVENT_ON_AVAILABLE, 0, network);
+ }
+
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
+ mTarget.sendMessage(mWhat, EVENT_ON_CAPABILITIES, 0,
+ new NetworkState(null, null, newNc, network, null, null));
+ }
+
+ @Override
+ public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
+ mTarget.sendMessage(mWhat, EVENT_ON_LINKPROPERTIES, 0,
+ new NetworkState(null, newLp, null, network, null, null));
+ }
+
+ @Override
+ public void onLost(Network network) {
+ mTarget.sendMessage(mWhat, EVENT_ON_LOST, 0, network);
+ }
+ }
}
// Needed because the canonical source of upstream truth is just the
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 477ecdf60c48..015345c23a1a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -577,6 +577,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
brightness = mPowerRequest.dozeScreenBrightness;
}
break;
+ case DisplayPowerRequest.POLICY_VR:
+ state = Display.STATE_VR;
+ break;
case DisplayPowerRequest.POLICY_DIM:
case DisplayPowerRequest.POLICY_BRIGHT:
default:
@@ -618,6 +621,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Animate the screen state change unless already animating.
// The transition may be deferred, so after this point we will use the
// actual state instead of the desired one.
+ final int oldState = mPowerState.getScreenState();
animateScreenStateChange(state, performScreenOffTransition);
state = mPowerState.getScreenState();
@@ -717,9 +721,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
// Animate the screen brightness when the screen is on or dozing.
- // Skip the animation when the screen is off or suspended.
+ // Skip the animation when the screen is off or suspended or transition to/from VR.
if (!mPendingScreenOff) {
- if (state == Display.STATE_ON || state == Display.STATE_DOZE) {
+ boolean wasOrWillBeInVr = (state == Display.STATE_VR || oldState == Display.STATE_VR);
+ if ((state == Display.STATE_ON || state == Display.STATE_DOZE) && !wasOrWillBeInVr) {
animateScreenBrightness(brightness,
slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
} else {
@@ -903,6 +908,23 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mPowerState.setColorFadeLevel(1.0f);
mPowerState.dismissColorFade();
}
+ } else if (target == Display.STATE_VR) {
+ // Wait for brightness animation to complete beforehand when entering VR
+ // from screen on to prevent a perceptible jump because brightness may operate
+ // differently when the display is configured for dozing.
+ if (mScreenBrightnessRampAnimator.isAnimating()
+ && mPowerState.getScreenState() == Display.STATE_ON) {
+ return;
+ }
+
+ // Set screen state.
+ if (!setScreenState(Display.STATE_VR)) {
+ return; // screen on blocked
+ }
+
+ // Dismiss the black surface without fanfare.
+ mPowerState.setColorFadeLevel(1.0f);
+ mPowerState.dismissColorFade();
} else if (target == Display.STATE_DOZE) {
// Want screen dozing.
// Wait for brightness animation to complete beforehand when entering doze
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 61c2eacaa8f9..867322578a66 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -471,6 +471,16 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
}
+ // If the state change was from or to VR, then we need to tell the light
+ // so that it can apply appropriate VR brightness settings. This should
+ // happen prior to changing the brightness but also if there is no
+ // brightness change at all.
+ if ((state == Display.STATE_VR || currentState == Display.STATE_VR) &&
+ currentState != state) {
+ setVrMode(state == Display.STATE_VR);
+ }
+
+
// Apply brightness changes given that we are in a non-suspended state.
if (brightnessChanged) {
setDisplayBrightness(brightness);
@@ -482,6 +492,15 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
}
+ private void setVrMode(boolean isVrEnabled) {
+ if (DEBUG) {
+ Slog.d(TAG, "setVrMode("
+ + "id=" + displayId
+ + ", state=" + Display.stateToString(state) + ")");
+ }
+ mBacklight.setVrMode(isVrEnabled);
+ }
+
private void setDisplayState(int state) {
if (DEBUG) {
Slog.d(TAG, "setDisplayState("
diff --git a/services/core/java/com/android/server/lights/Light.java b/services/core/java/com/android/server/lights/Light.java
index 0bab86b67e82..b5ec603f5f70 100644
--- a/services/core/java/com/android/server/lights/Light.java
+++ b/services/core/java/com/android/server/lights/Light.java
@@ -46,4 +46,5 @@ public abstract class Light {
public abstract void pulse();
public abstract void pulse(int color, int onMS);
public abstract void turnOff();
+ public abstract void setVrMode(boolean enabled);
}
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index bba0a50e7af4..e07156ece1b9 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -1,5 +1,4 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
+/* * Copyright (C) 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.
@@ -17,18 +16,13 @@
package com.android.server.lights;
import com.android.server.SystemService;
-import com.android.server.vr.VrManagerService;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
import android.os.Trace;
-import android.os.UserHandle;
import android.provider.Settings;
-import android.service.vr.IVrManager;
-import android.service.vr.IVrStateCallbacks;
import android.util.Slog;
public class LightsService extends SystemService {
@@ -36,7 +30,6 @@ public class LightsService extends SystemService {
static final boolean DEBUG = false;
final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT];
- private boolean mVrModeEnabled;
private final class LightImpl extends Light {
@@ -52,6 +45,13 @@ public class LightsService extends SystemService {
@Override
public void setBrightness(int brightness, int brightnessMode) {
synchronized (this) {
+ // LOW_PERSISTENCE cannot be manually set
+ if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
+ Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId +
+ ": brightness=0x" + Integer.toHexString(brightness));
+ return;
+ }
+
int color = brightness & 0x000000ff;
color = 0xff000000 | (color << 16) | (color << 8) | color;
setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
@@ -80,11 +80,9 @@ public class LightsService extends SystemService {
@Override
public void pulse(int color, int onMS) {
synchronized (this) {
- if (mBrightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
- return;
- }
if (mColor == 0 && !mFlashing) {
- setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000, BRIGHTNESS_MODE_USER);
+ setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000,
+ BRIGHTNESS_MODE_USER);
mColor = 0;
mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS);
}
@@ -98,17 +96,23 @@ public class LightsService extends SystemService {
}
}
- void enableLowPersistence() {
- synchronized(this) {
- setLightLocked(0, LIGHT_FLASH_NONE, 0, 0, BRIGHTNESS_MODE_LOW_PERSISTENCE);
- mLocked = true;
- }
- }
-
- void disableLowPersistence() {
- synchronized(this) {
- mLocked = false;
- setLightLocked(mLastColor, LIGHT_FLASH_NONE, 0, 0, mLastBrightnessMode);
+ @Override
+ public void setVrMode(boolean enabled) {
+ synchronized (this) {
+ if (mVrModeEnabled != enabled) {
+ mVrModeEnabled = enabled;
+
+ mUseLowPersistenceForVR =
+ (getVrDisplayMode() == Settings.Secure.VR_DISPLAY_MODE_LOW_PERSISTENCE);
+ if (shouldBeInLowPersistenceMode()) {
+ mLastBrightnessMode = mBrightnessMode;
+ }
+
+ // NOTE: We do not trigger a call to setLightLocked here. We do not know the
+ // current brightness or other values when leaving VR so we avoid any incorrect
+ // jumps. The code that calls this method will immediately issue a brightness
+ // update which is when the change will occur.
+ }
}
}
@@ -119,7 +123,13 @@ public class LightsService extends SystemService {
}
private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
- if (!mLocked && (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS ||
+ if (shouldBeInLowPersistenceMode()) {
+ brightnessMode = BRIGHTNESS_MODE_LOW_PERSISTENCE;
+ } else if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
+ brightnessMode = mLastBrightnessMode;
+ }
+
+ if ((color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS ||
mBrightnessMode != brightnessMode)) {
if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
+ Integer.toHexString(color) + ": brightnessMode=" + brightnessMode);
@@ -128,7 +138,6 @@ public class LightsService extends SystemService {
mMode = mode;
mOnMS = onMS;
mOffMS = offMS;
- mLastBrightnessMode = mBrightnessMode;
mBrightnessMode = brightnessMode;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"
+ Integer.toHexString(color) + ")");
@@ -140,6 +149,10 @@ public class LightsService extends SystemService {
}
}
+ private boolean shouldBeInLowPersistenceMode() {
+ return mVrModeEnabled && mUseLowPersistenceForVR;
+ }
+
private int mId;
private int mColor;
private int mMode;
@@ -149,7 +162,8 @@ public class LightsService extends SystemService {
private int mBrightnessMode;
private int mLastBrightnessMode;
private int mLastColor;
- private boolean mLocked;
+ private boolean mVrModeEnabled;
+ private boolean mUseLowPersistenceForVR;
}
public LightsService(Context context) {
@@ -167,17 +181,6 @@ public class LightsService extends SystemService {
@Override
public void onBootPhase(int phase) {
- if (phase == PHASE_SYSTEM_SERVICES_READY) {
- IVrManager vrManager =
- (IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE);
- if (vrManager != null) {
- try {
- vrManager.registerListener(mVrStateCallbacks);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register VR mode state listener: " + e);
- }
- }
- }
}
private int getVrDisplayMode() {
@@ -188,30 +191,6 @@ public class LightsService extends SystemService {
currentUser);
}
- private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
- @Override
- public void onVrStateChanged(boolean enabled) throws RemoteException {
- LightImpl l = mLights[LightsManager.LIGHT_ID_BACKLIGHT];
- int vrDisplayMode = getVrDisplayMode();
-
- // User leaves VR mode before altering display settings.
- if (enabled && vrDisplayMode == Settings.Secure.VR_DISPLAY_MODE_LOW_PERSISTENCE) {
- if (!mVrModeEnabled) {
- if (DEBUG)
- Slog.v(TAG, "VR mode enabled, setting brightness to low persistence");
- l.enableLowPersistence();
- mVrModeEnabled = true;
- }
- } else {
- if (mVrModeEnabled) {
- if (DEBUG) Slog.v(TAG, "VR mode disabled, resetting brightnes");
- l.disableLowPersistence();
- mVrModeEnabled = false;
- }
- }
- }
- };
-
private final LightsManager mService = new LightsManager() {
@Override
public Light getLight(int id) {
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 2bf5ef10ec89..601a2194e8f3 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -42,12 +42,18 @@ import java.util.concurrent.TimeUnit;
* {@hide}
*/
public class BackgroundDexOptService extends JobService {
- static final String TAG = "BackgroundDexOptService";
+ private static final String TAG = "BackgroundDexOptService";
- static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR;
+ private static final boolean DEBUG = false;
- static final int JOB_IDLE_OPTIMIZE = 800;
- static final int JOB_POST_BOOT_UPDATE = 801;
+ private static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR;
+
+ private static final int JOB_IDLE_OPTIMIZE = 800;
+ private static final int JOB_POST_BOOT_UPDATE = 801;
+
+ private static final long IDLE_OPTIMIZATION_PERIOD = DEBUG
+ ? TimeUnit.MINUTES.toMillis(1)
+ : TimeUnit.DAYS.toMillis(1);
private static ComponentName sDexoptServiceName = new ComponentName(
"android",
@@ -86,7 +92,7 @@ public class BackgroundDexOptService extends JobService {
js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName)
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
- .setPeriodic(TimeUnit.DAYS.toMillis(1))
+ .setPeriodic(IDLE_OPTIMIZATION_PERIOD)
.build());
if (DEBUG_DEXOPT) {
@@ -208,6 +214,7 @@ public class BackgroundDexOptService extends JobService {
private void idleOptimization(JobParameters jobParams, PackageManagerService pm,
ArraySet<String> pkgs) {
+ Log.i(TAG, "Performing idle optimizations");
// If post-boot update is still running, request that it exits early.
mExitPostBootUpdate.set(true);
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 2e0199b0b863..8c4a95ce1cdf 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -217,26 +217,11 @@ class PackageDexOptimizer {
dexoptNeeded);
}
- final String dexoptType;
- String oatDir = null;
- boolean isOdexLocation = (dexoptNeeded < 0);
- switch (Math.abs(dexoptNeeded)) {
- case DexFile.NO_DEXOPT_NEEDED:
- continue;
- case DexFile.DEX2OAT_FROM_SCRATCH:
- case DexFile.DEX2OAT_FOR_BOOT_IMAGE:
- case DexFile.DEX2OAT_FOR_FILTER:
- case DexFile.DEX2OAT_FOR_RELOCATION:
- dexoptType = "dex2oat";
- oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
- break;
- case DexFile.PATCHOAT_FOR_RELOCATION:
- dexoptType = "patchoat";
- break;
- default:
- throw new IllegalStateException("Invalid dexopt:" + dexoptNeeded);
+ if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
+ continue;
}
+ String oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
String sharedLibrariesPath = null;
if (sharedLibraries != null && sharedLibraries.length != 0) {
StringBuilder sb = new StringBuilder();
@@ -248,7 +233,7 @@ class PackageDexOptimizer {
}
sharedLibrariesPath = sb.toString();
}
- Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
+ Log.i(TAG, "Running dexopt on: " + path + " pkg="
+ pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
+ " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
+ " target-filter=" + targetCompilerFilter + " oatDir=" + oatDir
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index ddbc5fa59e16..3085c9cc9d29 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -361,6 +361,11 @@ class ShortcutPackage extends ShortcutPackageItem {
}
oldShortcut.setTimestamp(mShortcutUser.mService.injectCurrentTimeMillis());
+ // See ShortcutRequestPinProcessor.directPinShortcut().
+ if (mShortcutUser.mService.isDummyMainActivity(oldShortcut.getActivity())) {
+ oldShortcut.setActivity(null);
+ }
+
return oldShortcut;
} else {
deleteShortcutInner(shortcutId);
@@ -1515,6 +1520,8 @@ class ShortcutPackage extends ShortcutPackageItem {
boolean failed = false;
+ final ShortcutService s = mShortcutUser.mService;
+
final ArrayMap<ComponentName, ArrayList<ShortcutInfo>> all =
sortShortcutsToActivities();
@@ -1554,10 +1561,10 @@ class ShortcutPackage extends ShortcutPackageItem {
Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ " is both dynamic and manifest at the same time.");
}
- if (si.getActivity() == null) {
+ if (si.getActivity() == null && !si.isFloating()) {
failed = true;
Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
- + " has null activity.");
+ + " has null activity, but not floating.");
}
if ((si.isDynamic() || si.isManifestShortcut()) && !si.isEnabled()) {
failed = true;
@@ -1579,6 +1586,11 @@ class ShortcutPackage extends ShortcutPackageItem {
Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ " has both resource and bitmap icons");
}
+ if (s.isDummyMainActivity(si.getActivity())) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " has a dummy target activity");
+ }
}
if (failed) {
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index e815f0ae18fd..c8ddf0a711bf 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -16,7 +16,6 @@
package com.android.server.pm;
import android.annotation.Nullable;
-import android.app.PendingIntent;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Intent;
@@ -209,7 +208,7 @@ class ShortcutRequestPinProcessor {
final boolean existsAlready = existing != null;
if (DEBUG) {
- Slog.d(TAG, "requestPinnedShortcut package=" + inShortcut.getPackage()
+ Slog.d(TAG, "requestPinnedShortcut: package=" + inShortcut.getPackage()
+ " existsAlready=" + existsAlready
+ " shortcut=" + inShortcut.toInsecureString());
}
@@ -237,6 +236,14 @@ class ShortcutRequestPinProcessor {
// FLAG_PINNED is still set, if it's pinned by other launchers.
shortcutForLauncher.clearFlags(ShortcutInfo.FLAG_PINNED);
} else {
+ // If the shortcut has no default activity, try to set the main activity.
+ // But in the request-pin case, it's optional, so it's okay even if the caller
+ // has no default activity.
+ if (inShortcut.getActivity() == null) {
+ inShortcut.setActivity(mService.injectGetDefaultMainActivity(
+ inShortcut.getPackage(), inShortcut.getUserId()));
+ }
+
// It doesn't exist, so it must have all mandatory fields.
mService.validateShortcutForPinRequest(inShortcut);
@@ -244,12 +251,15 @@ class ShortcutRequestPinProcessor {
inShortcut.resolveResourceStrings(mService.injectGetResourcesForApplicationAsUser(
inShortcut.getPackage(), inShortcut.getUserId()));
if (DEBUG) {
- Slog.d(TAG, "resolved shortcut=" + inShortcut.toInsecureString());
+ Slog.d(TAG, "Resolved shortcut=" + inShortcut.toInsecureString());
}
// We should strip out the intent, but should preserve the icon.
shortcutForLauncher = inShortcut.clone(
ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER_APPROVAL);
}
+ if (DEBUG) {
+ Slog.d(TAG, "Sending to launcher=" + shortcutForLauncher.toInsecureString());
+ }
// Create a request object.
final PinShortcutRequestInner inner =
@@ -360,7 +370,9 @@ class ShortcutRequestPinProcessor {
if (DEBUG) {
Slog.d(TAG, "Temporarily adding " + shortcutId + " as dynamic");
}
- // Add as a dynamic shortcut.
+ // Add as a dynamic shortcut. In order for a shortcut to be dynamic, it must
+ // have a target activity, so we set a dummy here. It's later removed
+ // in deleteDynamicWithId().
if (original.getActivity() == null) {
original.setActivity(mService.getDummyMainActivity(appPackageName));
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index c02ce6e0daeb..a890526c0f91 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1604,7 +1604,7 @@ public class ShortcutService extends IShortcutService.Stub {
private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate,
boolean forPinRequest) {
Preconditions.checkNotNull(shortcut, "Null shortcut detected");
- if (!forPinRequest && shortcut.getActivity() != null) {
+ if (shortcut.getActivity() != null) {
Preconditions.checkState(
shortcut.getPackage().equals(shortcut.getActivity().getPackageName()),
"Cannot publish shortcut: activity " + shortcut.getActivity() + " does not"
@@ -1618,10 +1618,8 @@ public class ShortcutService extends IShortcutService.Stub {
if (!forUpdate) {
shortcut.enforceMandatoryFields(/* forPinned= */ forPinRequest);
if (!forPinRequest) {
- Preconditions.checkArgument(
- injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()),
- "Cannot publish shortcut: " + shortcut.getActivity()
- + " is not main activity");
+ Preconditions.checkState(shortcut.getActivity() != null,
+ "Cannot publish shortcut: target activity is not set");
}
}
if (shortcut.getIcon() != null) {
@@ -1870,9 +1868,7 @@ public class ShortcutService extends IShortcutService.Stub {
throwIfUserLockedL(userId);
Preconditions.checkState(isUidForegroundLocked(injectBinderCallingUid()),
- "Calling application must have a foreground activity or a foreground service");
-
- // TODO Cancel all pending requests from the caller.
+ "Calling application must have a foreground activity or a foreground service");
// Send request to the launcher, if supported.
ret = mShortcutRequestPinProcessor.requestPinItemLocked(shortcut, appWidget, userId,
@@ -3193,6 +3189,10 @@ public class ShortcutService extends IShortcutService.Stub {
return new ComponentName(packageName, DUMMY_MAIN_ACTIVITY);
}
+ boolean isDummyMainActivity(@Nullable ComponentName name) {
+ return name != null && DUMMY_MAIN_ACTIVITY.equals(name.getClassName());
+ }
+
/**
* Return all the enabled, exported and main activities from a package.
*/
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index fa1d991fa39a..0a312f0ad7ee 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -251,9 +251,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_KEYGUARD = false;
static final boolean DEBUG_LAYOUT = false;
- static final boolean DEBUG_STARTING_WINDOW = false;
+ static final boolean DEBUG_SPLASH_SCREEN = false;
static final boolean DEBUG_WAKEUP = false;
- static final boolean SHOW_STARTING_ANIMATIONS = true;
+ static final boolean SHOW_SPLASH_SCREENS = true;
// Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
// No longer recommended for desk docks;
@@ -2794,10 +2794,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/** {@inheritDoc} */
@Override
- public View addStartingWindow(IBinder appToken, String packageName, int theme,
- CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
- int icon, int logo, int windowFlags, Configuration overrideConfig) {
- if (!SHOW_STARTING_ANIMATIONS) {
+ public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
+ CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
+ int logo, int windowFlags, Configuration overrideConfig) {
+ if (!SHOW_SPLASH_SCREENS) {
return null;
}
if (packageName == null) {
@@ -2809,7 +2809,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
try {
Context context = mContext;
- if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow " + packageName
+ if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen " + packageName
+ ": nonLocalizedLabel=" + nonLocalizedLabel + " theme="
+ Integer.toHexString(theme));
if (theme != context.getThemeResId() || labelRes != 0) {
@@ -2822,8 +2822,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
if (overrideConfig != null && !overrideConfig.equals(EMPTY)) {
- if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: creating context based"
- + " on overrideConfig" + overrideConfig + " for starting window");
+ if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: creating context based"
+ + " on overrideConfig" + overrideConfig + " for splash screen");
final Context overrideContext = context.createConfigurationContext(overrideConfig);
overrideContext.setTheme(theme);
final TypedArray typedArray = overrideContext.obtainStyledAttributes(
@@ -2833,7 +2833,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// We want to use the windowBackground for the override context if it is
// available, otherwise we use the default one to make sure a themed starting
// window is displayed for the app.
- if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: apply overrideConfig"
+ if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: apply overrideConfig"
+ overrideConfig + " to starting window resId=" + resId);
context = overrideContext;
}
@@ -2895,19 +2895,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
}
- params.setTitle("Starting " + packageName);
+ params.setTitle("Splash Screen " + packageName);
wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
view = win.getDecorView();
- if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Adding starting window for "
+ if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
+ packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));
wm.addView(view, params);
// Only return the view if it was successfully added to the
// window manager... which we can tell by it having a parent.
- return view.getParent() != null ? view : null;
+ return view.getParent() != null ? new SplashScreenSurface(view) : null;
} catch (WindowManager.BadTokenException e) {
// ignore
Log.w(TAG, appToken + " already running, starting window not displayed. " +
@@ -2929,13 +2929,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/** {@inheritDoc} */
@Override
- public void removeStartingWindow(IBinder appToken, View window) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Removing starting window for " + appToken + ": "
- + window + " Callers=" + Debug.getCallers(4));
+ public void removeSplashScreen(IBinder appToken, StartingSurface surface) {
+ if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + appToken + ": "
+ + surface + " Callers=" + Debug.getCallers(4));
- if (window != null) {
+ if (surface != null) {
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- wm.removeView(window);
+ wm.removeView(((SplashScreenSurface) surface).view);
}
}
diff --git a/services/core/java/com/android/server/policy/SplashScreenSurface.java b/services/core/java/com/android/server/policy/SplashScreenSurface.java
new file mode 100644
index 000000000000..d4212915c0a6
--- /dev/null
+++ b/services/core/java/com/android/server/policy/SplashScreenSurface.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 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.policy;
+
+import android.view.View;
+import android.view.WindowManagerPolicy;
+import android.view.WindowManagerPolicy.StartingSurface;
+
+import com.android.internal.policy.DecorView;
+import com.android.internal.policy.PhoneWindow;
+
+/**
+ * Holds the contents of a splash screen starting window, i.e. the {@link DecorView} of a
+ * {@link PhoneWindow}. This is just a wrapper such that we can return it from
+ * {@link WindowManagerPolicy#addSplashScreen}.
+ */
+class SplashScreenSurface implements StartingSurface {
+
+ final View view;
+
+ SplashScreenSurface(View view) {
+ this.view = view;
+ }
+}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index f2ccac5197aa..8aefebc9f58d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -136,6 +136,8 @@ public final class PowerManagerService extends SystemService
private static final int DIRTY_SCREEN_BRIGHTNESS_BOOST = 1 << 11;
// Dirty bit: sQuiescent changed
private static final int DIRTY_QUIESCENT = 1 << 12;
+ // Dirty bit: VR Mode enabled changed
+ private static final int DIRTY_VR_MODE_CHANGED = 1 << 13;
// Summarizes the state of all active wakelocks.
private static final int WAKE_LOCK_CPU = 1 << 0;
@@ -413,11 +415,15 @@ public final class PowerManagerService extends SystemService
private int mScreenBrightnessSettingMinimum;
private int mScreenBrightnessSettingMaximum;
private int mScreenBrightnessSettingDefault;
+ private int mScreenBrightnessForVrSettingDefault;
// The screen brightness setting, from 0 to 255.
// Use -1 if no value has been set.
private int mScreenBrightnessSetting;
+ // The screen brightness setting, from 0 to 255, to be used while in VR Mode.
+ private int mScreenBrightnessForVrSetting;
+
// The screen auto-brightness adjustment setting, from -1 to 1.
// Use 0 if there is no adjustment.
private float mScreenAutoBrightnessAdjustmentSetting;
@@ -511,6 +517,9 @@ public final class PowerManagerService extends SystemService
// True if brightness should be affected by twilight.
private boolean mBrightnessUseTwilight;
+ // True if we are currently in VR Mode.
+ private boolean mIsVrModeEnabled;
+
private native void nativeInit();
private static native void nativeAcquireSuspendBlocker(String name);
@@ -594,6 +603,7 @@ public final class PowerManagerService extends SystemService
mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
+ mScreenBrightnessForVrSettingDefault = pm.getDefaultScreenBrightnessForVrSetting();
SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
@@ -640,6 +650,9 @@ public final class PowerManagerService extends SystemService
Settings.System.SCREEN_BRIGHTNESS),
false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS_FOR_VR),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.SCREEN_BRIGHTNESS_MODE),
false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
@@ -773,11 +786,17 @@ public final class PowerManagerService extends SystemService
}
}
- final int oldScreenBrightnessSetting = mScreenBrightnessSetting;
+ final int oldScreenBrightnessSetting = getCurrentBrightnessSettingLocked();
+
+ mScreenBrightnessForVrSetting = Settings.System.getIntForUser(resolver,
+ Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mScreenBrightnessForVrSettingDefault,
+ UserHandle.USER_CURRENT);
+
mScreenBrightnessSetting = Settings.System.getIntForUser(resolver,
Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessSettingDefault,
UserHandle.USER_CURRENT);
- if (oldScreenBrightnessSetting != mScreenBrightnessSetting) {
+
+ if (oldScreenBrightnessSetting != getCurrentBrightnessSettingLocked()) {
mTemporaryScreenBrightnessSettingOverride = -1;
}
@@ -811,6 +830,10 @@ public final class PowerManagerService extends SystemService
mDirty |= DIRTY_SETTINGS;
}
+ private int getCurrentBrightnessSettingLocked() {
+ return mIsVrModeEnabled ? mScreenBrightnessForVrSetting : mScreenBrightnessSetting;
+ }
+
private void postAfterBootCompleted(Runnable r) {
if (mBootCompleted) {
BackgroundThread.getHandler().post(r);
@@ -2069,6 +2092,7 @@ public final class PowerManagerService extends SystemService
|| !mDreamsSupportedConfig
|| !mDreamsEnabledSetting
|| !mDisplayPowerRequest.isBrightOrDim()
+ || mDisplayPowerRequest.isVr()
|| (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
| USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0
|| !mBootCompleted) {
@@ -2113,7 +2137,8 @@ public final class PowerManagerService extends SystemService
final boolean oldDisplayReady = mDisplayReady;
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
| DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
- | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_QUIESCENT)) != 0) {
+ | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED |
+ DIRTY_QUIESCENT)) != 0) {
mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
// Determine appropriate screen brightness and auto-brightness adjustments.
@@ -2127,6 +2152,9 @@ public final class PowerManagerService extends SystemService
// bootloader brightness and the default brightness to be identical.
autoBrightness = false;
brightnessSetByUser = false;
+ } else if (mIsVrModeEnabled) {
+ screenBrightness = mScreenBrightnessForVrSetting;
+ autoBrightness = false;
} else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
screenBrightness = mScreenBrightnessOverrideFromWindowManager;
autoBrightness = false;
@@ -2160,7 +2188,7 @@ public final class PowerManagerService extends SystemService
mDisplayPowerRequest.useAutoBrightness = autoBrightness;
mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled;
- mDisplayPowerRequest.boostScreenBrightness = mScreenBrightnessBoostInProgress;
+ mDisplayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
mDisplayPowerRequest.useTwilight = mBrightnessUseTwilight;
if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
@@ -2191,6 +2219,7 @@ public final class PowerManagerService extends SystemService
+ ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
+ ", mBootCompleted=" + mBootCompleted
+ ", mScreenBrightnessBoostInProgress=" + mScreenBrightnessBoostInProgress
+ + ", mIsVrModeEnabled= " + mIsVrModeEnabled
+ ", sQuiescent=" + sQuiescent);
}
}
@@ -2220,6 +2249,10 @@ public final class PowerManagerService extends SystemService
}
}
+ private boolean shouldBoostScreenBrightness() {
+ return !mIsVrModeEnabled && mScreenBrightnessBoostInProgress;
+ }
+
private static boolean isValidBrightness(int value) {
return value >= 0 && value <= 255;
}
@@ -2230,6 +2263,10 @@ public final class PowerManagerService extends SystemService
}
private int getDesiredScreenPolicyLocked() {
+ if (mIsVrModeEnabled) {
+ return DisplayPowerRequest.POLICY_VR;
+ }
+
if (mWakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
return DisplayPowerRequest.POLICY_OFF;
}
@@ -2333,7 +2370,7 @@ public final class PowerManagerService extends SystemService
};
private boolean shouldUseProximitySensorLocked() {
- return (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0;
+ return !mIsVrModeEnabled && (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0;
}
/**
@@ -3085,7 +3122,11 @@ public final class PowerManagerService extends SystemService
pw.println(" mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum);
pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum);
pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault);
+ pw.println(" mScreenBrightnessForVrSettingDefault="
+ + mScreenBrightnessForVrSettingDefault);
+ pw.println(" mScreenBrightnessForVrSetting=" + mScreenBrightnessForVrSetting);
pw.println(" mDoubleTapWakeEnabled=" + mDoubleTapWakeEnabled);
+ pw.println(" mIsVrModeEnabled=" + mIsVrModeEnabled);
final int sleepTimeout = getSleepTimeoutLocked();
final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
@@ -3223,6 +3264,14 @@ public final class PowerManagerService extends SystemService
@Override
public void onVrStateChanged(boolean enabled) {
powerHintInternal(PowerHint.VR_MODE, enabled ? 1 : 0);
+
+ synchronized (mLock) {
+ if (mIsVrModeEnabled != enabled) {
+ mIsVrModeEnabled = enabled;
+ mDirty |= DIRTY_VR_MODE_CHANGED;
+ updatePowerStateLocked();
+ }
+ }
}
};
@@ -3975,6 +4024,7 @@ public final class PowerManagerService extends SystemService
case Display.STATE_DOZE:
case Display.STATE_DOZE_SUSPEND:
case Display.STATE_ON:
+ case Display.STATE_VR:
break;
default:
screenState = Display.STATE_UNKNOWN;
diff --git a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
index b260e4e2fc34..b704eb1d991c 100644
--- a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
@@ -20,7 +20,7 @@ import android.util.Slog;
import java.io.File;
import java.io.IOException;
-import libcore.tzdata.update.TzDataBundleInstaller;
+import libcore.tzdata.update2.TimeZoneBundleInstaller;
/**
* An install receiver responsible for installing timezone data updates.
@@ -29,18 +29,19 @@ public class TzDataInstallReceiver extends ConfigUpdateInstallReceiver {
private static final String TAG = "TZDataInstallReceiver";
+ private static final File SYSTEM_TZ_DATA_FILE = new File("/system/usr/share/zoneinfo/tzdata");
private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
private static final String UPDATE_DIR_NAME = TZ_DATA_DIR.getPath() + "/updates/";
private static final String UPDATE_METADATA_DIR_NAME = "metadata/";
private static final String UPDATE_VERSION_FILE_NAME = "version";
private static final String UPDATE_CONTENT_FILE_NAME = "tzdata_bundle.zip";
- private final TzDataBundleInstaller installer;
+ private final TimeZoneBundleInstaller installer;
public TzDataInstallReceiver() {
super(UPDATE_DIR_NAME, UPDATE_CONTENT_FILE_NAME, UPDATE_METADATA_DIR_NAME,
UPDATE_VERSION_FILE_NAME);
- installer = new TzDataBundleInstaller(TAG, TZ_DATA_DIR);
+ installer = new TimeZoneBundleInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR);
}
@Override
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 35004c295681..cab39b56c3e9 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -27,21 +27,21 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WIND
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.ADD_STARTING;
-
-import android.graphics.Bitmap;
-import android.os.Trace;
-import com.android.server.AttributeCache;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.graphics.Bitmap;
import android.os.Binder;
import android.os.Debug;
+import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
+import android.os.Looper;
+import android.os.Trace;
import android.util.Slog;
import android.view.IApplicationToken;
+import android.view.WindowManagerPolicy.StartingSurface;
+import com.android.server.AttributeCache;
/**
* Controller for the app window token container. This is created by activity manager to link
* activity records to the app window token container they use in window manager.
@@ -52,6 +52,7 @@ public class AppWindowContainerController
extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
private final IApplicationToken mToken;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Runnable mOnWindowsDrawn = () -> {
if (mListener == null) {
@@ -80,6 +81,94 @@ public class AppWindowContainerController
mListener.onWindowsGone();
};
+ private final Runnable mAddStartingWindow = () -> {
+ final StartingData startingData;
+ final Configuration mergedOverrideConfiguration;
+
+ synchronized (mWindowMap) {
+ startingData = mContainer.startingData;
+ mergedOverrideConfiguration = mContainer.getMergedOverrideConfiguration();
+ }
+
+ if (startingData == null) {
+ // Animation has been canceled... do nothing.
+ return;
+ }
+
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
+ + this + ": pkg=" + mContainer.startingData.pkg);
+
+ StartingSurface contents = null;
+ try {
+ contents = mService.mPolicy.addSplashScreen(mContainer.token, startingData.pkg,
+ startingData.theme, startingData.compatInfo, startingData.nonLocalizedLabel,
+ startingData.labelRes, startingData.icon, startingData.logo,
+ startingData.windowFlags, mergedOverrideConfiguration);
+ } catch (Exception e) {
+ Slog.w(TAG_WM, "Exception when adding starting window", e);
+ }
+ if (contents != null) {
+ boolean abort = false;
+
+ synchronized(mWindowMap) {
+ if (mContainer.removed || mContainer.startingData == null) {
+ // If the window was successfully added, then
+ // we need to remove it.
+ if (mContainer.startingWindow != null) {
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
+ "Aborted starting " + mContainer
+ + ": removed=" + mContainer.removed
+ + " startingData=" + mContainer.startingData);
+ mContainer.startingWindow = null;
+ mContainer.startingData = null;
+ abort = true;
+ }
+ } else {
+ mContainer.startingSurface = contents;
+ }
+ if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
+ "Added starting " + mContainer
+ + ": startingWindow="
+ + mContainer.startingWindow + " startingView="
+ + mContainer.startingSurface);
+ }
+
+ if (abort) {
+ try {
+ mService.mPolicy.removeSplashScreen(mContainer.token, contents);
+ } catch (Exception e) {
+ Slog.w(TAG_WM, "Exception when removing starting window", e);
+ }
+ }
+ }
+ };
+
+ private final Runnable mRemoveStartingWindow = () -> {
+ IBinder token = null;
+ StartingSurface contents = null;
+ synchronized (mWindowMap) {
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting "
+ + mContainer + ": startingWindow="
+ + mContainer.startingWindow + " startingView="
+ + mContainer.startingSurface);
+ if (mContainer.startingWindow != null) {
+ contents = mContainer.startingSurface;
+ token = mContainer.token;
+ mContainer.startingData = null;
+ mContainer.startingSurface = null;
+ mContainer.startingWindow = null;
+ mContainer.startingDisplayed = false;
+ }
+ }
+ if (contents != null) {
+ try {
+ mService.mPolicy.removeSplashScreen(token, contents);
+ } catch (Exception e) {
+ Slog.w(TAG_WM, "Exception when removing starting window", e);
+ }
+ }
+ };
+
public AppWindowContainerController(IApplicationToken token,
AppWindowContainerListener listener, int taskId, int index, int requestedOrientation,
boolean fullscreen, boolean showForAllUsers, int configChanges,
@@ -393,19 +482,42 @@ public class AppWindowContainerController
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating StartingData");
mContainer.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
labelRes, icon, logo, windowFlags);
- final Message m = mService.mH.obtainMessage(ADD_STARTING, mContainer);
- // Note: we really want to do sendMessageAtFrontOfQueue() because we
- // want to process the message ASAP, before any other queued
- // messages.
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
- mService.mH.sendMessageAtFrontOfQueue(m);
+ scheduleAddStartingWindow();
}
return true;
}
+ void scheduleAddStartingWindow() {
+
+ // Note: we really want to do sendMessageAtFrontOfQueue() because we
+ // want to process the message ASAP, before any other queued
+ // messages.
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
+ mHandler.postAtFrontOfQueue(mAddStartingWindow);
+ }
+
public void removeStartingWindow() {
synchronized (mWindowMap) {
- mService.scheduleRemoveStartingWindowLocked(mContainer);
+ if (mHandler.hasCallbacks(mRemoveStartingWindow)) {
+ // Already scheduled.
+ return;
+ }
+
+ if (mContainer.startingWindow == null) {
+ if (mContainer.startingData != null) {
+ // Starting window has not been added yet, but it is scheduled to be added.
+ // Go ahead and cancel the request.
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
+ "Clearing startingData for token=" + mContainer);
+ mContainer.startingData = null;
+ }
+ return;
+ }
+
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, Debug.getCallers(1)
+ + ": Schedule remove starting " + mContainer
+ + " startingWindow=" + mContainer.startingWindow);
+ mHandler.post(mRemoveStartingWindow);
}
}
@@ -508,15 +620,15 @@ public class AppWindowContainerController
void reportWindowsDrawn() {
- mService.mH.post(mOnWindowsDrawn);
+ mHandler.post(mOnWindowsDrawn);
}
void reportWindowsVisible() {
- mService.mH.post(mOnWindowsVisible);
+ mHandler.post(mOnWindowsVisible);
}
void reportWindowsGone() {
- mService.mH.post(mOnWindowsGone);
+ mHandler.post(mOnWindowsGone);
}
/** Calls directly into activity manager so window manager lock shouldn't held. */
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 0a4875810aba..f4fa2206a22a 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -29,9 +29,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -47,22 +47,21 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.logWithStack;
-import android.os.Debug;
-import com.android.internal.util.ToBooleanFunction;
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.wm.WindowManagerService.H;
-
import android.annotation.NonNull;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.Debug;
import android.os.IBinder;
-import android.os.Message;
import android.os.SystemClock;
import android.util.Slog;
import android.view.IApplicationToken;
-import android.view.View;
import android.view.WindowManager;
+import android.view.WindowManagerPolicy.StartingSurface;
+
+import com.android.internal.util.ToBooleanFunction;
+import com.android.server.input.InputApplicationHandle;
+import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
import java.util.ArrayDeque;
@@ -138,7 +137,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
// Information about an application starting window if displayed.
StartingData startingData;
WindowState startingWindow;
- View startingView;
+ StartingSurface startingSurface;
boolean startingDisplayed;
boolean startingMoved;
boolean firstWindowDrawn;
@@ -213,8 +212,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
// it from behind the starting window, so there is no need for it to also be doing its
// own stuff.
winAnimator.clearAnimation();
- winAnimator.mService.mFinishedStarting.add(this);
- winAnimator.mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
+ if (getController() != null) {
+ getController().removeStartingWindow();
+ }
}
updateReportedVisibilityLocked();
}
@@ -439,8 +439,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
}
void onRemovedFromDisplay() {
- AppWindowToken startingToken = null;
-
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this);
boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
@@ -461,6 +459,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
+ this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
+ if (startingData != null && getController() != null) {
+ getController().removeStartingWindow();
+ }
+
final TaskStack stack = mTask.mStack;
if (delayed && !isEmpty()) {
// set the token aside because it has an active animation to be finished
@@ -477,9 +479,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
}
removed = true;
- if (startingData != null) {
- startingToken = this;
- }
stopFreezingScreen(true, true);
if (mService.mFocusedApp == this) {
if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this);
@@ -491,9 +490,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (!delayed) {
updateReportedVisibilityLocked();
}
-
- // Will only remove if startingToken non null.
- mService.scheduleRemoveStartingWindowLocked(startingToken);
}
void clearAnimatingFlags() {
@@ -557,7 +553,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
mAppStopped = true;
destroySurfaces();
// Remove any starting window that was added for this app if they are still around.
- mTask.mService.scheduleRemoveStartingWindowLocked(this);
+ if (getController() != null) {
+ getController().removeStartingWindow();
+ }
}
/**
@@ -667,16 +665,20 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
// TODO: Something smells about the code below...Is there a better way?
if (startingWindow == win) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
- mService.scheduleRemoveStartingWindowLocked(this);
+ if (getController() != null) {
+ getController().removeStartingWindow();
+ }
} else if (mChildren.size() == 0 && startingData != null) {
// If this is the last window and we had requested a starting transition window,
// well there is no point now.
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingWindow");
startingData = null;
- } else if (mChildren.size() == 1 && startingView != null) {
+ } else if (mChildren.size() == 1 && startingSurface != null) {
// If this is the last window except for a starting transition window,
// we need to get rid of the starting transition.
- mService.scheduleRemoveStartingWindowLocked(this);
+ if (getController() != null) {
+ getController().removeStartingWindow();
+ }
}
}
@@ -1015,7 +1017,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
}
final WindowState tStartingWindow = fromToken.startingWindow;
- if (tStartingWindow != null && fromToken.startingView != null) {
+ if (tStartingWindow != null && fromToken.startingSurface != null) {
// In this case, the starting icon has already been displayed, so start
// letting windows get shown immediately without any more transitions.
mService.mSkipAppTransitionAnimation = true;
@@ -1027,13 +1029,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
// Transfer the starting window over to the new token.
startingData = fromToken.startingData;
- startingView = fromToken.startingView;
+ startingSurface = fromToken.startingSurface;
startingDisplayed = fromToken.startingDisplayed;
fromToken.startingDisplayed = false;
startingWindow = tStartingWindow;
reportedVisible = fromToken.reportedVisible;
fromToken.startingData = null;
- fromToken.startingView = null;
+ fromToken.startingSurface = null;
fromToken.startingWindow = null;
fromToken.startingMoved = true;
tStartingWindow.mToken = this;
@@ -1080,10 +1082,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
startingData = fromToken.startingData;
fromToken.startingData = null;
fromToken.startingMoved = true;
- final Message m = mService.mH.obtainMessage(H.ADD_STARTING, this);
- // Note: we really want to do sendMessageAtFrontOfQueue() because we want to process the
- // message ASAP, before any other queued messages.
- mService.mH.sendMessageAtFrontOfQueue(m);
+ if (getController() != null) {
+ getController().scheduleAddStartingWindow();
+ }
return true;
}
@@ -1421,10 +1422,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
pw.print(" mIsExiting="); pw.println(mIsExiting);
}
- if (startingWindow != null || startingView != null
+ if (startingWindow != null || startingSurface != null
|| startingDisplayed || startingMoved) {
pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
- pw.print(" startingView="); pw.print(startingView);
+ pw.print(" startingSurface="); pw.print(startingSurface);
pw.print(" startingDisplayed="); pw.print(startingDisplayed);
pw.print(" startingMoved="); pw.println(startingMoved);
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index dc06d129c5bb..6ac172b26e24 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -322,7 +322,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
if (!configChanged) {
return null;
}
- displayContent.onOverrideConfigurationChanged(currentConfig);
+ displayContent.onOverrideConfigurationChanged(newConfiguration);
if (displayId == DEFAULT_DISPLAY) {
// Override configuration of the default display duplicates global config. In this case
@@ -498,7 +498,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
"RECOVER DESTROY", false);
winAnimator.destroySurface();
- mService.scheduleRemoveStartingWindowLocked(winAnimator.mWin.mAppToken);
+ if (winAnimator.mWin.mAppToken != null
+ && winAnimator.mWin.mAppToken.getController() != null) {
+ winAnimator.mWin.mAppToken.getController().removeStartingWindow();
+ }
}
try {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 38cb54320a1b..4e259354ae2a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2851,33 +2851,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- void scheduleRemoveStartingWindowLocked(AppWindowToken wtoken) {
- if (wtoken == null) {
- return;
- }
- if (mH.hasMessages(H.REMOVE_STARTING, wtoken)) {
- // Already scheduled.
- return;
- }
-
- if (wtoken.startingWindow == null) {
- if (wtoken.startingData != null) {
- // Starting window has not been added yet, but it is scheduled to be added.
- // Go ahead and cancel the request.
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
- "Clearing startingData for token=" + wtoken);
- wtoken.startingData = null;
- }
- return;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, Debug.getCallers(1) +
- ": Schedule remove starting " + wtoken + (wtoken != null ?
- " startingWindow=" + wtoken.startingWindow : ""));
- Message m = mH.obtainMessage(H.REMOVE_STARTING, wtoken);
- mH.sendMessage(m);
- }
-
public void moveTaskToTop(int taskId) {
final long origId = Binder.clearCallingIdentity();
try {
@@ -5578,9 +5551,6 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int REPORT_FOCUS_CHANGE = 2;
public static final int REPORT_LOSING_FOCUS = 3;
public static final int DO_TRAVERSAL = 4;
- public static final int ADD_STARTING = 5;
- public static final int REMOVE_STARTING = 6;
- public static final int FINISHED_STARTING = 7;
public static final int WINDOW_FREEZE_TIMEOUT = 11;
public static final int APP_TRANSITION_TIMEOUT = 13;
@@ -5722,126 +5692,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
} break;
- case ADD_STARTING: {
- final AppWindowToken wtoken = (AppWindowToken)msg.obj;
- final StartingData sd = wtoken.startingData;
-
- if (sd == null) {
- // Animation has been canceled... do nothing.
- return;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
- + wtoken + ": pkg=" + sd.pkg);
-
- View view = null;
- try {
- view = mPolicy.addStartingWindow(wtoken.token, sd.pkg, sd.theme,
- sd.compatInfo, sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo,
- sd.windowFlags, wtoken.getMergedOverrideConfiguration());
- } catch (Exception e) {
- Slog.w(TAG_WM, "Exception when adding starting window", e);
- }
-
- if (view != null) {
- boolean abort = false;
-
- synchronized(mWindowMap) {
- if (wtoken.removed || wtoken.startingData == null) {
- // If the window was successfully added, then
- // we need to remove it.
- if (wtoken.startingWindow != null) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
- "Aborted starting " + wtoken
- + ": removed=" + wtoken.removed
- + " startingData=" + wtoken.startingData);
- wtoken.startingWindow = null;
- wtoken.startingData = null;
- abort = true;
- }
- } else {
- wtoken.startingView = view;
- }
- if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
- "Added starting " + wtoken
- + ": startingWindow="
- + wtoken.startingWindow + " startingView="
- + wtoken.startingView);
- }
-
- if (abort) {
- try {
- mPolicy.removeStartingWindow(wtoken.token, view);
- } catch (Exception e) {
- Slog.w(TAG_WM, "Exception when removing starting window", e);
- }
- }
- }
- } break;
-
- case REMOVE_STARTING: {
- final AppWindowToken wtoken = (AppWindowToken)msg.obj;
- IBinder token = null;
- View view = null;
- synchronized (mWindowMap) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting "
- + wtoken + ": startingWindow="
- + wtoken.startingWindow + " startingView="
- + wtoken.startingView);
- if (wtoken.startingWindow != null) {
- view = wtoken.startingView;
- token = wtoken.token;
- wtoken.startingData = null;
- wtoken.startingView = null;
- wtoken.startingWindow = null;
- wtoken.startingDisplayed = false;
- }
- }
- if (view != null) {
- try {
- mPolicy.removeStartingWindow(token, view);
- } catch (Exception e) {
- Slog.w(TAG_WM, "Exception when removing starting window", e);
- }
- }
- } break;
-
- case FINISHED_STARTING: {
- IBinder token = null;
- View view = null;
- while (true) {
- synchronized (mWindowMap) {
- final int N = mFinishedStarting.size();
- if (N <= 0) {
- break;
- }
- AppWindowToken wtoken = mFinishedStarting.remove(N-1);
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
- "Finished starting " + wtoken
- + ": startingWindow=" + wtoken.startingWindow
- + " startingView=" + wtoken.startingView);
-
- if (wtoken.startingWindow == null) {
- continue;
- }
-
- view = wtoken.startingView;
- token = wtoken.token;
- wtoken.startingData = null;
- wtoken.startingView = null;
- wtoken.startingWindow = null;
- wtoken.startingDisplayed = false;
- }
-
- try {
- mPolicy.removeStartingWindow(token, view);
- } catch (Exception e) {
- Slog.w(TAG_WM, "Exception when removing starting window", e);
- }
- }
- } break;
-
case WINDOW_FREEZE_TIMEOUT: {
// TODO(multidisplay): Can non-default displays rotate?
synchronized (mWindowMap) {
@@ -7348,21 +7198,6 @@ public class WindowManagerService extends IWindowManager.Stub
private void dumpTokensLocked(PrintWriter pw, boolean dumpAll) {
pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)");
mRoot.dumpTokens(pw, dumpAll);
- if (!mFinishedStarting.isEmpty()) {
- pw.println();
- pw.println(" Finishing start of application tokens:");
- for (int i=mFinishedStarting.size()-1; i>=0; i--) {
- WindowToken token = mFinishedStarting.get(i);
- pw.print(" Finished Starting #"); pw.print(i);
- pw.print(' '); pw.print(token);
- if (dumpAll) {
- pw.println(':');
- token.dump(pw, " ");
- } else {
- pw.println();
- }
- }
- }
if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty()) {
pw.println();
if (mOpeningApps.size() > 0) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e5ed18d118ad..19ef44cace33 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -461,16 +461,7 @@ class WindowStateAnimator {
mStackClip = STACK_CLIP_BEFORE_ANIM;
mWin.checkPolicyVisibilityChange();
mTransformation.clear();
- if (mDrawState == HAS_DRAWN
- && mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
- && mWin.mAppToken != null
- && mWin.mAppToken.firstWindowDrawn
- && mWin.mAppToken.startingData != null) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Finish starting "
- + mWin.mToken + ": first real window done animating");
- mService.mFinishedStarting.add(mWin.mAppToken);
- mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
- } else if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
+ if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
// Upon completion of a not-visible to visible status bar animation a relayout is
// required.
if (displayContent != null) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 1aabd5ea66f9..4df100105bcb 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -441,8 +441,9 @@ class WindowSurfacePlacer {
wtoken.deferClearAllDrawn = false;
// Ensure that apps that are mid-starting are also scheduled to have their
// starting windows removed after the animation is complete
- if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) {
- mService.scheduleRemoveStartingWindowLocked(wtoken);
+ if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit
+ && wtoken.getController() != null) {
+ wtoken.getController().removeStartingWindow();
}
mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
diff --git a/services/core/jni/com_android_server_connectivity_Vpn.cpp b/services/core/jni/com_android_server_connectivity_Vpn.cpp
index c54d732b9285..4d85d9a558aa 100644
--- a/services/core/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/core/jni/com_android_server_connectivity_Vpn.cpp
@@ -17,24 +17,25 @@
#define LOG_NDEBUG 0
#define LOG_TAG "VpnJni"
-#include <cutils/log.h>
-#include "netutils/ifc.h"
-#include <stdio.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
-
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/route.h>
#include <linux/ipv6_route.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <log/log.h>
+
+#include "netutils/ifc.h"
#include "jni.h"
#include "JNIHelp.h"
diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp
index cf7f1cbfd0be..2fd0603c8f89 100644
--- a/services/core/jni/com_android_server_lights_LightsService.cpp
+++ b/services/core/jni/com_android_server_lights_LightsService.cpp
@@ -103,16 +103,15 @@ static void setLight_native(
state.flashMode = Flash::NONE;
} else {
// Only set non-brightness settings when not in low-persistence mode
- state.color = colorARGB;
state.flashMode = flash;
state.flashOnMs = onMS;
state.flashOffMs = offMS;
}
+ state.color = colorARGB;
state.brightnessMode = brightness;
Status status;
-
{
ALOGD_IF_SLOW(50, "Excessive delay setting light");
Return<Status> ret = gLight->setLight(type, state);
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 09886db17af6..fa6405a1dd87 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -59,6 +59,12 @@ static jmethodID method_reportGeofenceResumeStatus;
static jmethodID method_reportMeasurementData;
static jmethodID method_reportNavigationMessages;
+/*
+ * Save a pointer to JavaVm to attach/detach threads executing
+ * callback methods that need to make JNI calls.
+ */
+static JavaVM* sJvm;
+
using android::OK;
using android::sp;
using android::status_t;
@@ -216,6 +222,62 @@ static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodNa
}
}
+class ScopedJniThreadAttach {
+public:
+ ScopedJniThreadAttach() {
+ /*
+ * attachResult will also be JNI_OK if the thead was already attached to
+ * JNI before the call to AttachCurrentThread().
+ */
+ jint attachResult = sJvm->AttachCurrentThread(&mEnv, nullptr);
+ LOG_ALWAYS_FATAL_IF(attachResult != JNI_OK, "Unable to attach thread. Error %d",
+ attachResult);
+ }
+
+ ~ScopedJniThreadAttach() {
+ jint detachResult = sJvm->DetachCurrentThread();
+ /*
+ * Return if the thread was already detached. Log error for any other
+ * failure.
+ */
+ if (detachResult == JNI_EDETACHED) {
+ return;
+ }
+
+ LOG_ALWAYS_FATAL_IF(detachResult != JNI_OK, "Unable to detach thread. Error %d",
+ detachResult);
+ }
+
+ JNIEnv* getEnv() {
+ /*
+ * Checking validity of mEnv in case the thread was detached elsewhere.
+ */
+ LOG_ALWAYS_FATAL_IF(AndroidRuntime::getJNIEnv() != mEnv);
+ return mEnv;
+ }
+
+private:
+ JNIEnv* mEnv = nullptr;
+};
+
+thread_local std::unique_ptr<ScopedJniThreadAttach> tJniThreadAttacher;
+
+static JNIEnv* getJniEnv() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ /*
+ * If env is nullptr, the thread is not already attached to
+ * JNI. It is attached below and the destructor for ScopedJniThreadAttach
+ * will detach it on thread exit.
+ */
+ if (env == nullptr) {
+ tJniThreadAttacher.reset(new ScopedJniThreadAttach());
+ env = tJniThreadAttacher->getEnv();
+ }
+
+ return env;
+}
+
/*
* GnssCallback class implements the callback methods for IGnss interface.
*/
@@ -247,7 +309,7 @@ size_t GnssCallback::sGnssSvListSize = 0;
Return<void> GnssCallback::gnssLocationCb(
const ::android::hardware::gnss::V1_0::GnssLocation& location) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj,
method_reportLocation,
location.gnssLocationFlags,
@@ -263,14 +325,14 @@ Return<void> GnssCallback::gnssLocationCb(
}
Return<void> GnssCallback::gnssStatusCb(const IGnssCallback::GnssStatusValue status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
}
Return<void> GnssCallback::gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
sGnssSvListSize = svStatus.numSvs;
if (sGnssSvListSize > static_cast<uint32_t>(
@@ -292,7 +354,7 @@ Return<void> GnssCallback::gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svS
Return<void> GnssCallback::gnssNmeaCb(
int64_t timestamp, const ::android::hardware::hidl_string& nmea) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
/*
* The Java code will call back to read these values.
* We do this to avoid creating unnecessary String objects.
@@ -308,7 +370,7 @@ Return<void> GnssCallback::gnssNmeaCb(
Return<void> GnssCallback::gnssSetCapabilitesCb(uint32_t capabilities) {
ALOGD("%s: %du\n", __func__, capabilities);
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_setEngineCapabilities, capabilities);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
@@ -325,7 +387,7 @@ Return<void> GnssCallback::gnssReleaseWakelockCb() {
}
Return<void> GnssCallback::gnssRequestTimeCb() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
@@ -334,7 +396,7 @@ Return<void> GnssCallback::gnssRequestTimeCb() {
Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) {
ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw);
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware,
info.yearOfHw);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -350,7 +412,7 @@ class GnssXtraCallback : public IGnssXtraCallback {
* interface.
*/
Return<void> GnssXtraCallback::downloadRequestCb() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_xtraDownloadRequest);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
@@ -385,7 +447,7 @@ Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
const android::hardware::gnss::V1_0::GnssLocation& location,
GeofenceTransition transition,
hardware::gnss::V1_0::GnssUtcTime timestamp) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj,
method_reportGeofenceTransition,
@@ -408,7 +470,7 @@ Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb(
GeofenceAvailability status,
const android::hardware::gnss::V1_0::GnssLocation& location) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj,
method_reportGeofenceStatus,
status,
@@ -426,7 +488,7 @@ Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb(
Return<void> GnssGeofenceCallback::gnssGeofenceAddCb(int32_t geofenceId,
GeofenceStatus status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
ALOGE("%s: Error in adding a Geofence: %d\n", __func__, status);
}
@@ -441,7 +503,7 @@ Return<void> GnssGeofenceCallback::gnssGeofenceAddCb(int32_t geofenceId,
Return<void> GnssGeofenceCallback::gnssGeofenceRemoveCb(int32_t geofenceId,
GeofenceStatus status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
ALOGE("%s: Error in removing a Geofence: %d\n", __func__, status);
}
@@ -455,7 +517,7 @@ Return<void> GnssGeofenceCallback::gnssGeofenceRemoveCb(int32_t geofenceId,
Return<void> GnssGeofenceCallback::gnssGeofencePauseCb(int32_t geofenceId,
GeofenceStatus status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
ALOGE("%s: Error in pausing Geofence: %d\n", __func__, status);
}
@@ -469,7 +531,7 @@ Return<void> GnssGeofenceCallback::gnssGeofencePauseCb(int32_t geofenceId,
Return<void> GnssGeofenceCallback::gnssGeofenceResumeCb(int32_t geofenceId,
GeofenceStatus status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
ALOGE("%s: Error in resuming Geofence: %d\n", __func__, status);
}
@@ -496,7 +558,7 @@ struct GnssNavigationMessageCallback : public IGnssNavigationMessageCallback {
Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb(
const IGnssNavigationMessageCallback::GnssNavigationMessage& message) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
size_t dataLength = message.data.size();
@@ -545,7 +607,7 @@ struct GnssMeasurementCallback : public IGnssMeasurementCallback {
Return<void> GnssMeasurementCallback::GnssMeasurementCb(
const IGnssMeasurementCallback::GnssData& data) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
jobject clock;
jobjectArray measurementArray;
@@ -700,7 +762,7 @@ struct GnssNiCallback : public IGnssNiCallback {
Return<void> GnssNiCallback::niNotifyCb(
const IGnssNiCallback::GnssNiNotification& notification) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
jstring requestorId = env->NewStringUTF(notification.requestorId.c_str());
jstring text = env->NewStringUTF(notification.notificationMessage.c_str());
@@ -742,7 +804,7 @@ struct AGnssCallback : public IAGnssCallback {
Return<void> AGnssCallback::agnssStatusIpV6Cb(
const IAGnssCallback::AGnssStatusIpV6& agps_status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
jbyteArray byteArray = NULL;
bool isSupported = false;
@@ -778,7 +840,7 @@ Return<void> AGnssCallback::agnssStatusIpV6Cb(
Return<void> AGnssCallback::agnssStatusIpV4Cb(
const IAGnssCallback::AGnssStatusIpV4& agps_status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
jbyteArray byteArray = NULL;
uint32_t ipAddr = agps_status.ipV4Addr;
@@ -813,7 +875,7 @@ jbyteArray AGnssCallback::convertToIpV4(uint32_t ip) {
return NULL;
}
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
jbyteArray byteArray = env->NewByteArray(4);
if (byteArray == NULL) {
ALOGE("Unable to allocate byte array for IPv4 address");
@@ -832,19 +894,19 @@ jbyteArray AGnssCallback::convertToIpV4(uint32_t ip) {
* interface.
*/
struct AGnssRilCallback : IAGnssRilCallback {
- Return<void> requestSetIdCb(IAGnssRilCallback::ID setIdFlag) override;
+ Return<void> requestSetIdCb(uint32_t setIdFlag) override;
Return<void> requestRefLocCb() override;
};
-Return<void> AGnssRilCallback::requestSetIdCb(IAGnssRilCallback::ID setIdFlag) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+Return<void> AGnssRilCallback::requestSetIdCb(uint32_t setIdFlag) {
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_requestSetID, setIdFlag);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
}
Return<void> AGnssRilCallback::requestRefLocCb() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_requestRefLocation);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
@@ -885,6 +947,14 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env,
"reportNavigationMessage",
"(Landroid/location/GnssNavigationMessage;)V");
+ /*
+ * Save a pointer to JVM.
+ */
+ jint jvmStatus = env->GetJavaVM(&sJvm);
+ if (jvmStatus != JNI_OK) {
+ LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus);
+ }
+
// TODO(b/31632518)
gnssHal = IGnss::getService("gnss");
if (gnssHal != nullptr) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9f6606278cec..2e5b6876719e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10097,6 +10097,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
.setSmallIcon(R.drawable.ic_qs_network_logging)
.setContentTitle(mContext.getString(R.string.network_logging_notification_title))
.setContentText(mContext.getString(R.string.network_logging_notification_text))
+ .setTicker(mContext.getString(R.string.network_logging_notification_title))
.setShowWhen(true)
.setContentIntent(pendingIntent)
.build();
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index af570b0dff08..1189dae7b21c 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -23,6 +23,7 @@ import static android.net.NetworkScoreManager.CACHE_FILTER_NONE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -61,6 +62,7 @@ import android.net.RecommendationResult;
import android.net.ScoredNetwork;
import android.net.WifiKey;
import android.net.wifi.WifiConfiguration;
+import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
@@ -261,7 +263,7 @@ public class NetworkScoreServiceTest {
@Test
public void testUpdateScores_notActiveScorer() {
- when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
+ bindToScorer(false /*callerIsScorer*/);
try {
mNetworkScoreService.updateScores(new ScoredNetwork[0]);
@@ -273,7 +275,7 @@ public class NetworkScoreServiceTest {
@Test
public void testUpdateScores_oneRegisteredCache() throws RemoteException {
- when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
+ bindToScorer(true /*callerIsScorer*/);
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
mNetworkScoreCache, CACHE_FILTER_NONE);
@@ -288,7 +290,7 @@ public class NetworkScoreServiceTest {
@Test
public void testUpdateScores_twoRegisteredCaches() throws RemoteException {
- when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
+ bindToScorer(true /*callerIsScorer*/);
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
mNetworkScoreCache, CACHE_FILTER_NONE);
@@ -323,7 +325,7 @@ public class NetworkScoreServiceTest {
@Test
public void testClearScores_notActiveScorer_noRequestNetworkScoresPermission() {
- when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
+ bindToScorer(false /*callerIsScorer*/);
when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES))
.thenReturn(PackageManager.PERMISSION_DENIED);
try {
@@ -336,7 +338,7 @@ public class NetworkScoreServiceTest {
@Test
public void testClearScores_activeScorer_noRequestNetworkScoresPermission() {
- when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
+ bindToScorer(true /*callerIsScorer*/);
when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES))
.thenReturn(PackageManager.PERMISSION_DENIED);
@@ -345,7 +347,7 @@ public class NetworkScoreServiceTest {
@Test
public void testClearScores_activeScorer() throws RemoteException {
- when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
+ bindToScorer(true /*callerIsScorer*/);
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
CACHE_FILTER_NONE);
@@ -357,7 +359,7 @@ public class NetworkScoreServiceTest {
@Test
public void testClearScores_notActiveScorer_hasRequestNetworkScoresPermission()
throws RemoteException {
- when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
+ bindToScorer(false /*callerIsScorer*/);
when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES))
.thenReturn(PackageManager.PERMISSION_GRANTED);
@@ -383,7 +385,7 @@ public class NetworkScoreServiceTest {
@Test
public void testDisableScoring_notActiveScorer_noRequestNetworkScoresPermission() {
- when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
+ bindToScorer(false /*callerIsScorer*/);
when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES))
.thenReturn(PackageManager.PERMISSION_DENIED);
@@ -448,6 +450,42 @@ public class NetworkScoreServiceTest {
assertFalse(stringWriter.toString().isEmpty());
}
+ @Test
+ public void testIsCallerActiveScorer_noBoundService() throws Exception {
+ mNetworkScoreService.systemRunning();
+
+ assertFalse(mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid()));
+ }
+
+ @Test
+ public void testIsCallerActiveScorer_boundServiceIsNotCaller() throws Exception {
+ bindToScorer(false /*callerIsScorer*/);
+
+ assertFalse(mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid()));
+ }
+
+ @Test
+ public void testIsCallerActiveScorer_boundServiceIsCaller() throws Exception {
+ bindToScorer(true /*callerIsScorer*/);
+
+ assertTrue(mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid()));
+ }
+
+ @Test
+ public void testGetActiveScorerPackage_notActive() throws Exception {
+ mNetworkScoreService.systemRunning();
+
+ assertNull(mNetworkScoreService.getActiveScorerPackage());
+ }
+
+ @Test
+ public void testGetActiveScorerPackage_active() throws Exception {
+ when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
+ mNetworkScoreService.systemRunning();
+
+ assertEquals(NEW_SCORER.packageName, mNetworkScoreService.getActiveScorerPackage());
+ }
+
// "injects" the mock INetworkRecommendationProvider into the NetworkScoreService.
private void injectProvider() {
final ComponentName componentName = new ComponentName(NEW_SCORER.packageName,
@@ -467,4 +505,14 @@ public class NetworkScoreServiceTest {
});
mNetworkScoreService.systemRunning();
}
+
+ private void bindToScorer(boolean callerIsScorer) {
+ final int callingUid = callerIsScorer ? Binder.getCallingUid() : 0;
+ NetworkScorerAppData appData = new NetworkScorerAppData(NEW_SCORER.packageName,
+ callingUid, NEW_SCORER.recommendationServiceClassName);
+ when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(appData);
+ when(mContext.bindServiceAsUser(isA(Intent.class), isA(ServiceConnection.class), anyInt(),
+ isA(UserHandle.class))).thenReturn(true);
+ mNetworkScoreService.systemRunning();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 10ca90207d85..228b8e0d82fb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -1403,7 +1403,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
protected ShortcutInfo makeShortcut(String id, String title, ComponentName activity,
Icon icon, Intent intent, int rank) {
final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id)
- .setActivity(new ComponentName(mClientContext.getPackageName(), "dummy"))
+ .setActivity(new ComponentName(mClientContext.getPackageName(), "main"))
.setShortLabel(title)
.setRank(rank)
.setIntent(intent);
@@ -1432,7 +1432,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
protected ShortcutInfo makeShortcut(String id, String title, ComponentName activity,
Icon icon, Intent[] intents, int rank) {
final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id)
- .setActivity(new ComponentName(mClientContext.getPackageName(), "dummy"))
+ .setActivity(new ComponentName(mClientContext.getPackageName(), "main"))
.setShortLabel(title)
.setRank(rank)
.setIntents(intents);
@@ -1455,7 +1455,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
protected ShortcutInfo makeShortcutWithExtras(String id, Intent intent,
PersistableBundle extras) {
final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id)
- .setActivity(new ComponentName(mClientContext.getPackageName(), "dummy"))
+ .setActivity(new ComponentName(mClientContext.getPackageName(), "main"))
.setShortLabel("title-" + id)
.setExtras(extras)
.setIntent(intent);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
index fbf0ed2938eb..bcd72fcd6181 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -38,6 +38,7 @@ import android.graphics.drawable.Icon;
import android.os.UserHandle;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
import android.util.Pair;
import com.android.frameworks.servicestests.R;
@@ -227,8 +228,9 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
private void assertPinItemRequest(PinItemRequest actualRequest) {
assertNotNull(actualRequest);
-
assertEquals(PinItemRequest.REQUEST_TYPE_SHORTCUT, actualRequest.getRequestType());
+
+ Log.i(TAG, "Requested shortcut: " + actualRequest.getShortcutInfo().toInsecureString());
}
/**
@@ -243,9 +245,16 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
- ShortcutInfo s1 = makeShortcutWithIcon("s1", res32x32);
+ /// Create a shortcut with no target activity.
+ final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, "s1")
+ .setShortLabel("Title-" + "s1")
+ .setIcon(res32x32)
+ .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class));
+ final ShortcutInfo s = b.build();
+
+ assertNull(s.getActivity());
- assertTrue(mManager.requestPinShortcut(s1,
+ assertTrue(mManager.requestPinShortcut(s,
resultIntent == null ? null : resultIntent.getIntentSender()));
verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
@@ -271,6 +280,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
assertWith(request.getShortcutInfo())
.haveIds("s1")
.areAllOrphan()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, MAIN_ACTIVITY_CLASS))
.areAllWithNoIntent();
assertAllHaveIcon(list(request.getShortcutInfo()));
@@ -295,6 +305,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.areAllNotDynamic()
.areAllEnabled()
.areAllPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, MAIN_ACTIVITY_CLASS))
.areAllWithIntent();
});
}
@@ -310,6 +321,145 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
checkRequestPinShortcut(resultIntent);
}
+ public void testRequestPinShortcut_explicitTargetActivity() {
+ setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+ setDefaultLauncher(USER_10, mMainActivityFetcher.apply(LAUNCHER_2, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ ShortcutInfo s1 = makeShortcutWithActivity("s1",
+ new ComponentName(CALLING_PACKAGE_1, "different_activity"));
+
+ assertTrue(mManager.requestPinShortcut(s1, null));
+
+ verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+ // Shortcut shouldn't be registered yet.
+ assertWith(getCallerShortcuts())
+ .isEmpty();
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ // Check the intent passed to startActivityAsUser().
+ final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+ verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+ assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+ // Check the request object.
+ final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+ assertPinItemRequest(request);
+
+ assertWith(request.getShortcutInfo())
+ .haveIds("s1")
+ .areAllOrphan()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "different_activity"))
+ .areAllWithNoIntent();
+
+ // Accept the request.
+ assertForLauncherCallbackNoThrow(mLauncherApps,
+ () -> assertTrue(request.accept()))
+ .assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_P0)
+ .haveIds("s1");
+ });
+
+ verify(mServiceContext, times(1)).sendIntentSender(eq(null));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("s1")
+ .areAllNotDynamic()
+ .areAllEnabled()
+ .areAllPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "different_activity"))
+ .areAllWithIntent();
+ });
+ }
+
+ public void testRequestPinShortcut_wrongTargetActivity() {
+ setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ // Create dynamic shortcut
+ ShortcutInfo s1 = makeShortcutWithActivity("s1",
+ new ComponentName("wrong_package", "different_activity"));
+
+ assertExpectException(IllegalStateException.class, "not belong to package", () -> {
+ assertTrue(mManager.requestPinShortcut(s1, /* resultIntent=*/ null));
+ });
+
+ verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+ verify(mServiceContext, times(0)).startActivityAsUser(
+ any(Intent.class), any(UserHandle.class));
+ });
+ }
+
+ public void testRequestPinShortcut_noTargetActivity_noMainActivity() {
+ setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+ setDefaultLauncher(USER_10, mMainActivityFetcher.apply(LAUNCHER_2, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ /// Create a shortcut with no target activity.
+ final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, "s1")
+ .setShortLabel("Title-" + "s1")
+ .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class));
+ final ShortcutInfo s = b.build();
+
+ assertNull(s.getActivity());
+
+ // Caller has no main activity.
+ mMainActivityFetcher = (packageName, userId) -> null;
+
+ assertTrue(mManager.requestPinShortcut(s, null));
+
+ verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+ // Shortcut shouldn't be registered yet.
+ assertWith(getCallerShortcuts())
+ .isEmpty();
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ // Check the intent passed to startActivityAsUser().
+ final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+ verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+ assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+ // Check the request object.
+ final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+ assertPinItemRequest(request);
+
+ assertWith(request.getShortcutInfo())
+ .haveIds("s1")
+ .areAllOrphan()
+ .areAllWithNoActivity() // Activity is not set; expected.
+ .areAllWithNoIntent();
+
+ // Accept the request.
+ assertForLauncherCallbackNoThrow(mLauncherApps,
+ () -> assertTrue(request.accept()))
+ .assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_P0)
+ .haveIds("s1");
+ });
+
+ verify(mServiceContext, times(1)).sendIntentSender(eq(null));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("s1")
+ .areAllNotDynamic()
+ .areAllEnabled()
+ .areAllPinned()
+ .areAllWithNoActivity() // Activity is not set; expected.
+ .areAllWithIntent();
+ });
+
+ }
+
public void testRequestPinShortcut_dynamicExists() {
setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
@@ -328,6 +478,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
assertWith(getCallerShortcuts())
.haveIds("s1")
.areAllDynamic()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllNotPinned();
});
@@ -348,6 +499,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1")
.areAllDynamic()
.areAllNotPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllWithNoIntent();
assertAllHaveIcon(list(request.getShortcutInfo()));
@@ -361,6 +513,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1")
.areAllDynamic()
.areAllEnabled()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllPinned();
});
}
@@ -379,6 +532,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
assertWith(getCallerShortcuts())
.haveIds("ms1")
.areAllManifest()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllNotPinned();
});
@@ -399,6 +554,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("ms1")
.areAllManifest()
.areAllNotPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllWithNoIntent();
assertAllHaveIcon(list(request.getShortcutInfo()));
@@ -412,6 +569,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("ms1")
.areAllManifest()
.areAllEnabled()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllPinned();
});
}
@@ -431,6 +590,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
assertWith(getCallerShortcuts())
.haveIds("s1")
.areAllDynamic()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllPinned();
assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
@@ -456,6 +616,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
assertWith(getCallerShortcuts())
.haveIds("ms1")
.areAllManifest()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllPinned();
assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
@@ -483,6 +645,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1")
.areAllNotDynamic()
.areAllEnabled()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllPinned();
assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
@@ -511,6 +674,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1")
.areAllNotDynamic()
.areAllDisabled()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllPinned();
assertExpectException(IllegalArgumentException.class, "exists but disabled", () -> {
@@ -541,6 +705,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("ms1")
.areAllNotManifest()
.areAllDisabled()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllPinned();
assertExpectException(IllegalArgumentException.class, "exists but disabled", () -> {
@@ -570,6 +736,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
assertWith(getCallerShortcuts())
.haveIds("s1")
.areAllDynamic()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllPinned();
// The shortcut is already pinned, but not by the current launcher, so it'll still
@@ -597,6 +764,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1")
.areAllDynamic()
.areAllNotPinned() // Note it's not pinned by this launcher.
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllWithNoIntent();
// Accept the request.
@@ -608,6 +776,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1")
.areAllDynamic()
.areAllEnabled()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllPinned();
});
}
@@ -629,6 +798,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
assertWith(getCallerShortcuts())
.haveIds("ms1")
.areAllManifest()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllPinned();
// The shortcut is already pinned, but not by the current launcher, so it'll still
@@ -656,6 +827,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("ms1")
.areAllManifest()
.areAllNotPinned() // Note it's not pinned by this launcher.
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllWithNoIntent();
// Accept the request.
@@ -667,6 +840,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("ms1")
.areAllManifest()
.areAllEnabled()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllPinned();
});
}
@@ -710,6 +885,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1")
.areAllDynamic()
.areAllNotPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllWithNoIntent();
// Accept the request.
@@ -719,6 +895,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1", "s2")
.areAllDynamic()
.areAllEnabled()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllPinned();
});
@@ -727,6 +904,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1", "s2")
.areAllDynamic()
.areAllEnabled()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllPinned();
});
}
@@ -752,6 +930,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
assertWith(getCallerShortcuts())
.haveIds("s1")
.areAllDynamic()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllNotPinned();
});
@@ -772,6 +951,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1")
.areAllDynamic()
.areAllNotPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllWithNoIntent();
assertAllHaveIcon(list(request.getShortcutInfo()));
@@ -786,6 +966,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.areAllDynamic()
.areAllEnabled()
.areAllPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.forShortcutWithId("s1", (si) -> {
// Still the original title.
assertEquals("Title-s1", si.getShortLabel());
@@ -810,6 +991,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
assertWith(getCallerShortcuts())
.haveIds("ms1")
.areAllManifest()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllNotPinned();
});
@@ -830,6 +1013,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("ms1")
.areAllManifest()
.areAllNotPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllWithNoIntent();
assertAllHaveIcon(list(request.getShortcutInfo()));
@@ -844,6 +1029,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.areAllManifest()
.areAllEnabled()
.areAllPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.forShortcutWithId("ms1", (si) -> {
// Still the original title.
// Title should be something like:
@@ -893,6 +1080,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1")
.areAllDynamic()
.areAllNotPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllWithNoIntent();
// Accept the request -> should fail.
@@ -950,9 +1138,9 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1")
.areAllDynamic()
.areAllNotPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllWithNoIntent();
- // Accept the request -> should fail.
assertTrue(request.accept());
});
@@ -1004,6 +1192,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("ms1")
.areAllManifest()
.areAllNotPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllWithNoIntent();
// Accept the request -> should fail.
@@ -1059,10 +1249,11 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("ms1")
.areAllManifest()
.areAllNotPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllWithNoIntent();
- // Accept the request -> should fail.
assertTrue(request.accept());
});
@@ -1071,6 +1262,9 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("ms1")
.areAllMutable() // Note it's no longer immutable.
.areAllFloating()
+
+ // Note it's the activity from makeShortcutWithShortLabel().
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.forShortcutWithId("ms1", si -> {
assertEquals("new", si.getShortLabel());
});
@@ -1104,6 +1298,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
mManager.disableShortcuts(list("s1"));
assertWith(getCallerShortcuts())
.haveIds("s1")
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllDisabled();
});
@@ -1125,6 +1320,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("s1")
.areAllDynamic()
.areAllNotPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllWithNoIntent();
// Accept the request -> should fail.
@@ -1144,6 +1340,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
assertWith(getCallerShortcuts())
.haveIds("s1")
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
.areAllDisabled();
});
}
@@ -1174,6 +1371,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
publishManifestShortcutsAsCaller(R.xml.shortcut_0);
assertWith(getCallerShortcuts())
.haveIds("ms1")
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllDisabled();
});
@@ -1195,6 +1394,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
.haveIds("ms1")
.areAllManifest()
.areAllNotPinned()
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllWithNoIntent();
// Accept the request -> should fail.
@@ -1214,6 +1415,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
assertWith(getCallerShortcuts())
.haveIds("ms1")
+ .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+ ShortcutActivity.class.getName()))
.areAllDisabled();
});
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 786c2bb06d96..85931e8ac878 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import org.junit.Test;
import org.junit.runner.RunWith;
+import android.content.res.Configuration;
import android.hardware.display.DisplayManagerGlobal;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
@@ -208,6 +209,10 @@ public class DisplayContentTests extends WindowTestsBase {
voiceInteractionWindow.removeImmediately();
}
+ /**
+ * This tests stack movement between displays and proper stack's, task's and app token's display
+ * container references updates.
+ */
@Test
public void testMoveStackBetweenDisplays() throws Exception {
// Create second display.
@@ -238,4 +243,28 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(sDisplayContent, task.getDisplayContent());
assertEquals(sDisplayContent, token.getDisplayContent());
}
+
+ /**
+ * This tests override configuration updates for display content.
+ */
+ @Test
+ public void testDisplayOverrideConfigUpdate() throws Exception {
+ final int displayId = sDisplayContent.getDisplayId();
+ final Configuration currentOverrideConfig = sDisplayContent.getOverrideConfiguration();
+
+ // Create new, slightly changed override configuration and apply it to the display.
+ final Configuration newOverrideConfig = new Configuration(currentOverrideConfig);
+ newOverrideConfig.densityDpi += 120;
+ newOverrideConfig.fontScale += 0.3;
+
+ sWm.setNewDisplayOverrideConfiguration(newOverrideConfig, displayId);
+
+ // Check that override config is applied.
+ assertEquals(newOverrideConfig, sDisplayContent.getOverrideConfiguration());
+
+ // Check that global configuration is updated, as we've updated default display's config.
+ final Configuration globalConfig = sWm.mRoot.getConfiguration();
+ assertEquals(newOverrideConfig.densityDpi, globalConfig.densityDpi);
+ assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 1853a653ed1a..c4fd7221d3f4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -16,29 +16,6 @@
package com.android.server.wm;
-import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.policy.IShortcutService;
-import com.android.server.input.InputManagerService;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.Display;
-import android.view.IWindowManager;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-import android.view.animation.Animation;
-
-import java.io.PrintWriter;
-
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
@@ -62,8 +39,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
-import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
@@ -81,9 +58,30 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-
import static org.mockito.Mockito.mock;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+import android.view.animation.Animation;
+
+import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IShortcutService;
+import com.android.server.input.InputManagerService;
+
+import java.io.PrintWriter;
+
class TestWindowManagerPolicy implements WindowManagerPolicy {
private static final String TAG = "TestWindowManagerPolicy";
@@ -308,14 +306,14 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
@Override
- public View addStartingWindow(IBinder appToken, String packageName, int theme,
+ public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
int logo, int windowFlags, Configuration overrideConfig) {
return null;
}
@Override
- public void removeStartingWindow(IBinder appToken, View window) {
+ public void removeSplashScreen(IBinder appToken, StartingSurface surface) {
}
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index a664f21bc0dc..ea45bd17b216 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -925,7 +925,7 @@ public class ShortcutManagerTestUtils {
}
public ShortcutListAsserter areAllWithActivity(ComponentName activity) {
- forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.getActivity().equals(activity)));
+ forAllShortcuts(s -> assertEquals("id=" + s.getId(), activity, s.getActivity()));
return this;
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index c69b7c2771ac..b20384dfffbc 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -115,6 +115,13 @@ public final class Call {
*/
public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
+ /**
+ * Extra key used to indicate the time (in millis) when the last outgoing emergency call was
+ * made. This is used to identify potential emergency callbacks.
+ */
+ public static final String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS =
+ "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS";
+
public static class Details {
/** Call can currently be put on hold or unheld. */
diff --git a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
index 85584d3ed2cc..4445a2238538 100644
--- a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
+++ b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
@@ -51,4 +51,8 @@ public class AttachInfo_Accessor {
view.dispatchDetachedFromWindow();
}
}
+
+ public static ViewRootImpl getRootView(View view) {
+ return view.mAttachInfo != null ? view.mAttachInfo.mViewRootImpl : null;
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Accessor.java b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Accessor.java
new file mode 100644
index 000000000000..0e15b97243b0
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Accessor.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 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;
+
+/**
+ * Accessor to allow layoutlib to call {@link ViewRootImpl#dispatchApplyInsets} directly.
+ */
+public class ViewRootImpl_Accessor {
+ public static void dispatchApplyInsets(ViewRootImpl viewRoot, View host) {
+ viewRoot.dispatchApplyInsets(host);
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
index 726ff223bd8f..2fe3ed5dc5ee 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
@@ -38,7 +38,10 @@ import android.annotation.NonNull;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.util.TypedValue;
+import android.view.AttachInfo_Accessor;
import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.ViewRootImpl_Accessor;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
@@ -302,6 +305,17 @@ class Layout extends RelativeLayout {
return Bridge.getResourceId(ResourceType.ID, ID_PREFIX + name);
}
+ @Override
+ public void requestFitSystemWindows() {
+ // The framework call would usually bubble up to ViewRootImpl but, in layoutlib, Layout will
+ // act as view root for most purposes. That way, we can also save going through the Handler
+ // to dispatch the new applied insets.
+ ViewRootImpl root = AttachInfo_Accessor.getRootView(this);
+ if (root != null) {
+ ViewRootImpl_Accessor.dispatchApplyInsets(root, this);
+ }
+ }
+
/**
* A helper class to help initialize the Layout.
*/
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java
new file mode 100644
index 000000000000..36e5c2646420
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 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.layoutlib.test.myapplication.widgets;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.WindowInsets;
+import android.widget.TextView;
+
+public class InsetsWidget extends TextView {
+ public static boolean sApplyInsetsCalled = false;
+
+ public InsetsWidget(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ requestApplyInsets();
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ sApplyInsetsCalled = true;
+ return super.onApplyWindowInsets(insets);
+ }
+}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml
new file mode 100644
index 000000000000..ff06d79dd00a
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml
@@ -0,0 +1,12 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:padding="16dp"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <com.android.layoutlib.test.myapplication.widgets.InsetsWidget
+ android:text="Hello world"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/text1"/>
+</LinearLayout>
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index c813a1292318..7a436eb3313e 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -63,6 +63,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
@@ -87,6 +88,7 @@ import java.util.zip.ZipFile;
import com.google.android.collect.Lists;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -434,6 +436,37 @@ public class Main {
renderAndVerify(params, "simple_activity.png");
}
+ @Test
+ public void testOnApplyInsetsCall()
+ throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
+ // We get the widget via reflection to avoid IntelliJ complaining about the class being
+ // located in the wrong package. (From the Bridge tests point of view, it is)
+ Class insetsWidgetClass = Class.forName("com.android.layoutlib.test.myapplication.widgets" +
+ ".InsetsWidget");
+ Field field = insetsWidgetClass.getDeclaredField("sApplyInsetsCalled");
+ assertFalse((Boolean)field.get(null));
+
+ LayoutPullParser parser = createLayoutPullParser("insets.xml");
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.Light.NoActionBar", false,
+ RenderingMode.NORMAL, 22);
+ try {
+ renderAndVerify(params, "scrolled.png");
+ } catch(AssertionError e) {
+ // In this particular test we do not care about the image similarity.
+ // TODO: Create new render method that allows to not compare images.
+ if (!e.getLocalizedMessage().startsWith("Images differ")) {
+ throw e;
+ }
+ }
+
+ assertTrue((Boolean)field.get(null));
+ field.set(null, false);
+ }
+
@AfterClass
public static void tearDown() {
sLayoutLibLog = null;