summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/core/Android.mk6
-rw-r--r--apct-tests/perftests/core/AndroidManifest.xml7
-rw-r--r--apct-tests/perftests/core/apps/overlay/Android.bp188
-rw-r--r--apct-tests/perftests/core/apps/overlay/AndroidManifest.xml21
-rw-r--r--apct-tests/perftests/core/apps/overlay/res/values/values.xml19
-rw-r--r--apct-tests/perftests/core/apps/overlay/res_large/values/values.xml274
-rw-r--r--apct-tests/perftests/core/apps/reources_manager/Android.bp34
-rw-r--r--apct-tests/perftests/core/apps/reources_manager/AndroidManifest.xml20
-rw-r--r--apct-tests/perftests/core/res/color/color_state_list.xml25
-rw-r--r--apct-tests/perftests/core/res/values/overlayable.xml280
-rw-r--r--apct-tests/perftests/core/res/values/strings.xml32
-rw-r--r--apct-tests/perftests/core/res/values/values.xml368
-rw-r--r--apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java234
-rw-r--r--apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java139
-rw-r--r--apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java165
-rw-r--r--apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java71
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java145
-rw-r--r--apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java2
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java112
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java (renamed from services/usage/java/com/android/server/usage/AppIdleHistory.java)0
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java (renamed from services/usage/java/com/android/server/usage/AppStandbyController.java)148
-rw-r--r--api/current.txt4
-rw-r--r--api/system-current.txt13
-rw-r--r--api/test-current.txt8
-rw-r--r--cmds/locksettings/TEST_MAPPING2
-rw-r--r--cmds/statsd/src/main.cpp2
-rw-r--r--cmds/statsd/tests/StatsLogProcessor_test.cpp307
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java30
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl2
-rw-r--r--core/java/android/app/KeyguardManager.java6
-rw-r--r--core/java/android/app/ResourcesManager.java20
-rw-r--r--core/java/android/app/SystemServiceRegistry.java4
-rw-r--r--core/java/android/app/UiAutomation.java32
-rw-r--r--core/java/android/app/usage/AppStandbyInfo.java4
-rw-r--r--core/java/android/content/PermissionChecker.java319
-rw-r--r--core/java/android/content/om/IOverlayManager.aidl6
-rw-r--r--core/java/android/content/om/OverlayManager.java25
-rw-r--r--core/java/android/content/res/ApkAssets.java2
-rw-r--r--core/java/android/content/res/StringBlock.java2
-rw-r--r--core/java/android/hardware/biometrics/Authenticator.java (renamed from core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl)19
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java50
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java82
-rw-r--r--core/java/android/hardware/biometrics/IBiometricService.aidl17
-rw-r--r--core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl2
-rw-r--r--core/java/android/provider/Settings.java10
-rw-r--r--core/java/android/speech/RecognitionService.java24
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java35
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.aidl1
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java48
-rw-r--r--core/java/android/widget/TextView.java6
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java7
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java4
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl14
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl14
-rw-r--r--core/jni/android_util_Process.cpp18
-rw-r--r--core/res/AndroidManifest.xml12
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/strings.xml10
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java4
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--location/java/android/location/LocationManager.java126
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java6
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java3
-rw-r--r--packages/SystemUI/res/drawable/auth_dialog_lock.xml27
-rw-r--r--packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml113
-rw-r--r--packages/SystemUI/res/layout/auth_container_view.xml2
-rw-r--r--packages/SystemUI/res/layout/auth_credential_password_view.xml101
-rw-r--r--packages/SystemUI/res/layout/auth_credential_pattern_view.xml97
-rw-r--r--packages/SystemUI/res/values/dimens.xml5
-rw-r--r--packages/SystemUI/res/values/strings.xml15
-rw-r--r--packages/SystemUI/res/values/styles.xml6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java155
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java223
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java118
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java265
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java118
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/Utils.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java95
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java126
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java227
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java12
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java111
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java18
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java20
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java8
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/TEST_MAPPING1
-rw-r--r--services/core/java/com/android/server/am/UserController.java2
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java449
-rw-r--r--services/core/java/com/android/server/biometrics/Utils.java44
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java43
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java29
-rw-r--r--services/core/java/com/android/server/hdmi/OneTouchPlayAction.java2
-rw-r--r--services/core/java/com/android/server/locksettings/TEST_MAPPING2
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java43
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java19
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerServiceImpl.java5
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java16
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java11
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java286
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java212
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java2
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java10
-rw-r--r--telephony/java/android/telephony/CellBroadcastService.java112
-rw-r--r--telephony/java/android/telephony/ICellBroadcastService.aidl32
-rw-r--r--telephony/java/android/telephony/PhoneStateListener.java6
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java11
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java34
-rwxr-xr-xtools/codegen/src/com/android/codegen/Main.kt8
144 files changed, 6075 insertions, 1433 deletions
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index 3f87a1c8b598..968478c3f338 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -9,8 +9,11 @@ LOCAL_SRC_FILES := \
src/android/os/ISomeService.aidl
LOCAL_STATIC_JAVA_LIBRARIES := \
+ androidx.appcompat_appcompat \
androidx.test.rules \
androidx.annotation_annotation \
+ apct-perftests-overlay-apps \
+ apct-perftests-resources-manager-apps \
apct-perftests-utils \
guava
@@ -25,5 +28,6 @@ LOCAL_JNI_SHARED_LIBRARIES := libperftestscore_jni
LOCAL_ASSET_DIR := $(TOP)/external/google-fonts/dancing-script
LOCAL_COMPATIBILITY_SUITE += device-tests
+LOCAL_CERTIFICATE := platform
-include $(BUILD_PACKAGE)
+include $(BUILD_PACKAGE) \ No newline at end of file
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index a564a4d27fb3..525975d36772 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -5,8 +5,11 @@
<permission android:name="com.android.perftests.core.TestPermission" />
<uses-permission android:name="com.android.perftests.core.TestPermission" />
- <uses-permission
- android:name="android.permission.GET_ACCOUNTS" />
+ <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
+ <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+ <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+ <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/apct-tests/perftests/core/apps/overlay/Android.bp b/apct-tests/perftests/core/apps/overlay/Android.bp
new file mode 100644
index 000000000000..7bee30ee9cb4
--- /dev/null
+++ b/apct-tests/perftests/core/apps/overlay/Android.bp
@@ -0,0 +1,188 @@
+// Copyright (C) 2019 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.
+
+android_test_helper_app {
+ name: "Overlay0",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay0",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay1",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay1",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay2",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay2",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay3",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay3",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay4",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay4",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay5",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay5",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay6",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay6",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay7",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay7",
+ ]
+}
+android_test_helper_app {
+ name: "Overlay8",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay8",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay9",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay9",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay0",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large0",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay1",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large1",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay2",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large2",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay3",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large3",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay4",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large4",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay5",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large5",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay6",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large6",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay7",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large7",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay8",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large8",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay9",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large9",
+ ]
+}
+
+java_library {
+ name: "apct-perftests-overlay-apps",
+ java_resources: [
+ ":Overlay0",
+ ":Overlay1",
+ ":Overlay2",
+ ":Overlay3",
+ ":Overlay4",
+ ":Overlay5",
+ ":Overlay6",
+ ":Overlay7",
+ ":Overlay8",
+ ":Overlay9",
+ ":LargeOverlay0",
+ ":LargeOverlay1",
+ ":LargeOverlay2",
+ ":LargeOverlay3",
+ ":LargeOverlay4",
+ ":LargeOverlay5",
+ ":LargeOverlay6",
+ ":LargeOverlay7",
+ ":LargeOverlay8",
+ ":LargeOverlay9",
+ ],
+} \ No newline at end of file
diff --git a/apct-tests/perftests/core/apps/overlay/AndroidManifest.xml b/apct-tests/perftests/core/apps/overlay/AndroidManifest.xml
new file mode 100644
index 000000000000..52f5a89bc9e4
--- /dev/null
+++ b/apct-tests/perftests/core/apps/overlay/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.perftests.overlay">
+ <application android:hasCode="false" />
+ <uses-sdk android:targetSdkVersion="29" />
+ <overlay android:targetPackage="com.android.perftests.core" android:targetName="TestResources"/>
+</manifest> \ No newline at end of file
diff --git a/apct-tests/perftests/core/apps/overlay/res/values/values.xml b/apct-tests/perftests/core/apps/overlay/res/values/values.xml
new file mode 100644
index 000000000000..a1a8d83eb193
--- /dev/null
+++ b/apct-tests/perftests/core/apps/overlay/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<resources>
+ <string name="short_text">B</string>
+</resources>
diff --git a/apct-tests/perftests/core/apps/overlay/res_large/values/values.xml b/apct-tests/perftests/core/apps/overlay/res_large/values/values.xml
new file mode 100644
index 000000000000..e74144648e32
--- /dev/null
+++ b/apct-tests/perftests/core/apps/overlay/res_large/values/values.xml
@@ -0,0 +1,274 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<resources>
+ <string name="short_text000">B</string>
+ <string name="short_text001">B</string>
+ <string name="short_text002">B</string>
+ <string name="short_text003">B</string>
+ <string name="short_text004">B</string>
+ <string name="short_text005">B</string>
+ <string name="short_text006">B</string>
+ <string name="short_text007">B</string>
+ <string name="short_text008">B</string>
+ <string name="short_text009">B</string>
+ <string name="short_text010">B</string>
+ <string name="short_text011">B</string>
+ <string name="short_text012">B</string>
+ <string name="short_text013">B</string>
+ <string name="short_text014">B</string>
+ <string name="short_text015">B</string>
+ <string name="short_text016">B</string>
+ <string name="short_text017">B</string>
+ <string name="short_text018">B</string>
+ <string name="short_text019">B</string>
+ <string name="short_text020">B</string>
+ <string name="short_text021">B</string>
+ <string name="short_text022">B</string>
+ <string name="short_text023">B</string>
+ <string name="short_text024">B</string>
+ <string name="short_text025">B</string>
+ <string name="short_text026">B</string>
+ <string name="short_text027">B</string>
+ <string name="short_text028">B</string>
+ <string name="short_text029">B</string>
+ <string name="short_text030">B</string>
+ <string name="short_text031">B</string>
+ <string name="short_text032">B</string>
+ <string name="short_text033">B</string>
+ <string name="short_text034">B</string>
+ <string name="short_text035">B</string>
+ <string name="short_text036">B</string>
+ <string name="short_text037">B</string>
+ <string name="short_text038">B</string>
+ <string name="short_text039">B</string>
+ <string name="short_text040">B</string>
+ <string name="short_text041">B</string>
+ <string name="short_text042">B</string>
+ <string name="short_text043">B</string>
+ <string name="short_text044">B</string>
+ <string name="short_text045">B</string>
+ <string name="short_text046">B</string>
+ <string name="short_text047">B</string>
+ <string name="short_text048">B</string>
+ <string name="short_text049">B</string>
+ <string name="short_text050">B</string>
+ <string name="short_text051">B</string>
+ <string name="short_text052">B</string>
+ <string name="short_text053">B</string>
+ <string name="short_text054">B</string>
+ <string name="short_text055">B</string>
+ <string name="short_text056">B</string>
+ <string name="short_text057">B</string>
+ <string name="short_text058">B</string>
+ <string name="short_text059">B</string>
+ <string name="short_text060">B</string>
+ <string name="short_text061">B</string>
+ <string name="short_text062">B</string>
+ <string name="short_text063">B</string>
+ <string name="short_text064">B</string>
+ <string name="short_text065">B</string>
+ <string name="short_text066">B</string>
+ <string name="short_text067">B</string>
+ <string name="short_text068">B</string>
+ <string name="short_text069">B</string>
+ <string name="short_text070">B</string>
+ <string name="short_text071">B</string>
+ <string name="short_text072">B</string>
+ <string name="short_text073">B</string>
+ <string name="short_text074">B</string>
+ <string name="short_text075">B</string>
+ <string name="short_text076">B</string>
+ <string name="short_text077">B</string>
+ <string name="short_text078">B</string>
+ <string name="short_text079">B</string>
+ <string name="short_text080">B</string>
+ <string name="short_text081">B</string>
+ <string name="short_text082">B</string>
+ <string name="short_text083">B</string>
+ <string name="short_text084">B</string>
+ <string name="short_text085">B</string>
+ <string name="short_text086">B</string>
+ <string name="short_text087">B</string>
+ <string name="short_text088">B</string>
+ <string name="short_text089">B</string>
+ <string name="short_text090">B</string>
+ <string name="short_text091">B</string>
+ <string name="short_text092">B</string>
+ <string name="short_text093">B</string>
+ <string name="short_text094">B</string>
+ <string name="short_text095">B</string>
+ <string name="short_text096">B</string>
+ <string name="short_text097">B</string>
+ <string name="short_text098">B</string>
+ <string name="short_text099">B</string>
+ <string name="short_text100">B</string>
+ <string name="short_text101">B</string>
+ <string name="short_text102">B</string>
+ <string name="short_text103">B</string>
+ <string name="short_text104">B</string>
+ <string name="short_text105">B</string>
+ <string name="short_text106">B</string>
+ <string name="short_text107">B</string>
+ <string name="short_text108">B</string>
+ <string name="short_text109">B</string>
+ <string name="short_text110">B</string>
+ <string name="short_text111">B</string>
+ <string name="short_text112">B</string>
+ <string name="short_text113">B</string>
+ <string name="short_text114">B</string>
+ <string name="short_text115">B</string>
+ <string name="short_text116">B</string>
+ <string name="short_text117">B</string>
+ <string name="short_text118">B</string>
+ <string name="short_text119">B</string>
+ <string name="short_text120">B</string>
+ <string name="short_text121">B</string>
+ <string name="short_text122">B</string>
+ <string name="short_text123">B</string>
+ <string name="short_text124">B</string>
+ <string name="short_text125">B</string>
+ <string name="short_text126">B</string>
+ <string name="short_text127">B</string>
+ <string name="short_text128">B</string>
+ <string name="short_text129">B</string>
+ <string name="short_text130">B</string>
+ <string name="short_text131">B</string>
+ <string name="short_text132">B</string>
+ <string name="short_text133">B</string>
+ <string name="short_text134">B</string>
+ <string name="short_text135">B</string>
+ <string name="short_text136">B</string>
+ <string name="short_text137">B</string>
+ <string name="short_text138">B</string>
+ <string name="short_text139">B</string>
+ <string name="short_text140">B</string>
+ <string name="short_text141">B</string>
+ <string name="short_text142">B</string>
+ <string name="short_text143">B</string>
+ <string name="short_text144">B</string>
+ <string name="short_text145">B</string>
+ <string name="short_text146">B</string>
+ <string name="short_text147">B</string>
+ <string name="short_text148">B</string>
+ <string name="short_text149">B</string>
+ <string name="short_text150">B</string>
+ <string name="short_text151">B</string>
+ <string name="short_text152">B</string>
+ <string name="short_text153">B</string>
+ <string name="short_text154">B</string>
+ <string name="short_text155">B</string>
+ <string name="short_text156">B</string>
+ <string name="short_text157">B</string>
+ <string name="short_text158">B</string>
+ <string name="short_text159">B</string>
+ <string name="short_text160">B</string>
+ <string name="short_text161">B</string>
+ <string name="short_text162">B</string>
+ <string name="short_text163">B</string>
+ <string name="short_text164">B</string>
+ <string name="short_text165">B</string>
+ <string name="short_text166">B</string>
+ <string name="short_text167">B</string>
+ <string name="short_text168">B</string>
+ <string name="short_text169">B</string>
+ <string name="short_text170">B</string>
+ <string name="short_text171">B</string>
+ <string name="short_text172">B</string>
+ <string name="short_text173">B</string>
+ <string name="short_text174">B</string>
+ <string name="short_text175">B</string>
+ <string name="short_text176">B</string>
+ <string name="short_text177">B</string>
+ <string name="short_text178">B</string>
+ <string name="short_text179">B</string>
+ <string name="short_text180">B</string>
+ <string name="short_text181">B</string>
+ <string name="short_text182">B</string>
+ <string name="short_text183">B</string>
+ <string name="short_text184">B</string>
+ <string name="short_text185">B</string>
+ <string name="short_text186">B</string>
+ <string name="short_text187">B</string>
+ <string name="short_text188">B</string>
+ <string name="short_text189">B</string>
+ <string name="short_text190">B</string>
+ <string name="short_text191">B</string>
+ <string name="short_text192">B</string>
+ <string name="short_text193">B</string>
+ <string name="short_text194">B</string>
+ <string name="short_text195">B</string>
+ <string name="short_text196">B</string>
+ <string name="short_text197">B</string>
+ <string name="short_text198">B</string>
+ <string name="short_text199">B</string>
+ <string name="short_text200">B</string>
+ <string name="short_text201">B</string>
+ <string name="short_text202">B</string>
+ <string name="short_text203">B</string>
+ <string name="short_text204">B</string>
+ <string name="short_text205">B</string>
+ <string name="short_text206">B</string>
+ <string name="short_text207">B</string>
+ <string name="short_text208">B</string>
+ <string name="short_text209">B</string>
+ <string name="short_text210">B</string>
+ <string name="short_text211">B</string>
+ <string name="short_text212">B</string>
+ <string name="short_text213">B</string>
+ <string name="short_text214">B</string>
+ <string name="short_text215">B</string>
+ <string name="short_text216">B</string>
+ <string name="short_text217">B</string>
+ <string name="short_text218">B</string>
+ <string name="short_text219">B</string>
+ <string name="short_text220">B</string>
+ <string name="short_text221">B</string>
+ <string name="short_text222">B</string>
+ <string name="short_text223">B</string>
+ <string name="short_text224">B</string>
+ <string name="short_text225">B</string>
+ <string name="short_text226">B</string>
+ <string name="short_text227">B</string>
+ <string name="short_text228">B</string>
+ <string name="short_text229">B</string>
+ <string name="short_text230">B</string>
+ <string name="short_text231">B</string>
+ <string name="short_text232">B</string>
+ <string name="short_text233">B</string>
+ <string name="short_text234">B</string>
+ <string name="short_text235">B</string>
+ <string name="short_text236">B</string>
+ <string name="short_text237">B</string>
+ <string name="short_text238">B</string>
+ <string name="short_text239">B</string>
+ <string name="short_text240">B</string>
+ <string name="short_text241">B</string>
+ <string name="short_text242">B</string>
+ <string name="short_text243">B</string>
+ <string name="short_text244">B</string>
+ <string name="short_text245">B</string>
+ <string name="short_text246">B</string>
+ <string name="short_text247">B</string>
+ <string name="short_text248">B</string>
+ <string name="short_text249">B</string>
+ <string name="short_text250">B</string>
+ <string name="short_text251">B</string>
+ <string name="short_text252">B</string>
+ <string name="short_text253">B</string>
+ <string name="short_text254">B</string>
+ <string name="short_text255">B</string>
+</resources>
diff --git a/apct-tests/perftests/core/apps/reources_manager/Android.bp b/apct-tests/perftests/core/apps/reources_manager/Android.bp
new file mode 100644
index 000000000000..451613236140
--- /dev/null
+++ b/apct-tests/perftests/core/apps/reources_manager/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2019 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.
+
+android_test_helper_app {
+ name: "LargeResourcesCompressed",
+ static_libs: [ "androidx.appcompat_appcompat" ],
+}
+
+genrule {
+ name: "LargeResourcesUncompressed",
+ srcs: [ ":LargeResourcesCompressed" ],
+ out: ["LargeResourcesUncompressed.apk"],
+ cmd: "cp $(in) $(out) && unzip -o $(out) resources.arsc"
+ + " && zip $(out) resources.arsc"
+}
+
+java_library {
+ name: "apct-perftests-resources-manager-apps",
+ java_resources: [
+ ":LargeResourcesCompressed",
+ ":LargeResourcesUncompressed",
+ ],
+} \ No newline at end of file
diff --git a/apct-tests/perftests/core/apps/reources_manager/AndroidManifest.xml b/apct-tests/perftests/core/apps/reources_manager/AndroidManifest.xml
new file mode 100644
index 000000000000..adb4e406c608
--- /dev/null
+++ b/apct-tests/perftests/core/apps/reources_manager/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="fake.android">
+ <application android:hasCode="false" />
+ <uses-sdk android:targetSdkVersion="29" />
+</manifest> \ No newline at end of file
diff --git a/apct-tests/perftests/core/res/color/color_state_list.xml b/apct-tests/perftests/core/res/color/color_state_list.xml
new file mode 100644
index 000000000000..142e47ae2738
--- /dev/null
+++ b/apct-tests/perftests/core/res/color/color_state_list.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"
+ android:color="#000000" />
+ <item android:state_pressed="true"
+ android:state_enabled="false"
+ android:color="#212121" />
+ <item android:state_enabled="false"
+ android:color="#414141" />
+ <item android:color="#616161" />
+</selector> \ No newline at end of file
diff --git a/apct-tests/perftests/core/res/values/overlayable.xml b/apct-tests/perftests/core/res/values/overlayable.xml
new file mode 100644
index 000000000000..70cedd7b0b75
--- /dev/null
+++ b/apct-tests/perftests/core/res/values/overlayable.xml
@@ -0,0 +1,280 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <overlayable name="TestResources">
+ <policy type="public">
+ <item type="string" name="short_text" />
+ <item type="string" name="short_text000" />
+ <item type="string" name="short_text001" />
+ <item type="string" name="short_text002" />
+ <item type="string" name="short_text003" />
+ <item type="string" name="short_text004" />
+ <item type="string" name="short_text005" />
+ <item type="string" name="short_text006" />
+ <item type="string" name="short_text007" />
+ <item type="string" name="short_text008" />
+ <item type="string" name="short_text009" />
+ <item type="string" name="short_text010" />
+ <item type="string" name="short_text011" />
+ <item type="string" name="short_text012" />
+ <item type="string" name="short_text013" />
+ <item type="string" name="short_text014" />
+ <item type="string" name="short_text015" />
+ <item type="string" name="short_text016" />
+ <item type="string" name="short_text017" />
+ <item type="string" name="short_text018" />
+ <item type="string" name="short_text019" />
+ <item type="string" name="short_text020" />
+ <item type="string" name="short_text021" />
+ <item type="string" name="short_text022" />
+ <item type="string" name="short_text023" />
+ <item type="string" name="short_text024" />
+ <item type="string" name="short_text025" />
+ <item type="string" name="short_text026" />
+ <item type="string" name="short_text027" />
+ <item type="string" name="short_text028" />
+ <item type="string" name="short_text029" />
+ <item type="string" name="short_text030" />
+ <item type="string" name="short_text031" />
+ <item type="string" name="short_text032" />
+ <item type="string" name="short_text033" />
+ <item type="string" name="short_text034" />
+ <item type="string" name="short_text035" />
+ <item type="string" name="short_text036" />
+ <item type="string" name="short_text037" />
+ <item type="string" name="short_text038" />
+ <item type="string" name="short_text039" />
+ <item type="string" name="short_text040" />
+ <item type="string" name="short_text041" />
+ <item type="string" name="short_text042" />
+ <item type="string" name="short_text043" />
+ <item type="string" name="short_text044" />
+ <item type="string" name="short_text045" />
+ <item type="string" name="short_text046" />
+ <item type="string" name="short_text047" />
+ <item type="string" name="short_text048" />
+ <item type="string" name="short_text049" />
+ <item type="string" name="short_text050" />
+ <item type="string" name="short_text051" />
+ <item type="string" name="short_text052" />
+ <item type="string" name="short_text053" />
+ <item type="string" name="short_text054" />
+ <item type="string" name="short_text055" />
+ <item type="string" name="short_text056" />
+ <item type="string" name="short_text057" />
+ <item type="string" name="short_text058" />
+ <item type="string" name="short_text059" />
+ <item type="string" name="short_text060" />
+ <item type="string" name="short_text061" />
+ <item type="string" name="short_text062" />
+ <item type="string" name="short_text063" />
+ <item type="string" name="short_text064" />
+ <item type="string" name="short_text065" />
+ <item type="string" name="short_text066" />
+ <item type="string" name="short_text067" />
+ <item type="string" name="short_text068" />
+ <item type="string" name="short_text069" />
+ <item type="string" name="short_text070" />
+ <item type="string" name="short_text071" />
+ <item type="string" name="short_text072" />
+ <item type="string" name="short_text073" />
+ <item type="string" name="short_text074" />
+ <item type="string" name="short_text075" />
+ <item type="string" name="short_text076" />
+ <item type="string" name="short_text077" />
+ <item type="string" name="short_text078" />
+ <item type="string" name="short_text079" />
+ <item type="string" name="short_text080" />
+ <item type="string" name="short_text081" />
+ <item type="string" name="short_text082" />
+ <item type="string" name="short_text083" />
+ <item type="string" name="short_text084" />
+ <item type="string" name="short_text085" />
+ <item type="string" name="short_text086" />
+ <item type="string" name="short_text087" />
+ <item type="string" name="short_text088" />
+ <item type="string" name="short_text089" />
+ <item type="string" name="short_text090" />
+ <item type="string" name="short_text091" />
+ <item type="string" name="short_text092" />
+ <item type="string" name="short_text093" />
+ <item type="string" name="short_text094" />
+ <item type="string" name="short_text095" />
+ <item type="string" name="short_text096" />
+ <item type="string" name="short_text097" />
+ <item type="string" name="short_text098" />
+ <item type="string" name="short_text099" />
+ <item type="string" name="short_text100" />
+ <item type="string" name="short_text101" />
+ <item type="string" name="short_text102" />
+ <item type="string" name="short_text103" />
+ <item type="string" name="short_text104" />
+ <item type="string" name="short_text105" />
+ <item type="string" name="short_text106" />
+ <item type="string" name="short_text107" />
+ <item type="string" name="short_text108" />
+ <item type="string" name="short_text109" />
+ <item type="string" name="short_text110" />
+ <item type="string" name="short_text111" />
+ <item type="string" name="short_text112" />
+ <item type="string" name="short_text113" />
+ <item type="string" name="short_text114" />
+ <item type="string" name="short_text115" />
+ <item type="string" name="short_text116" />
+ <item type="string" name="short_text117" />
+ <item type="string" name="short_text118" />
+ <item type="string" name="short_text119" />
+ <item type="string" name="short_text120" />
+ <item type="string" name="short_text121" />
+ <item type="string" name="short_text122" />
+ <item type="string" name="short_text123" />
+ <item type="string" name="short_text124" />
+ <item type="string" name="short_text125" />
+ <item type="string" name="short_text126" />
+ <item type="string" name="short_text127" />
+ <item type="string" name="short_text128" />
+ <item type="string" name="short_text129" />
+ <item type="string" name="short_text130" />
+ <item type="string" name="short_text131" />
+ <item type="string" name="short_text132" />
+ <item type="string" name="short_text133" />
+ <item type="string" name="short_text134" />
+ <item type="string" name="short_text135" />
+ <item type="string" name="short_text136" />
+ <item type="string" name="short_text137" />
+ <item type="string" name="short_text138" />
+ <item type="string" name="short_text139" />
+ <item type="string" name="short_text140" />
+ <item type="string" name="short_text141" />
+ <item type="string" name="short_text142" />
+ <item type="string" name="short_text143" />
+ <item type="string" name="short_text144" />
+ <item type="string" name="short_text145" />
+ <item type="string" name="short_text146" />
+ <item type="string" name="short_text147" />
+ <item type="string" name="short_text148" />
+ <item type="string" name="short_text149" />
+ <item type="string" name="short_text150" />
+ <item type="string" name="short_text151" />
+ <item type="string" name="short_text152" />
+ <item type="string" name="short_text153" />
+ <item type="string" name="short_text154" />
+ <item type="string" name="short_text155" />
+ <item type="string" name="short_text156" />
+ <item type="string" name="short_text157" />
+ <item type="string" name="short_text158" />
+ <item type="string" name="short_text159" />
+ <item type="string" name="short_text160" />
+ <item type="string" name="short_text161" />
+ <item type="string" name="short_text162" />
+ <item type="string" name="short_text163" />
+ <item type="string" name="short_text164" />
+ <item type="string" name="short_text165" />
+ <item type="string" name="short_text166" />
+ <item type="string" name="short_text167" />
+ <item type="string" name="short_text168" />
+ <item type="string" name="short_text169" />
+ <item type="string" name="short_text170" />
+ <item type="string" name="short_text171" />
+ <item type="string" name="short_text172" />
+ <item type="string" name="short_text173" />
+ <item type="string" name="short_text174" />
+ <item type="string" name="short_text175" />
+ <item type="string" name="short_text176" />
+ <item type="string" name="short_text177" />
+ <item type="string" name="short_text178" />
+ <item type="string" name="short_text179" />
+ <item type="string" name="short_text180" />
+ <item type="string" name="short_text181" />
+ <item type="string" name="short_text182" />
+ <item type="string" name="short_text183" />
+ <item type="string" name="short_text184" />
+ <item type="string" name="short_text185" />
+ <item type="string" name="short_text186" />
+ <item type="string" name="short_text187" />
+ <item type="string" name="short_text188" />
+ <item type="string" name="short_text189" />
+ <item type="string" name="short_text190" />
+ <item type="string" name="short_text191" />
+ <item type="string" name="short_text192" />
+ <item type="string" name="short_text193" />
+ <item type="string" name="short_text194" />
+ <item type="string" name="short_text195" />
+ <item type="string" name="short_text196" />
+ <item type="string" name="short_text197" />
+ <item type="string" name="short_text198" />
+ <item type="string" name="short_text199" />
+ <item type="string" name="short_text200" />
+ <item type="string" name="short_text201" />
+ <item type="string" name="short_text202" />
+ <item type="string" name="short_text203" />
+ <item type="string" name="short_text204" />
+ <item type="string" name="short_text205" />
+ <item type="string" name="short_text206" />
+ <item type="string" name="short_text207" />
+ <item type="string" name="short_text208" />
+ <item type="string" name="short_text209" />
+ <item type="string" name="short_text210" />
+ <item type="string" name="short_text211" />
+ <item type="string" name="short_text212" />
+ <item type="string" name="short_text213" />
+ <item type="string" name="short_text214" />
+ <item type="string" name="short_text215" />
+ <item type="string" name="short_text216" />
+ <item type="string" name="short_text217" />
+ <item type="string" name="short_text218" />
+ <item type="string" name="short_text219" />
+ <item type="string" name="short_text220" />
+ <item type="string" name="short_text221" />
+ <item type="string" name="short_text222" />
+ <item type="string" name="short_text223" />
+ <item type="string" name="short_text224" />
+ <item type="string" name="short_text225" />
+ <item type="string" name="short_text226" />
+ <item type="string" name="short_text227" />
+ <item type="string" name="short_text228" />
+ <item type="string" name="short_text229" />
+ <item type="string" name="short_text230" />
+ <item type="string" name="short_text231" />
+ <item type="string" name="short_text232" />
+ <item type="string" name="short_text233" />
+ <item type="string" name="short_text234" />
+ <item type="string" name="short_text235" />
+ <item type="string" name="short_text236" />
+ <item type="string" name="short_text237" />
+ <item type="string" name="short_text238" />
+ <item type="string" name="short_text239" />
+ <item type="string" name="short_text240" />
+ <item type="string" name="short_text241" />
+ <item type="string" name="short_text242" />
+ <item type="string" name="short_text243" />
+ <item type="string" name="short_text244" />
+ <item type="string" name="short_text245" />
+ <item type="string" name="short_text246" />
+ <item type="string" name="short_text247" />
+ <item type="string" name="short_text248" />
+ <item type="string" name="short_text249" />
+ <item type="string" name="short_text250" />
+ <item type="string" name="short_text251" />
+ <item type="string" name="short_text252" />
+ <item type="string" name="short_text253" />
+ <item type="string" name="short_text254" />
+ <item type="string" name="short_text255" />
+ </policy>
+ </overlayable>
+</resources>
diff --git a/apct-tests/perftests/core/res/values/strings.xml b/apct-tests/perftests/core/res/values/strings.xml
deleted file mode 100644
index 7ab325f79dc7..000000000000
--- a/apct-tests/perftests/core/res/values/strings.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_text">text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text typo text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text </string>
- <string name="short_text">text text</string>
-</resources>
diff --git a/apct-tests/perftests/core/res/values/values.xml b/apct-tests/perftests/core/res/values/values.xml
new file mode 100644
index 000000000000..aad42ba04e11
--- /dev/null
+++ b/apct-tests/perftests/core/res/values/values.xml
@@ -0,0 +1,368 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="long_text">text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text typo text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text </string>
+
+ <plurals name="plurals_text">
+ <item quantity="one">1 text</item>
+ <item quantity="other"><xliff:g id="count" example="3">%d</xliff:g> texts</item>
+ </plurals>
+
+ <string-array name="strings">
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ </string-array>
+
+ <integer-array name="ints">
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ </integer-array>
+
+ <color name="white">#ffffff</color>
+
+ <integer name="forty_two">42</integer>
+
+ <string name="short_text">text text</string>
+ <string name="short_text000">B</string>
+ <string name="short_text001">B</string>
+ <string name="short_text002">B</string>
+ <string name="short_text003">B</string>
+ <string name="short_text004">B</string>
+ <string name="short_text005">B</string>
+ <string name="short_text006">B</string>
+ <string name="short_text007">B</string>
+ <string name="short_text008">B</string>
+ <string name="short_text009">B</string>
+ <string name="short_text010">B</string>
+ <string name="short_text011">B</string>
+ <string name="short_text012">B</string>
+ <string name="short_text013">B</string>
+ <string name="short_text014">B</string>
+ <string name="short_text015">B</string>
+ <string name="short_text016">B</string>
+ <string name="short_text017">B</string>
+ <string name="short_text018">B</string>
+ <string name="short_text019">B</string>
+ <string name="short_text020">B</string>
+ <string name="short_text021">B</string>
+ <string name="short_text022">B</string>
+ <string name="short_text023">B</string>
+ <string name="short_text024">B</string>
+ <string name="short_text025">B</string>
+ <string name="short_text026">B</string>
+ <string name="short_text027">B</string>
+ <string name="short_text028">B</string>
+ <string name="short_text029">B</string>
+ <string name="short_text030">B</string>
+ <string name="short_text031">B</string>
+ <string name="short_text032">B</string>
+ <string name="short_text033">B</string>
+ <string name="short_text034">B</string>
+ <string name="short_text035">B</string>
+ <string name="short_text036">B</string>
+ <string name="short_text037">B</string>
+ <string name="short_text038">B</string>
+ <string name="short_text039">B</string>
+ <string name="short_text040">B</string>
+ <string name="short_text041">B</string>
+ <string name="short_text042">B</string>
+ <string name="short_text043">B</string>
+ <string name="short_text044">B</string>
+ <string name="short_text045">B</string>
+ <string name="short_text046">B</string>
+ <string name="short_text047">B</string>
+ <string name="short_text048">B</string>
+ <string name="short_text049">B</string>
+ <string name="short_text050">B</string>
+ <string name="short_text051">B</string>
+ <string name="short_text052">B</string>
+ <string name="short_text053">B</string>
+ <string name="short_text054">B</string>
+ <string name="short_text055">B</string>
+ <string name="short_text056">B</string>
+ <string name="short_text057">B</string>
+ <string name="short_text058">B</string>
+ <string name="short_text059">B</string>
+ <string name="short_text060">B</string>
+ <string name="short_text061">B</string>
+ <string name="short_text062">B</string>
+ <string name="short_text063">B</string>
+ <string name="short_text064">B</string>
+ <string name="short_text065">B</string>
+ <string name="short_text066">B</string>
+ <string name="short_text067">B</string>
+ <string name="short_text068">B</string>
+ <string name="short_text069">B</string>
+ <string name="short_text070">B</string>
+ <string name="short_text071">B</string>
+ <string name="short_text072">B</string>
+ <string name="short_text073">B</string>
+ <string name="short_text074">B</string>
+ <string name="short_text075">B</string>
+ <string name="short_text076">B</string>
+ <string name="short_text077">B</string>
+ <string name="short_text078">B</string>
+ <string name="short_text079">B</string>
+ <string name="short_text080">B</string>
+ <string name="short_text081">B</string>
+ <string name="short_text082">B</string>
+ <string name="short_text083">B</string>
+ <string name="short_text084">B</string>
+ <string name="short_text085">B</string>
+ <string name="short_text086">B</string>
+ <string name="short_text087">B</string>
+ <string name="short_text088">B</string>
+ <string name="short_text089">B</string>
+ <string name="short_text090">B</string>
+ <string name="short_text091">B</string>
+ <string name="short_text092">B</string>
+ <string name="short_text093">B</string>
+ <string name="short_text094">B</string>
+ <string name="short_text095">B</string>
+ <string name="short_text096">B</string>
+ <string name="short_text097">B</string>
+ <string name="short_text098">B</string>
+ <string name="short_text099">B</string>
+ <string name="short_text100">B</string>
+ <string name="short_text101">B</string>
+ <string name="short_text102">B</string>
+ <string name="short_text103">B</string>
+ <string name="short_text104">B</string>
+ <string name="short_text105">B</string>
+ <string name="short_text106">B</string>
+ <string name="short_text107">B</string>
+ <string name="short_text108">B</string>
+ <string name="short_text109">B</string>
+ <string name="short_text110">B</string>
+ <string name="short_text111">B</string>
+ <string name="short_text112">B</string>
+ <string name="short_text113">B</string>
+ <string name="short_text114">B</string>
+ <string name="short_text115">B</string>
+ <string name="short_text116">B</string>
+ <string name="short_text117">B</string>
+ <string name="short_text118">B</string>
+ <string name="short_text119">B</string>
+ <string name="short_text120">B</string>
+ <string name="short_text121">B</string>
+ <string name="short_text122">B</string>
+ <string name="short_text123">B</string>
+ <string name="short_text124">B</string>
+ <string name="short_text125">B</string>
+ <string name="short_text126">B</string>
+ <string name="short_text127">B</string>
+ <string name="short_text128">B</string>
+ <string name="short_text129">B</string>
+ <string name="short_text130">B</string>
+ <string name="short_text131">B</string>
+ <string name="short_text132">B</string>
+ <string name="short_text133">B</string>
+ <string name="short_text134">B</string>
+ <string name="short_text135">B</string>
+ <string name="short_text136">B</string>
+ <string name="short_text137">B</string>
+ <string name="short_text138">B</string>
+ <string name="short_text139">B</string>
+ <string name="short_text140">B</string>
+ <string name="short_text141">B</string>
+ <string name="short_text142">B</string>
+ <string name="short_text143">B</string>
+ <string name="short_text144">B</string>
+ <string name="short_text145">B</string>
+ <string name="short_text146">B</string>
+ <string name="short_text147">B</string>
+ <string name="short_text148">B</string>
+ <string name="short_text149">B</string>
+ <string name="short_text150">B</string>
+ <string name="short_text151">B</string>
+ <string name="short_text152">B</string>
+ <string name="short_text153">B</string>
+ <string name="short_text154">B</string>
+ <string name="short_text155">B</string>
+ <string name="short_text156">B</string>
+ <string name="short_text157">B</string>
+ <string name="short_text158">B</string>
+ <string name="short_text159">B</string>
+ <string name="short_text160">B</string>
+ <string name="short_text161">B</string>
+ <string name="short_text162">B</string>
+ <string name="short_text163">B</string>
+ <string name="short_text164">B</string>
+ <string name="short_text165">B</string>
+ <string name="short_text166">B</string>
+ <string name="short_text167">B</string>
+ <string name="short_text168">B</string>
+ <string name="short_text169">B</string>
+ <string name="short_text170">B</string>
+ <string name="short_text171">B</string>
+ <string name="short_text172">B</string>
+ <string name="short_text173">B</string>
+ <string name="short_text174">B</string>
+ <string name="short_text175">B</string>
+ <string name="short_text176">B</string>
+ <string name="short_text177">B</string>
+ <string name="short_text178">B</string>
+ <string name="short_text179">B</string>
+ <string name="short_text180">B</string>
+ <string name="short_text181">B</string>
+ <string name="short_text182">B</string>
+ <string name="short_text183">B</string>
+ <string name="short_text184">B</string>
+ <string name="short_text185">B</string>
+ <string name="short_text186">B</string>
+ <string name="short_text187">B</string>
+ <string name="short_text188">B</string>
+ <string name="short_text189">B</string>
+ <string name="short_text190">B</string>
+ <string name="short_text191">B</string>
+ <string name="short_text192">B</string>
+ <string name="short_text193">B</string>
+ <string name="short_text194">B</string>
+ <string name="short_text195">B</string>
+ <string name="short_text196">B</string>
+ <string name="short_text197">B</string>
+ <string name="short_text198">B</string>
+ <string name="short_text199">B</string>
+ <string name="short_text200">B</string>
+ <string name="short_text201">B</string>
+ <string name="short_text202">B</string>
+ <string name="short_text203">B</string>
+ <string name="short_text204">B</string>
+ <string name="short_text205">B</string>
+ <string name="short_text206">B</string>
+ <string name="short_text207">B</string>
+ <string name="short_text208">B</string>
+ <string name="short_text209">B</string>
+ <string name="short_text210">B</string>
+ <string name="short_text211">B</string>
+ <string name="short_text212">B</string>
+ <string name="short_text213">B</string>
+ <string name="short_text214">B</string>
+ <string name="short_text215">B</string>
+ <string name="short_text216">B</string>
+ <string name="short_text217">B</string>
+ <string name="short_text218">B</string>
+ <string name="short_text219">B</string>
+ <string name="short_text220">B</string>
+ <string name="short_text221">B</string>
+ <string name="short_text222">B</string>
+ <string name="short_text223">B</string>
+ <string name="short_text224">B</string>
+ <string name="short_text225">B</string>
+ <string name="short_text226">B</string>
+ <string name="short_text227">B</string>
+ <string name="short_text228">B</string>
+ <string name="short_text229">B</string>
+ <string name="short_text230">B</string>
+ <string name="short_text231">B</string>
+ <string name="short_text232">B</string>
+ <string name="short_text233">B</string>
+ <string name="short_text234">B</string>
+ <string name="short_text235">B</string>
+ <string name="short_text236">B</string>
+ <string name="short_text237">B</string>
+ <string name="short_text238">B</string>
+ <string name="short_text239">B</string>
+ <string name="short_text240">B</string>
+ <string name="short_text241">B</string>
+ <string name="short_text242">B</string>
+ <string name="short_text243">B</string>
+ <string name="short_text244">B</string>
+ <string name="short_text245">B</string>
+ <string name="short_text246">B</string>
+ <string name="short_text247">B</string>
+ <string name="short_text248">B</string>
+ <string name="short_text249">B</string>
+ <string name="short_text250">B</string>
+ <string name="short_text251">B</string>
+ <string name="short_text252">B</string>
+ <string name="short_text253">B</string>
+ <string name="short_text254">B</string>
+ <string name="short_text255">B</string>
+</resources>
diff --git a/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
new file mode 100644
index 000000000000..fcb13a8d51f1
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.om.OverlayManager;
+import android.os.UserHandle;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.TestPackageInstaller;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import com.android.perftests.core.R;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Benchmarks for {@link android.content.om.OverlayManager}.
+ */
+@LargeTest
+public class OverlayManagerPerfTest {
+ private static final int OVERLAY_PKG_COUNT = 10;
+ private static Context sContext;
+ private static OverlayManager sOverlayManager;
+ private static Executor sExecutor;
+ private static ArrayList<TestPackageInstaller.InstalledPackage> sSmallOverlays =
+ new ArrayList<>();
+ private static ArrayList<TestPackageInstaller.InstalledPackage> sLargeOverlays =
+ new ArrayList<>();
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @BeforeClass
+ public static void classSetUp() throws Exception {
+ sContext = InstrumentationRegistry.getTargetContext();
+ sOverlayManager = new OverlayManager(sContext);
+ sExecutor = (command) -> new Thread(command).start();
+
+ // Install all of the test overlays.
+ TestPackageInstaller installer = new TestPackageInstaller(sContext);
+ for (int i = 0; i < OVERLAY_PKG_COUNT; i++) {
+ sSmallOverlays.add(installer.installPackage("Overlay" + i +".apk"));
+ sLargeOverlays.add(installer.installPackage("LargeOverlay" + i +".apk"));
+ }
+ }
+
+ @AfterClass
+ public static void classTearDown() throws Exception {
+ for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
+ overlay.uninstall();
+ }
+
+ for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
+ overlay.uninstall();
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // Disable all test overlays after each test.
+ for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
+ assertSetEnabled(sContext, overlay.getPackageName(), false);
+ }
+
+ for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
+ assertSetEnabled(sContext, overlay.getPackageName(), false);
+ }
+ }
+
+ /**
+ * Enables the overlay and waits for the APK path change sto be propagated to the context
+ * AssetManager.
+ */
+ private void assertSetEnabled(Context context, String overlayPackage, boolean eanabled)
+ throws Exception {
+ sOverlayManager.setEnabled(overlayPackage, true, UserHandle.SYSTEM);
+
+ // Wait for the overlay changes to propagate
+ FutureTask<Boolean> task = new FutureTask<>(() -> {
+ while (true) {
+ for (String path : context.getAssets().getApkPaths()) {
+ if (eanabled == path.contains(overlayPackage)) {
+ return true;
+ }
+ }
+ }
+ });
+
+ sExecutor.execute(task);
+ assertTrue("Failed to load overlay " + overlayPackage,
+ task.get(20, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void setEnabledWarmCache() throws Exception {
+ String packageName = sSmallOverlays.get(0).getPackageName();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ assertSetEnabled(sContext, packageName, true);
+
+ // Disable the overlay for the next iteration of the test
+ state.pauseTiming();
+ assertSetEnabled(sContext, packageName, false);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void setEnabledColdCacheSmallOverlay() throws Exception {
+ String packageName = sSmallOverlays.get(0).getPackageName();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ assertSetEnabled(sContext, packageName, true);
+
+ // Disable the overlay and remove the idmap for the next iteration of the test
+ state.pauseTiming();
+ assertSetEnabled(sContext, packageName, false);
+ sOverlayManager.invalidateCachesForOverlay(packageName, UserHandle.SYSTEM);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void setEnabledColdCacheLargeOverlay() throws Exception {
+ String packageName = sLargeOverlays.get(0).getPackageName();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ assertSetEnabled(sContext, packageName, true);
+
+ // Disable the overlay and remove the idmap for the next iteration of the test
+ state.pauseTiming();
+ assertSetEnabled(sContext, packageName, false);
+ sOverlayManager.invalidateCachesForOverlay(packageName, UserHandle.SYSTEM);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void setEnabledDisable() throws Exception {
+ String packageName = sSmallOverlays.get(0).getPackageName();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ assertSetEnabled(sContext, packageName, true);
+ state.resumeTiming();
+
+ assertSetEnabled(sContext, packageName, false);
+ }
+ }
+
+ @Test
+ public void getStringOneSmallOverlay() throws Exception {
+ String packageName = sSmallOverlays.get(0).getPackageName();
+ assertSetEnabled(sContext, packageName, true);
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ sContext.getString(R.string.short_text);
+ }
+
+ assertSetEnabled(sContext, packageName, false);
+ }
+
+ @Test
+ public void getStringOneLargeOverlay() throws Exception {
+ String packageName = sLargeOverlays.get(0).getPackageName();
+ assertSetEnabled(sContext, packageName, true);
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int resId = R.string.short_text000; resId < R.string.short_text255; resId++) {
+ sContext.getString(resId);
+ }
+ }
+
+ assertSetEnabled(sContext, packageName, false);
+ }
+
+ @Test
+ public void getStringTenOverlays() throws Exception {
+ // Enable all test overlays
+ for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
+ assertSetEnabled(sContext, overlay.getPackageName(), true);
+ }
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ sContext.getString(R.string.short_text);
+ }
+ }
+
+ @Test
+ public void getStringLargeTenOverlays() throws Exception {
+ // Enable all test overlays
+ for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
+ assertSetEnabled(sContext, overlay.getPackageName(), true);
+ }
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int resId = R.string.short_text000; resId < R.string.short_text255; resId++) {
+ sContext.getString(resId);
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
new file mode 100644
index 000000000000..2955d2ca7d0e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.view.Display;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Benchmarks for {@link android.app.ResourcesManager}.
+ */
+@LargeTest
+public class ResourcesManagerPerfTest {
+ private static Context sContext;
+ private static File sResourcesCompressed;
+ private static File sResourcesUncompressed;
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ sContext = InstrumentationRegistry.getTargetContext();
+ sResourcesCompressed = copyApkToTemp("LargeResourcesCompressed.apk",
+ "LargeResourcesCompressed.apk");
+ sResourcesUncompressed = copyApkToTemp("LargeResourcesUncompressed.apk",
+ "LargeResourcesUncompressed.apk");
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ Assert.assertTrue(sResourcesCompressed.delete());
+ Assert.assertTrue(sResourcesUncompressed.delete());
+ }
+
+ private static File copyApkToTemp(String inputFileName, String fileName) throws Exception {
+ File file = File.createTempFile(fileName, null, sContext.getCacheDir());
+ try (OutputStream tempOutputStream = new FileOutputStream(file);
+ InputStream is = sContext.getResources().getAssets().openNonAsset(inputFileName)) {
+ byte[] buffer = new byte[4096];
+ int n;
+ while ((n = is.read(buffer)) >= 0) {
+ tempOutputStream.write(buffer, 0, n);
+ }
+ tempOutputStream.flush();
+ }
+ return file;
+ }
+
+ private void getResourcesForPath(String path) {
+ ResourcesManager.getInstance().getResources(null, path, null, null, null,
+ Display.DEFAULT_DISPLAY, null, sContext.getResources().getCompatibilityInfo(),
+ null);
+ }
+
+ @Test
+ public void getResourcesCached() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ getResourcesForPath(sResourcesCompressed.getPath());
+ while (state.keepRunning()) {
+ getResourcesForPath(sResourcesCompressed.getPath());
+ }
+ }
+
+ @Test
+ public void getResourcesCompressedUncached() {
+ ResourcesManager resourcesManager = ResourcesManager.getInstance();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ resourcesManager.invalidatePath(sResourcesCompressed.getPath());
+ state.resumeTiming();
+
+ getResourcesForPath(sResourcesCompressed.getPath());
+ }
+ }
+
+ @Test
+ public void getResourcesUncompressedUncached() {
+ ResourcesManager resourcesManager = ResourcesManager.getInstance();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ resourcesManager.invalidatePath(sResourcesUncompressed.getPath());
+ state.resumeTiming();
+
+ getResourcesForPath(sResourcesUncompressed.getPath());
+ }
+ }
+
+ @Test
+ public void applyConfigurationToResourcesLocked() {
+ ResourcesManager resourcesManager = ResourcesManager.getInstance();
+ Configuration c = new Configuration(resourcesManager.getConfiguration());
+ c.uiMode = Configuration.UI_MODE_TYPE_WATCH;
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ resourcesManager.applyConfigurationToResources(c, null);
+
+ // Alternate configurations to ensure the set configuration is different each iteration
+ if (c.uiMode == Configuration.UI_MODE_TYPE_WATCH) {
+ c.uiMode = Configuration.UI_MODE_TYPE_TELEVISION;
+ } else {
+ c.uiMode = Configuration.UI_MODE_TYPE_WATCH;
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
index c3e43ee07453..72162448a2e0 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
@@ -18,15 +18,18 @@ package android.app;
import static org.junit.Assert.fail;
-import android.content.res.AssetManager;
+import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
+import android.util.TypedValue;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
-import org.junit.After;
+import com.android.perftests.core.R;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -43,36 +46,123 @@ public class ResourcesPerfTest {
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
- private AssetManager mAsset;
private Resources mRes;
- private int mTextId;
- private int mColorId;
- private int mIntegerId;
- private int mLayoutId;
-
@Before
public void setUp() {
- mAsset = new AssetManager();
- mAsset.addAssetPath("/system/framework/framework-res.apk");
- mRes = new Resources(mAsset, null, null);
+ Context context = InstrumentationRegistry.getTargetContext();
+ mRes = context.getResources();
+ }
+
+ @Test
+ public void getValue() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ TypedValue value = new TypedValue();
+ while (state.keepRunning()) {
+ mRes.getValue(R.integer.forty_two, value, false /* resolve_refs */);
+ }
+ }
- mTextId = mRes.getIdentifier("cancel", "string", "android");
- mColorId = mRes.getIdentifier("transparent", "color", "android");
- mIntegerId = mRes.getIdentifier("config_shortAnimTime", "integer", "android");
- mLayoutId = mRes.getIdentifier("two_line_list_item", "layout", "android");
+ @Test
+ public void getFrameworkValue() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ TypedValue value = new TypedValue();
+ while (state.keepRunning()) {
+ mRes.getValue(com.android.internal.R.integer.autofill_max_visible_datasets, value,
+ false /* resolve_refs */);
+ }
+ }
+
+ @Test
+ public void getValueString() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ TypedValue value = new TypedValue();
+ while (state.keepRunning()) {
+ mRes.getValue(R.string.long_text, value, false /* resolve_refs */);
+ }
+ }
+
+ @Test
+ public void getFrameworkStringValue() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ TypedValue value = new TypedValue();
+ while (state.keepRunning()) {
+ mRes.getValue(com.android.internal.R.string.cancel, value, false /* resolve_refs */);
+ }
}
- @After
- public void tearDown() {
- mAsset.close();
+ @Test
+ public void getValueManyConfigurations() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ TypedValue value = new TypedValue();
+ while (state.keepRunning()) {
+ mRes.getValue(com.android.internal.R.string.mmcc_illegal_me, value,
+ false /* resolve_refs */);
+ }
}
@Test
public void getText() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- mRes.getText(mTextId);
+ mRes.getText(R.string.long_text);
+ }
+ }
+
+
+ @Test
+ public void getFont() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getFont(R.font.samplefont);
+ }
+ }
+
+ @Test
+ public void getString() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getString(R.string.long_text);
+ }
+ }
+
+ @Test
+ public void getQuantityString() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getQuantityString(R.plurals.plurals_text, 5);
+ }
+ }
+
+ @Test
+ public void getQuantityText() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getQuantityText(R.plurals.plurals_text, 5);
+ }
+ }
+
+ @Test
+ public void getTextArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getTextArray(R.array.strings);
+ }
+ }
+
+ @Test
+ public void getStringArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getStringArray(R.array.strings);
+ }
+ }
+
+ @Test
+ public void getIntegerArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getIntArray(R.array.ints);
}
}
@@ -80,15 +170,23 @@ public class ResourcesPerfTest {
public void getColor() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- mRes.getColor(mColorId, null);
+ mRes.getColor(R.color.white, null);
}
}
@Test
- public void getInteger() {
+ public void getColorStateList() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- mRes.getInteger(mIntegerId);
+ mRes.getColorStateList(R.color.color_state_list, null);
+ }
+ }
+
+ @Test
+ public void getVectorDrawable() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getDrawable(R.drawable.vector_drawable01, null);
}
}
@@ -96,13 +194,32 @@ public class ResourcesPerfTest {
public void getLayoutAndTravese() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- try (XmlResourceParser parser = mRes.getLayout(mLayoutId)) {
+ try (XmlResourceParser parser = mRes.getLayout(R.layout.test_relative_layout)) {
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ // Walk the entire tree
+ }
+ } catch (IOException | XmlPullParserException exception) {
+ fail("Parsing of the layout failed. Something is really broken");
+ }
+ }
+ }
+
+ @Test
+ public void getLayoutAndTraverseInvalidateCaches() {
+ mRes.flushLayoutCache();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ try (XmlResourceParser parser = mRes.getLayout(R.layout.test_relative_layout)) {
while (parser.next() != XmlPullParser.END_DOCUMENT) {
// Walk the entire tree
}
} catch (IOException | XmlPullParserException exception) {
fail("Parsing of the layout failed. Something is really broken");
}
+
+ state.pauseTiming();
+ mRes.flushLayoutCache();
+ state.resumeTiming();
}
}
-}
+} \ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
index 1b07572fd3f8..6123e69b584e 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
@@ -16,13 +16,19 @@
package android.app;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.content.res.Resources;
+import android.os.UserHandle;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
+import android.view.Display;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -35,13 +41,69 @@ public class ResourcesThemePerfTest {
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ private Context mContext;
+ private int mThemeResId;
private Resources.Theme mTheme;
@Before
- public void setUp() {
- Context context = InstrumentationRegistry.getTargetContext();
- mTheme = context.getTheme();
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mThemeResId = com.android.perftests.core.R.style.Base_V7_Theme_AppCompat;
+ mTheme = mContext.getResources().newTheme();
+ mTheme.applyStyle(mThemeResId, true /* force */);
+ }
+
+ @Test
+ public void applyStyle() {
+ Resources.Theme destTheme = mContext.getResources().newTheme();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ destTheme.applyStyle(mThemeResId, true /* force */);
+ }
+ }
+ @Test
+ public void rebase() {
+ Resources.Theme destTheme = mContext.getResources().newTheme();
+ destTheme.applyStyle(mThemeResId, true /* force */);
+ destTheme.applyStyle(android.R.style.Theme_Material, true /* force */);
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ destTheme.rebase();
+ }
+ }
+
+ @Test
+ public void setToSameAssetManager() {
+ Resources.Theme destTheme = mContext.getResources().newTheme();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ destTheme.setTo(mTheme);
+ }
+ }
+
+ @Test
+ public void setToDifferentAssetManager() throws Exception {
+ // Create a new Resources object with the same asset paths but a different AssetManager
+ PackageManager packageManager = mContext.getApplicationContext().getPackageManager();
+ ApplicationInfo ai = packageManager.getApplicationInfo(mContext.getPackageName(),
+ UserHandle.myUserId());
+
+ ResourcesManager resourcesManager = ResourcesManager.getInstance();
+ Configuration c = resourcesManager.getConfiguration();
+ c.orientation = (c.orientation == Configuration.ORIENTATION_PORTRAIT)
+ ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+
+ Resources destResources = resourcesManager.getResources(null, ai.sourceDir,
+ ai.splitSourceDirs, ai.resourceDirs, ai.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
+ c, mContext.getResources().getCompatibilityInfo(), null);
+ Assert.assertNotEquals(destResources.getAssets(), mContext.getAssets());
+
+ Resources.Theme destTheme = destResources.newTheme();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ destTheme.setTo(mTheme);
+ }
}
@Test
@@ -51,5 +113,4 @@ public class ResourcesThemePerfTest {
mTheme.obtainStyledAttributes(android.R.style.Theme_Material, android.R.styleable.View);
}
}
-
-}
+} \ No newline at end of file
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java b/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
new file mode 100644
index 000000000000..a433d801acaf
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 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.perftests.utils;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.content.res.Resources;
+import android.util.Log;
+
+import org.junit.Assert;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Installs packages included within the assets directory.
+ */
+public class TestPackageInstaller {
+ private static final String LOG_TAG = "TestPackageInstaller";
+ private static final String BROADCAST_ACTION =
+ "com.android.perftests.core.ACTION_INSTALL_COMMIT";
+
+ private final Context mContext;
+ public TestPackageInstaller(Context context) {
+ mContext = context;
+ }
+
+
+
+ /**
+ * Installs an APK located at the specified path in the assets directory.
+ **/
+ public InstalledPackage installPackage(String resourceName) throws IOException,
+ InterruptedException {
+ Log.d(LOG_TAG, "Installing resource APK '" + resourceName + "'");
+ LocalBroadcastReceiver intentSender = new LocalBroadcastReceiver(mContext);
+
+ // Initialize the package install session.
+ PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ params.setInstallAsInstantApp(false);
+ int sessionId = packageInstaller.createSession(params);
+ PackageInstaller.Session session = packageInstaller.openSession(sessionId);
+
+ // Copy the apk to the install session.
+ try (OutputStream os = session.openWrite("TestPackageInstaller", 0, -1);
+ InputStream is = mContext.getResources().getAssets().openNonAsset(resourceName)) {
+ if (is == null) {
+ throw new IOException("Resource " + resourceName + " not found");
+ }
+ byte[] buffer = new byte[4096];
+ int n;
+ while ((n = is.read(buffer)) >= 0) {
+ os.write(buffer, 0, n);
+ }
+ }
+
+ session.commit(intentSender.getIntentSender(sessionId));
+ session.close();
+
+ // Retrieve the results of the installation.
+ Intent intent = intentSender.getIntentSenderResult();
+ int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ Assert.assertEquals(PackageInstaller.STATUS_SUCCESS, status);
+ String packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME);
+ return new InstalledPackage(sessionId, packageName);
+ }
+
+ public class InstalledPackage {
+ private int mSessionId;
+ private String mPackageName;
+
+ private InstalledPackage(int sessionId, String packageName) {
+ mSessionId = sessionId;
+ mPackageName = packageName;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public void uninstall() throws Exception {
+ Log.d(LOG_TAG, "Uninstalling package '" + mPackageName + "'");
+ LocalBroadcastReceiver intentSender = new LocalBroadcastReceiver(mContext);
+ PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
+ packageInstaller.uninstall(mPackageName, intentSender.getIntentSender(mSessionId));
+ int status = intentSender.getIntentSenderResult()
+ .getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
+ Assert.assertEquals(PackageInstaller.STATUS_SUCCESS, status);
+ }
+ }
+
+ private class LocalBroadcastReceiver extends BroadcastReceiver {
+ private final BlockingQueue<Intent> mIntentSenderResults = new LinkedBlockingQueue<>();
+ private final Context mContext;
+
+ private LocalBroadcastReceiver(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mIntentSenderResults.add(intent);
+ }
+
+ IntentSender getIntentSender(int sessionId) {
+ String action = BROADCAST_ACTION + "." + sessionId;
+ IntentFilter filter = new IntentFilter(action);
+ mContext.registerReceiver(this, filter);
+
+ Intent intent = new Intent(action);
+ PendingIntent pending = PendingIntent.getBroadcast(mContext, sessionId, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ return pending.getIntentSender();
+ }
+
+ Intent getIntentSenderResult() throws InterruptedException {
+ return mIntentSenderResults.take();
+ }
+ }
+}
diff --git a/apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java b/apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java
index a807eb1d3311..5b1405628b49 100644
--- a/apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java
+++ b/apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java
@@ -36,7 +36,7 @@ public class DeviceIdleFrameworkInitializer {
SystemServiceRegistry.registerCachedService(
Context.DEVICE_IDLE_CONTROLLER, DeviceIdleManager.class,
(context, b) -> new DeviceIdleManager(
- context.getOuterContext(), IDeviceIdleController.Stub.asInterface(b)));
+ context, IDeviceIdleController.Stub.asInterface(b)));
PowerManager.setIsIgnoringBatteryOptimizationsCallback((packageName) -> {
// No need for synchronization on sIDeviceIdleController; worst case
// we just initialize it twice.
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
new file mode 100644
index 000000000000..3cfb08082ff7
--- /dev/null
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -0,0 +1,112 @@
+package com.android.server.usage;
+
+import android.app.usage.AppStandbyInfo;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
+import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
+import android.content.Context;
+import android.os.Looper;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.Set;
+
+public interface AppStandbyInternal {
+ /**
+ * TODO AppStandbyController should probably be a binder service, and then we shouldn't need
+ * this method.
+ */
+ static AppStandbyInternal newAppStandbyController(ClassLoader loader, Context context,
+ Looper looper) {
+ try {
+ final Class<?> clazz = Class.forName("com.android.server.usage.AppStandbyController",
+ true, loader);
+ final Constructor<?> ctor = clazz.getConstructor(Context.class, Looper.class);
+ return (AppStandbyInternal) ctor.newInstance(context, looper);
+ } catch (NoSuchMethodException | InstantiationException
+ | IllegalAccessException | InvocationTargetException | ClassNotFoundException e) {
+ throw new RuntimeException("Unable to instantiate AppStandbyController!", e);
+ }
+ }
+
+ void onBootPhase(int phase);
+
+ boolean isParoledOrCharging();
+
+ void postCheckIdleStates(int userId);
+
+ /**
+ * We send a different message to check idle states once, otherwise we would end up
+ * scheduling a series of repeating checkIdleStates each time we fired off one.
+ */
+ void postOneTimeCheckIdleStates();
+
+ void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId);
+
+ void setLastJobRunTime(String packageName, int userId, long elapsedRealtime);
+
+ long getTimeSinceLastJobRun(String packageName, int userId);
+
+ void onUserRemoved(int userId);
+
+ void addListener(AppIdleStateChangeListener listener);
+
+ void removeListener(AppIdleStateChangeListener listener);
+
+ int getAppId(String packageName);
+
+ boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
+ boolean shouldObfuscateInstantApps);
+
+ /**
+ * Checks if an app has been idle for a while and filters out apps that are excluded.
+ * It returns false if the current system state allows all apps to be considered active.
+ * This happens if the device is plugged in or temporarily allowed to make exceptions.
+ * Called by interface impls.
+ */
+ boolean isAppIdleFiltered(String packageName, int appId, int userId,
+ long elapsedRealtime);
+
+ int[] getIdleUidsForUser(int userId);
+
+ void setAppIdleAsync(String packageName, boolean idle, int userId);
+
+ @StandbyBuckets
+ int getAppStandbyBucket(String packageName, int userId,
+ long elapsedRealtime, boolean shouldObfuscateInstantApps);
+
+ List<AppStandbyInfo> getAppStandbyBuckets(int userId);
+
+ void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
+ int reason, long elapsedRealtime, boolean resetTimeout);
+
+ void addActiveDeviceAdmin(String adminPkg, int userId);
+
+ void setActiveAdminApps(Set<String> adminPkgs, int userId);
+
+ void onAdminDataAvailable();
+
+ void clearCarrierPrivilegedApps();
+
+ void flushToDisk(int userId);
+
+ void flushDurationsToDisk();
+
+ void initializeDefaultsForSystemApps(int userId);
+
+ void postReportContentProviderUsage(String name, String packageName, int userId);
+
+ void postReportSyncScheduled(String packageName, int userId, boolean exempted);
+
+ void postReportExemptedSyncStart(String packageName, int userId);
+
+ void dumpUser(IndentingPrintWriter idpw, int userId, String pkg);
+
+ void dumpState(String[] args, PrintWriter pw);
+
+ boolean isAppIdleEnabled();
+}
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 1e4861a89694..1e4861a89694 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 75e8fb5a34fe..df5d6aeb8ee5 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -120,9 +120,9 @@ import java.util.concurrent.CountDownLatch;
* Manages the standby state of an app, listening to various events.
*
* Unit test:
- atest ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+ atest com.android.server.usage.AppStandbyControllerTests
*/
-public class AppStandbyController {
+public class AppStandbyController implements AppStandbyInternal {
private static final String TAG = "AppStandbyController";
static final boolean DEBUG = false;
@@ -247,7 +247,7 @@ public class AppStandbyController {
/** The length of time phone must be charging before considered stable enough to run jobs */
long mStableChargingThresholdMillis;
- volatile boolean mAppIdleEnabled;
+ private volatile boolean mAppIdleEnabled;
boolean mAppIdleTempParoled;
boolean mCharging;
boolean mChargingStable;
@@ -320,7 +320,7 @@ public class AppStandbyController {
}
}
- AppStandbyController(Context context, Looper looper) {
+ public AppStandbyController(Context context, Looper looper) {
this(new Injector(context, looper));
}
@@ -351,6 +351,7 @@ public class AppStandbyController {
null, mHandler);
}
+ @VisibleForTesting
void setAppIdleEnabled(boolean enabled) {
synchronized (mAppIdleLock) {
if (mAppIdleEnabled != enabled) {
@@ -363,6 +364,12 @@ public class AppStandbyController {
}
}
+ @Override
+ public boolean isAppIdleEnabled() {
+ return mAppIdleEnabled;
+ }
+
+ @Override
public void onBootPhase(int phase) {
mInjector.onBootPhase(phase);
if (phase == PHASE_SYSTEM_SERVICES_READY) {
@@ -400,7 +407,7 @@ public class AppStandbyController {
}
}
- void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
+ private void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
if (!mAppIdleEnabled) return;
// Get sync adapters for the authority
@@ -432,7 +439,7 @@ public class AppStandbyController {
}
}
- void reportExemptedSyncScheduled(String packageName, int userId) {
+ private void reportExemptedSyncScheduled(String packageName, int userId) {
if (!mAppIdleEnabled) return;
final int bucketToPromote;
@@ -463,7 +470,7 @@ public class AppStandbyController {
}
}
- void reportUnexemptedSyncScheduled(String packageName, int userId) {
+ private void reportUnexemptedSyncScheduled(String packageName, int userId) {
if (!mAppIdleEnabled) return;
final long elapsedRealtime = mInjector.elapsedRealtime();
@@ -482,7 +489,7 @@ public class AppStandbyController {
}
}
- void reportExemptedSyncStart(String packageName, int userId) {
+ private void reportExemptedSyncStart(String packageName, int userId) {
if (!mAppIdleEnabled) return;
final long elapsedRealtime = mInjector.elapsedRealtime();
@@ -497,6 +504,7 @@ public class AppStandbyController {
}
}
+ @VisibleForTesting
void setChargingState(boolean charging) {
synchronized (mAppIdleLock) {
if (mCharging != charging) {
@@ -517,7 +525,7 @@ public class AppStandbyController {
}
}
- void updateChargingStableState() {
+ private void updateChargingStableState() {
synchronized (mAppIdleLock) {
if (mChargingStable != mCharging) {
if (DEBUG) Slog.d(TAG, "Setting mChargingStable to " + mCharging);
@@ -527,8 +535,7 @@ public class AppStandbyController {
}
}
- /** Paroled here means temporary pardon from being inactive */
- void setAppIdleParoled(boolean paroled) {
+ private void setAppIdleParoled(boolean paroled) {
synchronized (mAppIdleLock) {
final long now = mInjector.currentTimeMillis();
if (mAppIdleTempParoled != paroled) {
@@ -545,7 +552,8 @@ public class AppStandbyController {
}
}
- boolean isParoledOrCharging() {
+ @Override
+ public boolean isParoledOrCharging() {
if (!mAppIdleEnabled) return true;
synchronized (mAppIdleLock) {
// Only consider stable charging when determining charge state.
@@ -583,15 +591,13 @@ public class AppStandbyController {
mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
}
- void postCheckIdleStates(int userId) {
+ @Override
+ public void postCheckIdleStates(int userId) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
}
- /**
- * We send a different message to check idle states once, otherwise we would end up
- * scheduling a series of repeating checkIdleStates each time we fired off one.
- */
- void postOneTimeCheckIdleStates() {
+ @Override
+ public void postOneTimeCheckIdleStates() {
if (mInjector.getBootPhase() < PHASE_SYSTEM_SERVICES_READY) {
// Not booted yet; wait for it!
mPendingOneTimeCheckIdleStates = true;
@@ -601,10 +607,7 @@ public class AppStandbyController {
}
}
- /**
- * Check all running users' or specified user's apps to see if they enter an idle state.
- * @return Returns whether checking should continue periodically.
- */
+ @VisibleForTesting
boolean checkIdleStates(int checkUserId) {
if (!mAppIdleEnabled) {
return false;
@@ -776,19 +779,15 @@ public class AppStandbyController {
* @return the bucket for the app, based on time since last used
*/
@GuardedBy("mAppIdleLock")
- @StandbyBuckets int getBucketForLocked(String packageName, int userId,
+ @StandbyBuckets
+ private int getBucketForLocked(String packageName, int userId,
long elapsedRealtime) {
int bucketIndex = mAppIdleHistory.getThresholdIndex(packageName, userId,
elapsedRealtime, mAppStandbyScreenThresholds, mAppStandbyElapsedThresholds);
return THRESHOLD_BUCKETS[bucketIndex];
}
- /**
- * Check if it's been a while since last parole and let idle apps do some work.
- * If network is not available, delay parole until it is available up until the end of the
- * parole window. Force the parole to be set if end of the parole window is reached.
- */
- void checkParoleTimeout() {
+ private void checkParoleTimeout() {
boolean setParoled = false;
boolean waitForNetwork = false;
NetworkInfo activeNetwork = mConnectivityManager.getActiveNetworkInfo();
@@ -845,7 +844,7 @@ public class AppStandbyController {
}
}
- void onDeviceIdleModeChanged() {
+ private void onDeviceIdleModeChanged() {
final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
boolean paroled = false;
@@ -869,7 +868,8 @@ public class AppStandbyController {
setAppIdleParoled(paroled);
}
- void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
+ @Override
+ public void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
if (!mAppIdleEnabled) return;
synchronized (mAppIdleLock) {
// TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
@@ -950,14 +950,7 @@ public class AppStandbyController {
}
}
- /**
- * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
- * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
- * the threshold for idle.
- *
- * This method is always called from the handler thread, so not much synchronization is
- * required.
- */
+ @VisibleForTesting
void forceIdleState(String packageName, int userId, boolean idle) {
if (!mAppIdleEnabled) return;
@@ -983,12 +976,14 @@ public class AppStandbyController {
}
}
+ @Override
public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) {
synchronized (mAppIdleLock) {
mAppIdleHistory.setLastJobRunTime(packageName, userId, elapsedRealtime);
}
}
+ @Override
public long getTimeSinceLastJobRun(String packageName, int userId) {
final long elapsedRealtime = mInjector.elapsedRealtime();
synchronized (mAppIdleLock) {
@@ -996,6 +991,7 @@ public class AppStandbyController {
}
}
+ @Override
public void onUserRemoved(int userId) {
synchronized (mAppIdleLock) {
mAppIdleHistory.onUserRemoved(userId);
@@ -1011,7 +1007,8 @@ public class AppStandbyController {
}
}
- void addListener(AppIdleStateChangeListener listener) {
+ @Override
+ public void addListener(AppIdleStateChangeListener listener) {
synchronized (mPackageAccessListeners) {
if (!mPackageAccessListeners.contains(listener)) {
mPackageAccessListeners.add(listener);
@@ -1019,13 +1016,15 @@ public class AppStandbyController {
}
}
- void removeListener(AppIdleStateChangeListener listener) {
+ @Override
+ public void removeListener(AppIdleStateChangeListener listener) {
synchronized (mPackageAccessListeners) {
mPackageAccessListeners.remove(listener);
}
}
- int getAppId(String packageName) {
+ @Override
+ public int getAppId(String packageName) {
try {
ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
PackageManager.MATCH_ANY_USER
@@ -1036,7 +1035,8 @@ public class AppStandbyController {
}
}
- boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
+ @Override
+ public boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
boolean shouldObfuscateInstantApps) {
if (isParoledOrCharging()) {
return false;
@@ -1048,8 +1048,7 @@ public class AppStandbyController {
return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
}
- /** Returns true if this app should be whitelisted for some reason, to never go into standby */
- boolean isAppSpecial(String packageName, int appId, int userId) {
+ private boolean isAppSpecial(String packageName, int appId, int userId) {
if (packageName == null) return false;
// If not enabled at all, of course nobody is ever idle.
if (!mAppIdleEnabled) {
@@ -1102,13 +1101,8 @@ public class AppStandbyController {
return false;
}
- /**
- * Checks if an app has been idle for a while and filters out apps that are excluded.
- * It returns false if the current system state allows all apps to be considered active.
- * This happens if the device is plugged in or temporarily allowed to make exceptions.
- * Called by interface impls.
- */
- boolean isAppIdleFiltered(String packageName, int appId, int userId,
+ @Override
+ public boolean isAppIdleFiltered(String packageName, int appId, int userId,
long elapsedRealtime) {
if (isAppSpecial(packageName, appId, userId)) {
return false;
@@ -1117,7 +1111,8 @@ public class AppStandbyController {
}
}
- int[] getIdleUidsForUser(int userId) {
+ @Override
+ public int[] getIdleUidsForUser(int userId) {
if (!mAppIdleEnabled) {
return new int[0];
}
@@ -1181,13 +1176,15 @@ public class AppStandbyController {
return res;
}
- void setAppIdleAsync(String packageName, boolean idle, int userId) {
+ @Override
+ public void setAppIdleAsync(String packageName, boolean idle, int userId) {
if (packageName == null || !mAppIdleEnabled) return;
mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
.sendToTarget();
}
+ @Override
@StandbyBuckets public int getAppStandbyBucket(String packageName, int userId,
long elapsedRealtime, boolean shouldObfuscateInstantApps) {
if (!mAppIdleEnabled || (shouldObfuscateInstantApps
@@ -1200,18 +1197,21 @@ public class AppStandbyController {
}
}
+ @Override
public List<AppStandbyInfo> getAppStandbyBuckets(int userId) {
synchronized (mAppIdleLock) {
return mAppIdleHistory.getAppStandbyBuckets(userId, mAppIdleEnabled);
}
}
+ @VisibleForTesting
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
int reason, long elapsedRealtime) {
setAppStandbyBucket(packageName, userId, newBucket, reason, elapsedRealtime, false);
}
- void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
+ @Override
+ public void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
int reason, long elapsedRealtime, boolean resetTimeout) {
synchronized (mAppIdleLock) {
// If the package is not installed, don't allow the bucket to be set.
@@ -1279,6 +1279,7 @@ public class AppStandbyController {
}
}
+ @Override
public void addActiveDeviceAdmin(String adminPkg, int userId) {
synchronized (mActiveAdminApps) {
Set<String> adminPkgs = mActiveAdminApps.get(userId);
@@ -1290,6 +1291,7 @@ public class AppStandbyController {
}
}
+ @Override
public void setActiveAdminApps(Set<String> adminPkgs, int userId) {
synchronized (mActiveAdminApps) {
if (adminPkgs == null) {
@@ -1300,6 +1302,7 @@ public class AppStandbyController {
}
}
+ @Override
public void onAdminDataAvailable() {
mAdminDataAvailableLatch.countDown();
}
@@ -1314,6 +1317,7 @@ public class AppStandbyController {
}
}
+ @VisibleForTesting
Set<String> getActiveAdminAppsForTest(int userId) {
synchronized (mActiveAdminApps) {
return mActiveAdminApps.get(userId);
@@ -1342,7 +1346,8 @@ public class AppStandbyController {
}
}
- void clearCarrierPrivilegedApps() {
+ @Override
+ public void clearCarrierPrivilegedApps() {
if (DEBUG) {
Slog.i(TAG, "Clearing carrier privileged apps list");
}
@@ -1368,7 +1373,7 @@ public class AppStandbyController {
return packageName != null && packageName.equals(activeScorer);
}
- void informListeners(String packageName, int userId, int bucket, int reason,
+ private void informListeners(String packageName, int userId, int bucket, int reason,
boolean userInteraction) {
final boolean idle = bucket >= STANDBY_BUCKET_RARE;
synchronized (mPackageAccessListeners) {
@@ -1381,7 +1386,7 @@ public class AppStandbyController {
}
}
- void informParoleStateChanged() {
+ private void informParoleStateChanged() {
final boolean paroled = isParoledOrCharging();
synchronized (mPackageAccessListeners) {
for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
@@ -1390,13 +1395,15 @@ public class AppStandbyController {
}
}
- void flushToDisk(int userId) {
+ @Override
+ public void flushToDisk(int userId) {
synchronized (mAppIdleLock) {
mAppIdleHistory.writeAppIdleTimes(userId);
}
}
- void flushDurationsToDisk() {
+ @Override
+ public void flushDurationsToDisk() {
// Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
// considered not-idle, which is the safest outcome in such an event.
synchronized (mAppIdleLock) {
@@ -1404,10 +1411,11 @@ public class AppStandbyController {
}
}
- boolean isDisplayOn() {
+ private boolean isDisplayOn() {
return mInjector.isDefaultDisplayOn();
}
+ @VisibleForTesting
void clearAppIdleForPackage(String packageName, int userId) {
synchronized (mAppIdleLock) {
mAppIdleHistory.clearUsage(packageName, userId);
@@ -1431,7 +1439,8 @@ public class AppStandbyController {
}
}
- void initializeDefaultsForSystemApps(int userId) {
+ @Override
+ public void initializeDefaultsForSystemApps(int userId) {
if (!mSystemServicesReady) {
// Do it later, since SettingsProvider wasn't queried yet for app_standby_enabled
mPendingInitializeDefaults = true;
@@ -1461,7 +1470,8 @@ public class AppStandbyController {
}
}
- void postReportContentProviderUsage(String name, String packageName, int userId) {
+ @Override
+ public void postReportContentProviderUsage(String name, String packageName, int userId) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = name;
args.arg2 = packageName;
@@ -1470,23 +1480,27 @@ public class AppStandbyController {
.sendToTarget();
}
- void postReportSyncScheduled(String packageName, int userId, boolean exempted) {
+ @Override
+ public void postReportSyncScheduled(String packageName, int userId, boolean exempted) {
mHandler.obtainMessage(MSG_REPORT_SYNC_SCHEDULED, userId, exempted ? 1 : 0, packageName)
.sendToTarget();
}
- void postReportExemptedSyncStart(String packageName, int userId) {
+ @Override
+ public void postReportExemptedSyncStart(String packageName, int userId) {
mHandler.obtainMessage(MSG_REPORT_EXEMPTED_SYNC_START, userId, 0, packageName)
.sendToTarget();
}
- void dumpUser(IndentingPrintWriter idpw, int userId, String pkg) {
+ @Override
+ public void dumpUser(IndentingPrintWriter idpw, int userId, String pkg) {
synchronized (mAppIdleLock) {
mAppIdleHistory.dump(idpw, userId, pkg);
}
}
- void dumpState(String[] args, PrintWriter pw) {
+ @Override
+ public void dumpState(String[] args, PrintWriter pw) {
synchronized (mAppIdleLock) {
pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
+ "): " + mCarrierPrivilegedApps);
diff --git a/api/current.txt b/api/current.txt
index 3ce043c89b65..a123f2cc8ba2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2834,6 +2834,7 @@ package android.accessibilityservice {
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method @NonNull public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController();
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
+ method @NonNull public final android.util.SparseArray<java.util.List<android.view.accessibility.AccessibilityWindowInfo>> getWindowsOnAllDisplays();
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public final android.os.IBinder onBind(android.content.Intent);
method @Deprecated protected boolean onGesture(int);
@@ -6363,6 +6364,7 @@ package android.app {
method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
+ method @NonNull public android.util.SparseArray<java.util.List<android.view.accessibility.AccessibilityWindowInfo>> getWindowsOnAllDisplays();
method public void grantRuntimePermission(String, String);
method public void grantRuntimePermissionAsUser(String, String, android.os.UserHandle);
method public boolean injectInputEvent(android.view.InputEvent, boolean);
@@ -57410,7 +57412,7 @@ package android.widget {
method public boolean onPrivateIMECommand(String, android.os.Bundle);
method public void onRestoreInstanceState(android.os.Parcelable);
method public android.os.Parcelable onSaveInstanceState();
- method protected void onSelectionChanged(int, int);
+ method @CallSuper protected void onSelectionChanged(int, int);
method protected void onTextChanged(CharSequence, int, int, int);
method public boolean onTextContextMenuItem(int);
method public void removeTextChangedListener(android.text.TextWatcher);
diff --git a/api/system-current.txt b/api/system-current.txt
index 6f4cb0f1a30d..696801a28882 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24,6 +24,7 @@ package android {
field public static final String BACKUP = "android.permission.BACKUP";
field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE";
field public static final String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE";
+ field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
field @Deprecated public static final String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
field public static final String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE";
field public static final String BIND_CONTENT_SUGGESTIONS_SERVICE = "android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE";
@@ -7304,6 +7305,14 @@ package android.telephony {
method @NonNull public android.telephony.CarrierRestrictionRules.Builder setMultiSimPolicy(int);
}
+ public abstract class CellBroadcastService extends android.app.Service {
+ ctor public CellBroadcastService();
+ method @CallSuper public android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onCdmaCellBroadcastSms(int, byte[]);
+ method public abstract void onGsmCellBroadcastSms(int, byte[]);
+ field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService";
+ }
+
public final class DataFailCause {
field public static final int ACCESS_ATTEMPT_ALREADY_IN_PROGRESS = 2219; // 0x8ab
field public static final int ACCESS_BLOCK = 2087; // 0x827
@@ -7876,8 +7885,8 @@ package android.telephony {
field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
- field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER = 268435456; // 0x10000000
- field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER = 536870912; // 0x20000000
+ field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
+ field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
diff --git a/api/test-current.txt b/api/test-current.txt
index 3dfd62e11622..d709886eff66 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5,6 +5,7 @@ package android {
field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
+ field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
@@ -2404,7 +2405,6 @@ package android.provider {
field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
field public static final String NOTIFICATION_BADGING = "notification_badging";
- field @Deprecated public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
@@ -2901,8 +2901,10 @@ package android.telephony {
}
public class PhoneStateListener {
- field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER = 268435456; // 0x10000000
- field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER = 536870912; // 0x20000000
+ method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
+ method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
+ field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
}
public class ServiceState implements android.os.Parcelable {
diff --git a/cmds/locksettings/TEST_MAPPING b/cmds/locksettings/TEST_MAPPING
index c1cba5f7f22d..56f5cc034f05 100644
--- a/cmds/locksettings/TEST_MAPPING
+++ b/cmds/locksettings/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-devicepolicy": [
{
"name": "CtsDevicePolicyManagerTestCases",
"options": [
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 42132ee0daae..7d446a9a1ed6 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -53,6 +53,8 @@ void sigHandler(int sig) {
if (gStatsService != nullptr) {
gStatsService->Terminate();
}
+ ALOGW("statsd terminated on receiving signal %d.", sig);
+ exit(1);
}
void registerSigHandler()
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 76ee9a6e5996..460b9e0995c8 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -1199,87 +1199,66 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
ConfigKey cfgKey1(uid, 12341);
long timeBase1 = 1;
- sp<StatsLogProcessor> processor =
+ sp<StatsLogProcessor> processor1 =
CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
// Metric 1 is not active.
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_EQ(1, processor->mMetricsManagers.size());
- auto it = processor->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ EXPECT_EQ(1, processor1->mMetricsManagers.size());
+ auto it = processor1->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor1->mMetricsManagers.end());
auto& metricsManager1 = it->second;
EXPECT_TRUE(metricsManager1->isActive());
- auto metricIt = metricsManager1->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
- auto& metricProducer1 = *metricIt;
- EXPECT_FALSE(metricProducer1->isActive());
-
- metricIt = metricsManager1->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
- auto& metricProducer2 = *metricIt;
- EXPECT_TRUE(metricProducer2->isActive());
-
- int i = 0;
- for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
- if (metricsManager1->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activation1 = metricProducer1->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
- EXPECT_EQ(0, activation1->start_ns);
- EXPECT_EQ(kNotActive, activation1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
-
- i = 0;
- for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
- if (metricsManager1->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
- const auto& activation2 = metricProducer1->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
- EXPECT_EQ(0, activation2->start_ns);
- EXPECT_EQ(kNotActive, activation2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
+ EXPECT_EQ(metricsManager1->mAllMetricProducers.size(), 2);
+ // We assume that the index of a MetricProducer within the mAllMetricProducers
+ // array follows the order in which metrics are added to the config.
+ auto& metricProducer1_1 = metricsManager1->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer1_1->getMetricId(), metricId1);
+ EXPECT_FALSE(metricProducer1_1->isActive()); // inactive due to associated MetricActivation
+
+ auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1];
+ EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2);
+ EXPECT_TRUE(metricProducer1_2->isActive());
+
+ EXPECT_EQ(metricProducer1_1->mEventActivationMap.size(), 2);
+ // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+ // that matchers are indexed in the order that they are added to the config.
+ const auto& activation1_1_1 = metricProducer1_1->mEventActivationMap.at(0);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1_1_1->ttl_ns);
+ EXPECT_EQ(0, activation1_1_1->start_ns);
+ EXPECT_EQ(kNotActive, activation1_1_1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation1_1_1->activationType);
+
+ const auto& activation1_1_2 = metricProducer1_1->mEventActivationMap.at(1);
+ EXPECT_EQ(200 * NS_PER_SEC, activation1_1_2->ttl_ns);
+ EXPECT_EQ(0, activation1_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation1_1_2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1_1_2->activationType);
// }}}------------------------------------------------------------------------------
// Trigger Activation 1 for Metric 1
std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
- processor->OnLogEvent(event.get());
+ processor1->OnLogEvent(event.get());
// Metric 1 is not active; Activation 1 set to kActiveOnBoot
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_FALSE(metricProducer1->isActive());
- EXPECT_EQ(0, activation1->start_ns);
- EXPECT_EQ(kActiveOnBoot, activation1->state);
- EXPECT_EQ(0, activation2->start_ns);
- EXPECT_EQ(kNotActive, activation2->state);
+ EXPECT_FALSE(metricProducer1_1->isActive());
+ EXPECT_EQ(0, activation1_1_1->start_ns);
+ EXPECT_EQ(kActiveOnBoot, activation1_1_1->state);
+ EXPECT_EQ(0, activation1_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation1_1_2->state);
- EXPECT_TRUE(metricProducer2->isActive());
+ EXPECT_TRUE(metricProducer1_2->isActive());
// }}}-----------------------------------------------------------------------------
// Simulate shutdown by saving state to disk
int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
- processor->SaveActiveConfigsToDisk(shutDownTime);
- EXPECT_FALSE(metricProducer1->isActive());
- int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
+ processor1->SaveActiveConfigsToDisk(shutDownTime);
+ EXPECT_FALSE(metricProducer1_1->isActive());
// Simulate device restarted state by creating new instance of StatsLogProcessor with the
// same config.
@@ -1293,55 +1272,34 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
EXPECT_EQ(1, processor2->mMetricsManagers.size());
it = processor2->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor2->mMetricsManagers.end());
- auto& metricsManager1001 = it->second;
- EXPECT_TRUE(metricsManager1001->isActive());
-
- metricIt = metricsManager1001->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
- auto& metricProducer1001 = *metricIt;
- EXPECT_FALSE(metricProducer1001->isActive());
-
- metricIt = metricsManager1001->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
- auto& metricProducer1002 = *metricIt;
- EXPECT_TRUE(metricProducer1002->isActive());
-
- i = 0;
- for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
- if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns);
- EXPECT_EQ(0, activation1001_1->start_ns);
- EXPECT_EQ(kNotActive, activation1001_1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001_1->activationType);
-
- i = 0;
- for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
- if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
+ auto& metricsManager2 = it->second;
+ EXPECT_TRUE(metricsManager2->isActive());
- const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns);
- EXPECT_EQ(0, activation1001_2->start_ns);
- EXPECT_EQ(kNotActive, activation1001_2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1001_2->activationType);
+ EXPECT_EQ(metricsManager2->mAllMetricProducers.size(), 2);
+ // We assume that the index of a MetricProducer within the mAllMetricProducers
+ // array follows the order in which metrics are added to the config.
+ auto& metricProducer2_1 = metricsManager2->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer2_1->getMetricId(), metricId1);
+ EXPECT_FALSE(metricProducer2_1->isActive());
+
+ auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1];
+ EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2);
+ EXPECT_TRUE(metricProducer2_2->isActive());
+
+ EXPECT_EQ(metricProducer2_1->mEventActivationMap.size(), 2);
+ // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+ // that matchers are indexed in the order that they are added to the config.
+ const auto& activation2_1_1 = metricProducer2_1->mEventActivationMap.at(0);
+ EXPECT_EQ(100 * NS_PER_SEC, activation2_1_1->ttl_ns);
+ EXPECT_EQ(0, activation2_1_1->start_ns);
+ EXPECT_EQ(kNotActive, activation2_1_1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation2_1_1->activationType);
+
+ const auto& activation2_1_2 = metricProducer2_1->mEventActivationMap.at(1);
+ EXPECT_EQ(200 * NS_PER_SEC, activation2_1_2->ttl_ns);
+ EXPECT_EQ(0, activation2_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation2_1_2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2_1_2->activationType);
// }}}-----------------------------------------------------------------------------------
// Load saved state from disk.
@@ -1350,13 +1308,14 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
// Metric 1 active; Activation 1 is active, Activation 2 is not active
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
- EXPECT_EQ(kActive, activation1001_1->state);
- EXPECT_EQ(0, activation1001_2->start_ns);
- EXPECT_EQ(kNotActive, activation1001_2->state);
+ EXPECT_TRUE(metricProducer2_1->isActive());
+ int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
+ EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
+ EXPECT_EQ(kActive, activation2_1_1->state);
+ EXPECT_EQ(0, activation2_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation2_1_2->state);
- EXPECT_TRUE(metricProducer1002->isActive());
+ EXPECT_TRUE(metricProducer2_2->isActive());
// }}}--------------------------------------------------------------------------------
// Trigger Activation 2 for Metric 1.
@@ -1369,23 +1328,23 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
// Metric 1 active; Activation 1 is active, Activation 2 is active
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
- EXPECT_EQ(kActive, activation1001_1->state);
- EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation1001_2->start_ns);
- EXPECT_EQ(kActive, activation1001_2->state);
+ EXPECT_TRUE(metricProducer2_1->isActive());
+ EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
+ EXPECT_EQ(kActive, activation2_1_1->state);
+ EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation2_1_2->start_ns);
+ EXPECT_EQ(kActive, activation2_1_2->state);
- EXPECT_TRUE(metricProducer1002->isActive());
+ EXPECT_TRUE(metricProducer2_2->isActive());
// }}}---------------------------------------------------------------------------
// Simulate shutdown by saving state to disk
shutDownTime = timeBase2 + 50 * NS_PER_SEC;
processor2->SaveActiveConfigsToDisk(shutDownTime);
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_TRUE(metricProducer1002->isActive());
- ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime;
- int64_t ttl2 = screenOnEvent->GetElapsedTimestampNs() +
- metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime;
+ EXPECT_TRUE(metricProducer2_1->isActive());
+ EXPECT_TRUE(metricProducer2_2->isActive());
+ ttl1 -= shutDownTime - timeBase2;
+ int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC
+ - (shutDownTime - screenOnEvent->GetElapsedTimestampNs());
// Simulate device restarted state by creating new instance of StatsLogProcessor with the
// same config.
@@ -1399,55 +1358,34 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
EXPECT_EQ(1, processor3->mMetricsManagers.size());
it = processor3->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor3->mMetricsManagers.end());
- auto& metricsManagerTimeBase3 = it->second;
- EXPECT_TRUE(metricsManagerTimeBase3->isActive());
-
- metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
- for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
- auto& metricProducerTimeBase3_1 = *metricIt;
- EXPECT_FALSE(metricProducerTimeBase3_1->isActive());
-
- metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
- for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
- auto& metricProducerTimeBase3_2 = *metricIt;
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
-
- i = 0;
- for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
- if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns);
- EXPECT_EQ(0, activationTimeBase3_1->start_ns);
- EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activationTimeBase3_1->activationType);
-
- i = 0;
- for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
- if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
+ auto& metricsManager3 = it->second;
+ EXPECT_TRUE(metricsManager3->isActive());
- const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns);
- EXPECT_EQ(0, activationTimeBase3_2->start_ns);
- EXPECT_EQ(kNotActive, activationTimeBase3_2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activationTimeBase3_2->activationType);
+ EXPECT_EQ(metricsManager3->mAllMetricProducers.size(), 2);
+ // We assume that the index of a MetricProducer within the mAllMetricProducers
+ // array follows the order in which metrics are added to the config.
+ auto& metricProducer3_1 = metricsManager3->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer3_1->getMetricId(), metricId1);
+ EXPECT_FALSE(metricProducer3_1->isActive());
+
+ auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1];
+ EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2);
+ EXPECT_TRUE(metricProducer3_2->isActive());
+
+ EXPECT_EQ(metricProducer3_1->mEventActivationMap.size(), 2);
+ // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+ // that matchers are indexed in the order that they are added to the config.
+ const auto& activation3_1_1 = metricProducer3_1->mEventActivationMap.at(0);
+ EXPECT_EQ(100 * NS_PER_SEC, activation3_1_1->ttl_ns);
+ EXPECT_EQ(0, activation3_1_1->start_ns);
+ EXPECT_EQ(kNotActive, activation3_1_1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation3_1_1->activationType);
+
+ const auto& activation3_1_2 = metricProducer3_1->mEventActivationMap.at(1);
+ EXPECT_EQ(200 * NS_PER_SEC, activation3_1_2->ttl_ns);
+ EXPECT_EQ(0, activation3_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation3_1_2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation3_1_2->activationType);
// }}}----------------------------------------------------------------------------------
// Load saved state from disk.
@@ -1456,13 +1394,13 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
// Metric 1 active: Activation 1 is active, Activation 2 is active
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
- EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns);
- EXPECT_EQ(kActive, activationTimeBase3_1->state);
- EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
- EXPECT_EQ(kActive, activationTimeBase3_2->state);
+ EXPECT_TRUE(metricProducer3_1->isActive());
+ EXPECT_EQ(timeBase3 + ttl1 - activation3_1_1->ttl_ns, activation3_1_1->start_ns);
+ EXPECT_EQ(kActive, activation3_1_1->state);
+ EXPECT_EQ(timeBase3 + ttl2 - activation3_1_2->ttl_ns, activation3_1_2->start_ns);
+ EXPECT_EQ(kActive, activation3_1_2->state);
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+ EXPECT_TRUE(metricProducer3_2->isActive());
// }}}-------------------------------------------------------------------------------
@@ -1473,15 +1411,16 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
);
processor3->OnLogEvent(screenOnEvent.get());
- // Metric 1 active; Activation 1 is not active, Activation 2 is set to active
+ // Metric 1 active; Activation 1 is inactive (above screenOnEvent causes ttl1 to expire),
+ // Activation 2 is set to active
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
- EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
- EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activationTimeBase3_2->start_ns);
- EXPECT_EQ(kActive, activationTimeBase3_2->state);
+ EXPECT_TRUE(metricProducer3_1->isActive());
+ EXPECT_EQ(kNotActive, activation3_1_1->state);
+ EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation3_1_2->start_ns);
+ EXPECT_EQ(kActive, activation3_1_2->state);
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+ EXPECT_TRUE(metricProducer3_2->isActive());
// }}}---------------------------------------------------------------------------
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index d3c274f4bdfe..4603f08c765f 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -605,7 +605,7 @@ public abstract class AccessibilityService extends Service {
}
/**
- * Gets the windows on the screen. This method returns only the windows
+ * Gets the windows on the screen of the default display. This method returns only the windows
* that a sighted user can interact with, as opposed to all windows.
* For example, if there is a modal dialog shown and the user cannot touch
* anything behind it, then only the modal window will be reported
@@ -632,6 +632,34 @@ public abstract class AccessibilityService extends Service {
}
/**
+ * Gets the windows on the screen of all displays. This method returns only the windows
+ * that a sighted user can interact with, as opposed to all windows.
+ * For example, if there is a modal dialog shown and the user cannot touch
+ * anything behind it, then only the modal window will be reported
+ * (assuming it is the top one). For convenience the returned windows
+ * are ordered in a descending layer order, which is the windows that
+ * are on top are reported first. Since the user can always
+ * interact with the window that has input focus by typing, the focused
+ * window is always returned (even if covered by a modal window).
+ * <p>
+ * <strong>Note:</strong> In order to access the windows your service has
+ * to declare the capability to retrieve window content by setting the
+ * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
+ * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
+ * Also the service has to opt-in to retrieve the interactive windows by
+ * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
+ * flag.
+ * </p>
+ *
+ * @return The windows of all displays if there are windows and the service is can retrieve
+ * them, otherwise an empty list. The key of SparseArray is display ID.
+ */
+ @NonNull
+ public final SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() {
+ return AccessibilityInteractionClient.getInstance().getWindowsOnAllDisplays(mConnectionId);
+ }
+
+ /**
* Gets the root node in the currently active window if this service
* can retrieve window content. The active window is the one that the user
* is currently touching or the window with input focus, if the user is not
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 1ca07dd6ed23..4841781170e1 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -60,7 +60,7 @@ interface IAccessibilityServiceConnection {
AccessibilityWindowInfo getWindow(int windowId);
- List<AccessibilityWindowInfo> getWindows();
+ AccessibilityWindowInfo.WindowListSparseArray getWindows();
AccessibilityServiceInfo getServiceInfo();
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 9b667a118ebc..b1565ab8a501 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -87,12 +87,6 @@ public class KeyguardManager {
"android.app.action.CONFIRM_FRP_CREDENTIAL";
/**
- * @hide
- */
- public static final String EXTRA_BIOMETRIC_PROMPT_BUNDLE =
- "android.app.extra.BIOMETRIC_PROMPT_BUNDLE";
-
- /**
* A CharSequence dialog title to show to the user when used with a
* {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
* @hide
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 40cb29fc80ab..cb9ebac728ec 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -20,6 +20,7 @@ import static android.app.ActivityThread.DEBUG_CONFIGURATION;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -191,6 +192,17 @@ public class ResourcesManager {
}
}
Log.i(TAG, "Invalidated " + count + " asset managers that referenced " + path);
+
+ for (int i = mCachedApkAssets.size() - 1; i >= 0; i--) {
+ final ApkKey key = mCachedApkAssets.keyAt(i);
+ if (key.path.equals(path)) {
+ WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.remove(key);
+ if (apkAssetsRef != null && apkAssetsRef.get() != null) {
+ apkAssetsRef.get().close();
+ }
+ mCachedApkAssets.remove(key);
+ }
+ }
}
}
@@ -1000,6 +1012,14 @@ public class ResourcesManager {
}
}
+ @TestApi
+ public final boolean applyConfigurationToResources(@NonNull Configuration config,
+ @Nullable CompatibilityInfo compat) {
+ synchronized(this) {
+ return applyConfigurationToResourcesLocked(config, compat);
+ }
+ }
+
public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
try {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 049933743450..e81dc1c59040 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1343,13 +1343,13 @@ public final class SystemServiceRegistry {
* @hide
*/
public static <T> void registerCachedService(String serviceName, Class<T> serviceWrapperClass,
- BiFunction<ContextImpl, IBinder, T> serviceFetcher) {
+ BiFunction<Context, IBinder, T> serviceFetcher) {
registerService(serviceName, serviceWrapperClass,
new CachedServiceFetcher<T>() {
@Override
public T createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(serviceName);
- return serviceFetcher.apply(ctx, b);
+ return serviceFetcher.apply(ctx.getOuterContext(), b);
}});
}
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index fd93450c29f0..13d566c0e04b 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -42,6 +42,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
+import android.util.SparseArray;
import android.view.Display;
import android.view.InputEvent;
import android.view.KeyEvent;
@@ -535,7 +536,7 @@ public final class UiAutomation {
}
/**
- * Gets the windows on the screen. This method returns only the windows
+ * Gets the windows on the screen of the default display. This method returns only the windows
* that a sighted user can interact with, as opposed to all windows.
* For example, if there is a modal dialog shown and the user cannot touch
* anything behind it, then only the modal window will be reported
@@ -562,6 +563,35 @@ public final class UiAutomation {
}
/**
+ * Gets the windows on the screen of all displays. This method returns only the windows
+ * that a sighted user can interact with, as opposed to all windows.
+ * For example, if there is a modal dialog shown and the user cannot touch
+ * anything behind it, then only the modal window will be reported
+ * (assuming it is the top one). For convenience the returned windows
+ * are ordered in a descending layer order, which is the windows that
+ * are higher in the Z-order are reported first.
+ * <p>
+ * <strong>Note:</strong> In order to access the windows you have to opt-in
+ * to retrieve the interactive windows by setting the
+ * {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
+ * </p>
+ *
+ * @return The windows of all displays if there are windows and the service is can retrieve
+ * them, otherwise an empty list. The key of SparseArray is display ID.
+ */
+ @NonNull
+ public SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() {
+ final int connectionId;
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ connectionId = mConnectionId;
+ }
+ // Calling out without a lock held.
+ return AccessibilityInteractionClient.getInstance()
+ .getWindowsOnAllDisplays(connectionId);
+ }
+
+ /**
* Gets the root {@link AccessibilityNodeInfo} in the active window.
*
* @return The root info.
diff --git a/core/java/android/app/usage/AppStandbyInfo.java b/core/java/android/app/usage/AppStandbyInfo.java
index ebdbf833b81a..c283702a6bbf 100644
--- a/core/java/android/app/usage/AppStandbyInfo.java
+++ b/core/java/android/app/usage/AppStandbyInfo.java
@@ -22,6 +22,10 @@ import android.os.Parcelable;
/**
* A pair of {package, bucket} to denote the app standby bucket for a given package.
* Used as a vehicle of data across the binder IPC.
+ *
+ * Note we're not moving this class to the jobscheduler apex, because it's consumed by
+ * UsageStatsManager, which is not updatable anyway, so making this updatable isn't really
+ * beneficial.
* @hide
*/
public final class AppStandbyInfo implements Parcelable {
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index e24512ac525d..c3daad188792 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -16,8 +16,6 @@
package android.content;
-import static android.app.AppOpsManager.strOpToOp;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -52,6 +50,19 @@ import java.lang.annotation.RetentionPolicy;
* permission model for which the user had disabled the "permission"
* which is achieved by disallowing the corresponding app op.
* </p>
+ * <p>
+ * This class has two types of methods and you should be careful which
+ * type to call based on whether permission protected data is being
+ * passed to the app or you are just checking whether the app holds a
+ * permission. The reason is that a permission check requires checking
+ * the runtime permission and if it is granted checking the corresponding
+ * app op as for apps not supporting the runtime mode we never revoke
+ * permissions but disable app ops. Since there are two types of app op
+ * checks, one that does not leave a record an action was performed and
+ * another the does, one needs to call the preflight flavor of the checks
+ * named xxxForPreflight only if no private data is being delivered but
+ * a permission check is what is needed and the xxxForDataDelivery where
+ * the permission check is right before private data delivery.
*
* @hide
*/
@@ -65,6 +76,9 @@ public final class PermissionChecker {
/** Permission result: The permission is denied because the app op is not allowed. */
public static final int PERMISSION_DENIED_APP_OP = PackageManager.PERMISSION_DENIED - 1;
+ /** Constant when the PID for which we check permissions is unknown. */
+ public static final int PID_UNKNOWN = -1;
+
/** @hide */
@IntDef({PERMISSION_GRANTED,
PERMISSION_DENIED,
@@ -77,63 +91,97 @@ public final class PermissionChecker {
}
/**
- * @deprecated Use {@link #checkPermission(Context, String, int, int, String, String)} instead
+ * Checks whether a given package in a UID and PID has a given permission
+ * and whether the app op that corresponds to this permission is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(Context, String, int, int, String)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
+ * is not known.
+ * @param uid The uid for which to check.
+ * @param packageName The package name for which to check. If null the
+ * the first package for the calling UID will be used.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ * @param message A message describing the reason the permission was checked
+ *
+ * @see #checkPermissionForPreflight(Context, String, int, int, String)
*/
- @Deprecated
@PermissionResult
- public static int checkPermission(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName) {
- return checkPermission(context, permission, pid, uid, packageName, null);
+ public static int checkPermissionForDataDelivery(@NonNull Context context,
+ @NonNull String permission, int pid, int uid, @Nullable String packageName,
+ @Nullable String message) {
+ return checkPermissionCommon(context, permission, pid, uid, packageName, message,
+ true /*forDataDelivery*/);
}
/**
* Checks whether a given package in a UID and PID has a given permission
* and whether the app op that corresponds to this permission is allowed.
*
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * preflight point where you will not deliver the permission protected data
+ * to clients but schedule permission data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have location
+ * permission (if app has only foreground location the grant state depends on the app's
+ * fg/gb state) and this check will not leave a trace that permission protected data
+ * was delivered. When you are about to deliver the location data to a registered
+ * listener you should use {@link #checkPermissionForDataDelivery(Context, String,
+ * int, int, String, String)} which will evaluate the permission access based on the current
+ * fg/bg state of the app and leave a record that the data was accessed.
+ *
* @param context Context for accessing resources.
* @param permission The permission to check.
* @param pid The process id for which to check.
* @param uid The uid for which to check.
* @param packageName The package name for which to check. If null the
* the first package for the calling UID will be used.
- * @param message A message describing the reason the permission was checked
- *
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ *
+ * @see #checkPermissionForDataDelivery(Context, String, int, int, String, String)
*/
@PermissionResult
- public static int checkPermission(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName, @Nullable String message) {
- if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
- return PERMISSION_DENIED;
- }
-
- AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- String op = appOpsManager.permissionToOp(permission);
- if (op == null) {
- return PERMISSION_GRANTED;
- }
-
- if (packageName == null) {
- String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
- if (packageNames == null || packageNames.length <= 0) {
- return PERMISSION_DENIED;
- }
- packageName = packageNames[0];
- }
-
- if (appOpsManager.noteProxyOpNoThrow(strOpToOp(op), packageName, uid, message)
- != AppOpsManager.MODE_ALLOWED) {
- return PERMISSION_DENIED_APP_OP;
- }
-
- return PERMISSION_GRANTED;
+ public static int checkPermissionForPreflight(@NonNull Context context,
+ @NonNull String permission, int pid, int uid, @Nullable String packageName) {
+ return checkPermissionCommon(context, permission, pid, uid, packageName, null /*message*/,
+ false /*forDataDelivery*/);
}
/**
* Checks whether your app has a given permission and whether the app op
* that corresponds to this permission is allowed.
*
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkSelfPermissionForPreflight(Context, String)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method
+ * which will evaluate the permission access based on the current fg/bg state of the
+ * app and leave a record that the data was accessed.
+ *
* <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
* {@link Process#myUid()}.
*
@@ -141,74 +189,229 @@ public final class PermissionChecker {
* @param permission The permission to check.
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ * @param message A message describing the reason the permission was checked
+ *
+ * @see #checkSelfPermissionForPreflight(Context, String)
*/
@PermissionResult
- public static int checkSelfPermission(@NonNull Context context,
- @NonNull String permission) {
- return checkPermission(context, permission, Process.myPid(),
- Process.myUid(), context.getPackageName(), null /* self access */);
+ public static int checkSelfPermissionForDataDelivery(@NonNull Context context,
+ @NonNull String permission, @Nullable String message) {
+ return checkPermissionForDataDelivery(context, permission, Process.myPid(),
+ Process.myUid(), context.getPackageName(), message);
}
/**
- * @deprecated Use {@link #checkCallingPermission(Context, String, String, String)} instead
+ * Checks whether your app has a given permission and whether the app op
+ * that corresponds to this permission is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * preflight point where you will not deliver the permission protected data
+ * to clients but schedule permission data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have location
+ * permission (if app has only foreground location the grant state depends on the
+ * app's fg/gb state) and this check will not leave a trace that permission protected
+ * data was delivered. When you are about to deliver the location data to a registered
+ * listener you should use this method which will evaluate the permission access based
+ * on the current fg/bg state of the app and leave a record that the data was accessed.
+ *
+ * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
+ * {@link Process#myUid()}.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ *
+ * @see #checkSelfPermissionForDataDelivery(Context, String, String)
*/
- @Deprecated
@PermissionResult
- public static int checkCallingPermission(@NonNull Context context,
- @NonNull String permission, @Nullable String packageName) {
- return checkCallingPermission(context, permission, packageName, null);
+ public static int checkSelfPermissionForPreflight(@NonNull Context context,
+ @NonNull String permission) {
+ return checkPermissionForPreflight(context, permission, Process.myPid(),
+ Process.myUid(), context.getPackageName());
}
/**
* Checks whether the IPC you are handling has a given permission and whether
* the app op that corresponds to this permission is allowed.
*
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkCallingPermissionForPreflight(Context, String, String)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
* @param context Context for accessing resources.
* @param permission The permission to check.
* @param packageName The package name making the IPC. If null the
* the first package for the calling UID will be used.
- * @param message A message describing the reason the permission was checked
- *
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ * @param message A message describing the reason the permission was checked
+ *
+ * @see #checkCallingPermissionForPreflight(Context, String, String)
*/
@PermissionResult
- public static int checkCallingPermission(@NonNull Context context,
+ public static int checkCallingPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, @Nullable String packageName, @Nullable String message) {
if (Binder.getCallingPid() == Process.myPid()) {
return PERMISSION_DENIED;
}
- return checkPermission(context, permission, Binder.getCallingPid(),
+ return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
Binder.getCallingUid(), packageName, message);
}
/**
- * @deprecated Use {@link #checkCallingOrSelfPermission(Context, String, String)} instead
+ * Checks whether the IPC you are handling has a given permission and whether
+ * the app op that corresponds to this permission is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * preflight point where you will not deliver the permission protected data
+ * to clients but schedule permission data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have location
+ * permission (if app has only foreground location the grant state depends on the app's
+ * fg/gb state) and this check will not leave a trace that permission protected data
+ * was delivered. When you are about to deliver the location data to a registered
+ * listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context,
+ * String, String)} which will evaluate the permission access based on the current fg/bg state
+ * of the app and leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param packageName The package name making the IPC. If null the
+ * the first package for the calling UID will be used.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ *
+ * @see #checkCallingPermissionForDataDelivery(Context, String, String, String)
*/
- @Deprecated
@PermissionResult
- public static int checkCallingOrSelfPermission(@NonNull Context context,
- @NonNull String permission) {
- return checkCallingOrSelfPermission(context, permission, null);
+ public static int checkCallingPermissionForPreflight(@NonNull Context context,
+ @NonNull String permission, @Nullable String packageName) {
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return PERMISSION_DENIED;
+ }
+ return checkPermissionForPreflight(context, permission, Binder.getCallingPid(),
+ Binder.getCallingUid(), packageName);
}
/**
* Checks whether the IPC you are handling or your app has a given permission
* and whether the app op that corresponds to this permission is allowed.
*
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkCallingOrSelfPermissionForPreflight(Context, String)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
* @param context Context for accessing resources.
* @param permission The permission to check.
- * @param message A message describing the reason the permission was checked
- *
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ * @param message A message describing the reason the permission was checked
+ *
+ * @see #checkCallingOrSelfPermissionForPreflight(Context, String)
*/
@PermissionResult
- public static int checkCallingOrSelfPermission(@NonNull Context context,
+ public static int checkCallingOrSelfPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, @Nullable String message) {
String packageName = (Binder.getCallingPid() == Process.myPid())
? context.getPackageName() : null;
- return checkPermission(context, permission, Binder.getCallingPid(),
+ return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
Binder.getCallingUid(), packageName, message);
}
-}
+
+ /**
+ * Checks whether the IPC you are handling or your app has a given permission
+ * and whether the app op that corresponds to this permission is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * preflight point where you will not deliver the permission protected data
+ * to clients but schedule permission data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have location
+ * permission (if app has only foreground location the grant state depends on the
+ * app's fg/gb state) and this check will not leave a trace that permission protected
+ * data was delivered. When you are about to deliver the location data to a registered
+ * listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context,
+ * String, String)} which will evaluate the permission access based on the current fg/bg state
+ * of the app and leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ *
+ * @see #checkCallingOrSelfPermissionForDataDelivery(Context, String, String)
+ */
+ @PermissionResult
+ public static int checkCallingOrSelfPermissionForPreflight(@NonNull Context context,
+ @NonNull String permission) {
+ String packageName = (Binder.getCallingPid() == Process.myPid())
+ ? context.getPackageName() : null;
+ return checkPermissionForPreflight(context, permission, Binder.getCallingPid(),
+ Binder.getCallingUid(), packageName);
+ }
+
+ private static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
+ int pid, int uid, @Nullable String packageName, @Nullable String message,
+ boolean forDataDelivery) {
+ if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
+ return PERMISSION_DENIED;
+ }
+
+ AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ String op = appOpsManager.permissionToOp(permission);
+ if (op == null) {
+ return PERMISSION_GRANTED;
+ }
+
+ if (packageName == null) {
+ String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+ if (packageNames == null || packageNames.length <= 0) {
+ return PERMISSION_DENIED;
+ }
+ packageName = packageNames[0];
+ }
+
+ if (forDataDelivery) {
+ if (appOpsManager.noteProxyOpNoThrow(op, packageName, uid, message)
+ != AppOpsManager.MODE_ALLOWED) {
+ return PERMISSION_DENIED_APP_OP;
+ }
+ } else {
+ final int mode = appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
+ if (mode != AppOpsManager.MODE_ALLOWED && mode != AppOpsManager.MODE_FOREGROUND) {
+ return PERMISSION_DENIED_APP_OP;
+ }
+ }
+
+ return PERMISSION_GRANTED;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 43a4fe5bc414..3d7e3befd9f1 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -157,4 +157,10 @@ interface IOverlayManager {
* Returns the list of default overlay packages, or an empty array if there are none.
*/
String[] getDefaultOverlayPackages();
+
+ /**
+ * Invalidates and removes the idmap for an overlay,
+ * @param packageName The name of the overlay package whose idmap should be deleted.
+ */
+ void invalidateCachesForOverlay(in String packageName, in int userIs);
}
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index f2716fedc186..853e8189ea8a 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -163,4 +164,28 @@ public class OverlayManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns information about all overlays for the given target package for
+ * the specified user. The returned list is ordered according to the
+ * overlay priority with the highest priority at the end of the list.
+ *
+ * @param targetPackageName The name of the target package.
+ * @param user The user to get the OverlayInfos for.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(anyOf = {
+ "android.permission.INTERACT_ACROSS_USERS",
+ })
+ @NonNull
+ public void invalidateCachesForOverlay(@NonNull final String targetPackageName,
+ @NonNull UserHandle user) {
+ try {
+ mService.invalidateCachesForOverlay(targetPackageName, user.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 69462ab99483..a35ad567ed81 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -188,7 +188,7 @@ public final class ApkAssets {
/**
* Closes this class and the contained {@link #mStringBlock}.
*/
- public void close() throws Throwable {
+ public void close() {
synchronized (this) {
if (mOpen) {
mOpen = false;
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index b7bc8229fa45..2ae1932c3437 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -175,7 +175,7 @@ final class StringBlock {
}
}
- public void close() throws Throwable {
+ public void close() {
synchronized (this) {
if (mOpen) {
mOpen = false;
diff --git a/core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl b/core/java/android/hardware/biometrics/Authenticator.java
index 8b35852efd31..6d7e7488f2d0 100644
--- a/core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl
+++ b/core/java/android/hardware/biometrics/Authenticator.java
@@ -17,10 +17,19 @@
package android.hardware.biometrics;
/**
- * Communication channel between ConfirmDeviceCredential / ConfirmLock* and BiometricService.
+ * Type of authenticators defined on a granularity that the BiometricManager / BiometricPrompt
+ * supports.
* @hide
*/
-interface IBiometricConfirmDeviceCredentialCallback {
- // Invoked when authentication should be canceled.
- oneway void cancel();
-} \ No newline at end of file
+public class Authenticator {
+
+ /**
+ * Device credential, e.g. Pin/Pattern/Password.
+ */
+ public static final int TYPE_CREDENTIAL = 1 << 0;
+ /**
+ * Encompasses all biometrics on the device, e.g. Fingerprint/Iris/Face.
+ */
+ public static final int TYPE_BIOMETRIC = 1 << 1;
+
+}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index d8110f33d723..cbe8a052db2f 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -201,55 +201,5 @@ public class BiometricManager {
}
}
- /**
- * TODO(b/123378871): Remove when moved.
- * @hide
- */
- @RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void onConfirmDeviceCredentialSuccess() {
- if (mService != null) {
- try {
- mService.onConfirmDeviceCredentialSuccess();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- } else {
- Slog.w(TAG, "onConfirmDeviceCredentialSuccess(): Service not connected");
- }
- }
-
- /**
- * TODO(b/123378871): Remove when moved.
- * @hide
- */
- @RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void onConfirmDeviceCredentialError(int error, String message) {
- if (mService != null) {
- try {
- mService.onConfirmDeviceCredentialError(error, message);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- } else {
- Slog.w(TAG, "onConfirmDeviceCredentialError(): Service not connected");
- }
- }
-
- /**
- * TODO(b/123378871): Remove when moved.
- * @hide
- */
- @RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void registerCancellationCallback(IBiometricConfirmDeviceCredentialCallback callback) {
- if (mService != null) {
- try {
- mService.registerCancellationCallback(callback);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- } else {
- Slog.w(TAG, "registerCancellationCallback(): Service not connected");
- }
- }
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index fb6b231632f1..cf86e25112d2 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -69,24 +69,21 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
/**
* @hide
*/
- public static final String KEY_POSITIVE_TEXT = "positive_text";
- /**
- * @hide
- */
public static final String KEY_NEGATIVE_TEXT = "negative_text";
/**
* @hide
*/
public static final String KEY_REQUIRE_CONFIRMATION = "require_confirmation";
/**
+ * This is deprecated. Internally we should use {@link #KEY_AUTHENTICATORS_ALLOWED}
* @hide
*/
public static final String KEY_ALLOW_DEVICE_CREDENTIAL = "allow_device_credential";
/**
+ * If this key is set, we will ignore {@link #KEY_ALLOW_DEVICE_CREDENTIAL}
* @hide
*/
- public static final String KEY_FROM_CONFIRM_DEVICE_CREDENTIAL
- = "from_confirm_device_credential";
+ public static final String KEY_AUTHENTICATORS_ALLOWED = "authenticators_allowed";
/**
* Error/help message will show for this amount of time.
@@ -100,7 +97,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
/**
* @hide
*/
- public static final int DISMISSED_REASON_CONFIRMED = 1;
+ public static final int DISMISSED_REASON_BIOMETRIC_CONFIRMED = 1;
/**
* Dialog is done animating away after user clicked on the button set via
@@ -119,7 +116,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* Authenticated, confirmation not required. Dialog animated away.
* @hide
*/
- public static final int DISMISSED_REASON_CONFIRM_NOT_REQUIRED = 4;
+ public static final int DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED = 4;
/**
* Error message shown on SystemUI. When BiometricService receives this, the UI is already
@@ -134,6 +131,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
*/
public static final int DISMISSED_REASON_SERVER_REQUESTED = 6;
+ /**
+ * @hide
+ */
+ public static final int DISMISSED_REASON_CREDENTIAL_CONFIRMED = 7;
+
private static class ButtonInfo {
Executor executor;
DialogInterface.OnClickListener listener;
@@ -203,30 +205,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
/**
- * Optional: Set the text for the positive button. If not set, the positive button
- * will not show.
- * @param text
- * @return
- * @hide
- */
- @NonNull public Builder setPositiveButton(@NonNull CharSequence text,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull DialogInterface.OnClickListener listener) {
- if (TextUtils.isEmpty(text)) {
- throw new IllegalArgumentException("Text must be set and non-empty");
- }
- if (executor == null) {
- throw new IllegalArgumentException("Executor must not be null");
- }
- if (listener == null) {
- throw new IllegalArgumentException("Listener must not be null");
- }
- mBundle.putCharSequence(KEY_POSITIVE_TEXT, text);
- mPositiveButtonInfo = new ButtonInfo(executor, listener);
- return this;
- }
-
- /**
* Required: Set the text for the negative button. This would typically be used as a
* "Cancel" button, but may be also used to show an alternative method for authentication,
* such as screen that asks for a backup password.
@@ -298,17 +276,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
/**
- * TODO(123378871): Remove when moved.
- * @return
- * @hide
- */
- @RequiresPermission(USE_BIOMETRIC_INTERNAL)
- @NonNull public Builder setFromConfirmDeviceCredential() {
- mBundle.putBoolean(KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, true);
- return this;
- }
-
- /**
* Creates a {@link BiometricPrompt}.
* @return a {@link BiometricPrompt}
* @throws IllegalArgumentException if any of the required fields are not set.
@@ -317,15 +284,19 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
final CharSequence title = mBundle.getCharSequence(KEY_TITLE);
final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
final boolean useDefaultTitle = mBundle.getBoolean(KEY_USE_DEFAULT_TITLE);
- final boolean enableFallback = mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL);
+ final boolean allowCredential = mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL);
+ final Object authenticatorsAllowed = mBundle.get(KEY_AUTHENTICATORS_ALLOWED);
if (TextUtils.isEmpty(title) && !useDefaultTitle) {
throw new IllegalArgumentException("Title must be set and non-empty");
- } else if (TextUtils.isEmpty(negative) && !enableFallback) {
+ } else if (TextUtils.isEmpty(negative) && !allowCredential) {
throw new IllegalArgumentException("Negative text must be set and non-empty");
- } else if (!TextUtils.isEmpty(negative) && enableFallback) {
+ } else if (!TextUtils.isEmpty(negative) && allowCredential) {
throw new IllegalArgumentException("Can't have both negative button behavior"
+ " and device credential enabled");
+ } else if (authenticatorsAllowed != null && allowCredential) {
+ throw new IllegalArgumentException("setAuthenticatorsAllowed and"
+ + " setDeviceCredentialAllowed should not be used simultaneously");
}
return new BiometricPrompt(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo);
}
@@ -384,7 +355,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
@Override
public void onDialogDismissed(int reason) throws RemoteException {
// Check the reason and invoke OnClickListener(s) if necessary
- if (reason == DISMISSED_REASON_CONFIRMED) {
+ if (reason == DISMISSED_REASON_BIOMETRIC_CONFIRMED) {
mPositiveButtonInfo.executor.execute(() -> {
mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE);
});
@@ -532,8 +503,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
public void authenticateUser(@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback,
- int userId,
- IBiometricConfirmDeviceCredentialCallback confirmDeviceCredentialCallback) {
+ int userId) {
if (cancel == null) {
throw new IllegalArgumentException("Must supply a cancellation signal");
}
@@ -543,8 +513,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
if (callback == null) {
throw new IllegalArgumentException("Must supply a callback");
}
- authenticateInternal(null /* crypto */, cancel, executor, callback, userId,
- confirmDeviceCredentialCallback);
+ authenticateInternal(null /* crypto */, cancel, executor, callback, userId);
}
/**
@@ -595,8 +564,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
if (mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL)) {
throw new IllegalArgumentException("Device credential not supported with crypto");
}
- authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId(),
- null /* confirmDeviceCredentialCallback */);
+ authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId());
}
/**
@@ -638,8 +606,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
if (callback == null) {
throw new IllegalArgumentException("Must supply a callback");
}
- authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId(),
- null /* confirmDeviceCredentialCallback */);
+ authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId());
}
private void cancelAuthentication() {
@@ -656,8 +623,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback,
- int userId,
- IBiometricConfirmDeviceCredentialCallback confirmDeviceCredentialCallback) {
+ int userId) {
try {
if (cancel.isCanceled()) {
Log.w(TAG, "Authentication already canceled");
@@ -672,7 +638,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
final long sessionId = crypto != null ? crypto.getOpId() : 0;
if (BiometricManager.hasBiometrics(mContext)) {
mService.authenticate(mToken, sessionId, userId, mBiometricServiceReceiver,
- mContext.getOpPackageName(), mBundle, confirmDeviceCredentialCallback);
+ mContext.getOpPackageName(), mBundle);
} else {
mExecutor.execute(() -> {
callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT,
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index f0a0b2f0235f..6a3bf38a97e1 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -17,7 +17,6 @@
package android.hardware.biometrics;
import android.os.Bundle;
-import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -31,10 +30,8 @@ import android.hardware.biometrics.IBiometricServiceReceiver;
interface IBiometricService {
// Requests authentication. The service choose the appropriate biometric to use, and show
// the corresponding BiometricDialog.
- // TODO(b/123378871): Remove callback when moved.
void authenticate(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle,
- IBiometricConfirmDeviceCredentialCallback callback);
+ IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
@@ -57,16 +54,4 @@ interface IBiometricService {
// Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
void resetLockout(in byte [] token);
-
- // TODO(b/123378871): Remove when moved.
- // CDCA needs to send results to BiometricService if it was invoked using BiometricPrompt's
- // setAllowDeviceCredential method, since there's no way for us to intercept onActivityResult.
- // CDCA is launched from BiometricService (startActivityAsUser) instead of *ForResult.
- void onConfirmDeviceCredentialSuccess();
- // TODO(b/123378871): Remove when moved.
- void onConfirmDeviceCredentialError(int error, String message);
- // TODO(b/123378871): Remove when moved.
- // When ConfirmLock* is invoked from BiometricPrompt, it needs to register a callback so that
- // it can receive the cancellation signal.
- void registerCancellationCallback(IBiometricConfirmDeviceCredentialCallback callback);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
index ca6114e4d842..66b6e896fc13 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
@@ -38,4 +38,6 @@ oneway interface IBiometricServiceReceiverInternal {
void onDialogDismissed(int reason);
// Notifies that the user has pressed the "try again" button on SystemUI
void onTryAgainPressed();
+ // Notifies that the user has pressed the "use password" button on SystemUI
+ void onDeviceCredentialPressed();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3c26df3c560b..e4e8bf791d47 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7908,16 +7908,6 @@ public final class Settings {
public static final String NOTIFICATION_BADGING = "notification_badging";
/**
- * Whether the notification bubbles are globally enabled
- * The value is boolean (1 or 0).
- * @hide
- * @deprecated use {@link Global#NOTIFICATION_BUBBLES} instead.
- */
- @TestApi
- @Deprecated
- public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
-
- /**
* Whether notifications are dismissed by a right-to-left swipe (instead of a left-to-right
* swipe).
*
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 70dfef574ca5..dfc5c8217fda 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -170,13 +170,23 @@ public abstract class RecognitionService extends Service {
* Checks whether the caller has sufficient permissions
*
* @param listener to send the error message to in case of error
+ * @param forDataDelivery If the permission check is for delivering the sensitive data.
* @return {@code true} if the caller has enough permissions, {@code false} otherwise
*/
- private boolean checkPermissions(IRecognitionListener listener) {
+ private boolean checkPermissions(IRecognitionListener listener, boolean forDataDelivery) {
if (DBG) Log.d(TAG, "checkPermissions");
- if (PermissionChecker.checkCallingOrSelfPermission(this,
- android.Manifest.permission.RECORD_AUDIO) == PermissionChecker.PERMISSION_GRANTED) {
- return true;
+ if (forDataDelivery) {
+ if (PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(this,
+ android.Manifest.permission.RECORD_AUDIO, null /*message*/)
+ == PermissionChecker.PERMISSION_GRANTED) {
+ return true;
+ }
+ } else {
+ if (PermissionChecker.checkCallingOrSelfPermissionForPreflight(this,
+ android.Manifest.permission.RECORD_AUDIO)
+ == PermissionChecker.PERMISSION_GRANTED) {
+ return true;
+ }
}
try {
Log.e(TAG, "call for recognition service without RECORD_AUDIO permissions");
@@ -342,7 +352,7 @@ public abstract class RecognitionService extends Service {
public void startListening(Intent recognizerIntent, IRecognitionListener listener) {
if (DBG) Log.d(TAG, "startListening called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener)) {
+ if (service != null && service.checkPermissions(listener, true /*forDataDelivery*/)) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_START_LISTENING, service.new StartListeningArgs(
recognizerIntent, listener, Binder.getCallingUid())));
@@ -353,7 +363,7 @@ public abstract class RecognitionService extends Service {
public void stopListening(IRecognitionListener listener) {
if (DBG) Log.d(TAG, "stopListening called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener)) {
+ if (service != null && service.checkPermissions(listener, false /*forDataDelivery*/)) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_STOP_LISTENING, listener));
}
@@ -363,7 +373,7 @@ public abstract class RecognitionService extends Service {
public void cancel(IRecognitionListener listener) {
if (DBG) Log.d(TAG, "cancel called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener)) {
+ if (service != null && service.checkPermissions(listener, false /*forDataDelivery*/)) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_CANCEL, listener));
}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index d9fa9f24f1ae..bb10ef10d79e 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -259,23 +259,38 @@ public final class AccessibilityInteractionClient
}
/**
- * Gets the info for all windows.
+ * Gets the info for all windows of the default display.
*
* @param connectionId The id of a connection for interacting with the system.
* @return The {@link AccessibilityWindowInfo} list.
*/
public List<AccessibilityWindowInfo> getWindows(int connectionId) {
+ final SparseArray<List<AccessibilityWindowInfo>> windows =
+ getWindowsOnAllDisplays(connectionId);
+ if (windows.size() > 0) {
+ return windows.valueAt(Display.DEFAULT_DISPLAY);
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * Gets the info for all windows of all displays.
+ *
+ * @param connectionId The id of a connection for interacting with the system.
+ * @return The SparseArray of {@link AccessibilityWindowInfo} list.
+ * The key of SparseArray is display ID.
+ */
+ public SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays(int connectionId) {
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
- SparseArray<List<AccessibilityWindowInfo>> allWindows =
+ SparseArray<List<AccessibilityWindowInfo>> windows =
sAccessibilityCache.getWindowsOnAllDisplays();
- List<AccessibilityWindowInfo> windows;
- if (allWindows != null) {
+ if (windows != null) {
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache hit");
}
- return allWindows.valueAt(Display.DEFAULT_DISPLAY);
+ return windows;
}
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache miss");
@@ -287,9 +302,7 @@ public final class AccessibilityInteractionClient
Binder.restoreCallingIdentity(identityToken);
}
if (windows != null) {
- allWindows = new SparseArray<>();
- allWindows.put(Display.DEFAULT_DISPLAY, windows);
- sAccessibilityCache.setWindowsOnAllDisplays(allWindows);
+ sAccessibilityCache.setWindowsOnAllDisplays(windows);
return windows;
}
} else {
@@ -298,9 +311,11 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while calling remote getWindows", re);
+ Log.e(LOG_TAG, "Error while calling remote getWindowsOnAllDisplays", re);
}
- return Collections.emptyList();
+
+ final SparseArray<List<AccessibilityWindowInfo>> emptyWindows = new SparseArray<>();
+ return emptyWindows;
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl b/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl
index fdb25fb1ee61..c36c4aa5b00a 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl
@@ -17,3 +17,4 @@
package android.view.accessibility;
parcelable AccessibilityWindowInfo;
+parcelable AccessibilityWindowInfo.WindowListSparseArray;
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 6a3af3478449..5fa8a6e0e06b 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -26,9 +26,12 @@ import android.os.Parcelable;
import android.text.TextUtils;
import android.util.LongArray;
import android.util.Pools.SynchronizedPool;
+import android.util.SparseArray;
import android.view.Display;
import android.view.accessibility.AccessibilityEvent.WindowsChangeTypes;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
@@ -797,4 +800,49 @@ public final class AccessibilityWindowInfo implements Parcelable {
return new AccessibilityWindowInfo[size];
}
};
+
+ /**
+ * Transfers a sparsearray with lists having {@link AccessibilityWindowInfo}s across an IPC.
+ * The key of this sparsearray is display Id.
+ *
+ * @hide
+ */
+ public static final class WindowListSparseArray
+ extends SparseArray<List<AccessibilityWindowInfo>> implements Parcelable {
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ final int count = size();
+ dest.writeInt(count);
+ for (int i = 0; i < count; i++) {
+ dest.writeParcelableList(valueAt(i), 0);
+ dest.writeInt(keyAt(i));
+ }
+ }
+
+ public static final Parcelable.Creator<WindowListSparseArray> CREATOR =
+ new Parcelable.Creator<WindowListSparseArray>() {
+ public WindowListSparseArray createFromParcel(
+ Parcel source) {
+ final WindowListSparseArray array = new WindowListSparseArray();
+ final ClassLoader loader = array.getClass().getClassLoader();
+ final int count = source.readInt();
+ for (int i = 0; i < count; i++) {
+ List<AccessibilityWindowInfo> windows = new ArrayList<>();
+ source.readParcelableList(windows, loader);
+ array.put(source.readInt(), windows);
+ }
+ return array;
+ }
+
+ public WindowListSparseArray[] newArray(int size) {
+ return new WindowListSparseArray[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f997d6878a9c..31f50555af1d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -23,6 +23,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_C
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
import android.R;
+import android.annotation.CallSuper;
import android.annotation.CheckResult;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
@@ -10446,10 +10447,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* This method is called when the selection has changed, in case any
* subclasses would like to know.
+ * </p>
+ * <p class="note"><strong>Note:</strong> Always call the super implementation, which informs
+ * the accessibility subsystem about the selection change.
+ * </p>
*
* @param selStart The new selection start location.
* @param selEnd The new selection end location.
*/
+ @CallSuper
protected void onSelectionChanged(int selStart, int selEnd) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 5142d3cd2b99..033e9b2c6eba 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -239,6 +239,13 @@ public final class SystemUiDeviceConfigFlags {
public static final String ASSIST_TRANSCRIPTION_MIN_DURATION =
"assist_transcription_min_duration";
+ /**
+ * (boolean) Whether or not to enable an extra section in the notification shade which
+ * filters for "people" related messages.
+ */
+ public static final String NOTIFICATIONS_USE_PEOPLE_FILTERING =
+ "notifications_use_people_filtering";
+
// Flags related to brightline falsing
/**
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 103c79d22530..fd3cd42b07a1 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -203,13 +203,15 @@ public class RuntimeInit {
public static void preForkInit() {
if (DEBUG) Slog.d(TAG, "Entered preForkInit.");
RuntimeInit.enableDdms();
+ // TODO(b/142019040#comment13): Decide whether to load the default instance eagerly, i.e.
+ // MimeMap.setDefault(DefaultMimeMapFactory.create());
/*
* Replace libcore's minimal default mapping between MIME types and file
* extensions with a mapping that's suitable for Android. Android's mapping
* contains many more entries that are derived from IANA registrations but
* with several customizations (extensions, overrides).
*/
- MimeMap.setDefault(DefaultMimeMapFactory.create());
+ MimeMap.setDefaultSupplier(DefaultMimeMapFactory::create);
}
@UnsupportedAppUsage
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 9441825a1ed6..c8ba52a63151 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -151,17 +151,17 @@ oneway interface IStatusBar
void showShutdownUi(boolean isReboot, String reason);
- // Used to show the dialog when BiometricService starts authentication
- void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
- boolean requireConfirmation, int userId, String opPackageName);
- // Used to hide the dialog when a biometric is authenticated
+ // Used to show the authentication dialog (Biometrics, Device Credential)
+ void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName);
+ // Used to notify the authentication dialog that a biometric has been authenticated or rejected
void onBiometricAuthenticated(boolean authenticated, String failureReason);
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
void onBiometricHelp(String message);
// Used to set a message - the dialog will dismiss after a certain amount of time
- void onBiometricError(String error);
- // Used to hide the biometric dialog when the AuthenticationClient is stopped
- void hideBiometricDialog();
+ void onBiometricError(int errorCode, String error);
+ // Used to hide the authentication dialog, e.g. when the application cancels authentication
+ void hideAuthenticationDialog();
/**
* Notifies System UI that the display is ready to show system decorations.
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 4c3a177a013b..a845b587c49f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -99,15 +99,15 @@ interface IStatusBarService
void showPinningEnterExitToast(boolean entering);
void showPinningEscapeToast();
- // Used to show the dialog when BiometricService starts authentication
- void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
- boolean requireConfirmation, int userId, String opPackageName);
- // Used to hide the dialog when a biometric is authenticated
+ // Used to show the authentication dialog (Biometrics, Device Credential)
+ void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName);
+ // Used to notify the authentication dialog that a biometric has been authenticated or rejected
void onBiometricAuthenticated(boolean authenticated, String failureReason);
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
void onBiometricHelp(String message);
// Used to set a message - the dialog will dismiss after a certain amount of time
- void onBiometricError(String error);
- // Used to hide the biometric dialog when the AuthenticationClient is stopped
- void hideBiometricDialog();
+ void onBiometricError(int errorCode, String error);
+ // Used to hide the authentication dialog, e.g. when the application cancels authentication
+ void hideAuthenticationDialog();
}
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 8aa6f860aa61..3cefeea68406 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -191,9 +191,21 @@ jint android_os_Process_getGidForName(JNIEnv* env, jobject clazz, jstring name)
return -1;
}
+static bool verifyGroup(JNIEnv* env, int grp)
+{
+ if (grp < SP_DEFAULT || grp >= SP_CNT) {
+ signalExceptionForError(env, EINVAL, grp);
+ return false;
+ }
+ return true;
+}
+
void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int tid, jint grp)
{
ALOGV("%s tid=%d grp=%" PRId32, __func__, tid, grp);
+ if (!verifyGroup(env, grp)) {
+ return;
+ }
SchedPolicy sp = (SchedPolicy) grp;
int res = set_sched_policy(tid, sp);
if (res != NO_ERROR) {
@@ -204,6 +216,9 @@ void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int tid, jint
void android_os_Process_setThreadGroupAndCpuset(JNIEnv* env, jobject clazz, int tid, jint grp)
{
ALOGV("%s tid=%d grp=%" PRId32, __func__, tid, grp);
+ if (!verifyGroup(env, grp)) {
+ return;
+ }
SchedPolicy sp = (SchedPolicy) grp;
int res = set_sched_policy(tid, sp);
@@ -234,6 +249,9 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin
grp = SP_FOREGROUND;
isDefault = true;
}
+ if (!verifyGroup(env, grp)) {
+ return;
+ }
SchedPolicy sp = (SchedPolicy) grp;
if (kDebugPolicy) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7a0d0cbe6095..7ce370ff52be 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -794,6 +794,18 @@
android:permissionFlags="hardRestricted"
android:protectionLevel="dangerous" />
+ <!-- @SystemApi @TestApi Allows an application to forward cell broadcast messages to the cell
+ broadcast module. This is required in order to bind to the cell broadcast service, and
+ ensures that only the system can forward messages to it.
+
+ <p>Protection level: signature|privileged
+
+ @hide -->
+ <permission android:name="android.permission.BIND_CELL_BROADCAST_SERVICE"
+ android:label="@string/permlab_bindCellBroadcastService"
+ android:description="@string/permdesc_bindCellBroadcastService"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast
messages and to register a content observer to get notifications when
a cell broadcast has been received and added to the database. For
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e3337b7f9ae5..7eca699d9eca 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -433,6 +433,10 @@
-->
</string-array>
+ <!-- Package name for the default CellBroadcastService module [DO NOT TRANSLATE] -->
+ <string name="cellbroadcast_default_package" translatable="false">com.android.cellbroadcastreceiver
+ </string>
+
<!-- If the mobile hotspot feature requires provisioning, a package name and class name
can be provided to launch a supported application that provisions the devices.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b53a399f0c8a..4e327082047b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -890,6 +890,16 @@
messages. This means the app could monitor or delete messages sent to your
device without showing them to you.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR LIMIT=NONE] -->
+ <string name="permlab_bindCellBroadcastService">Forward cell broadcast messages</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_bindCellBroadcastService">Allows the app to bind to the
+ cell broadcast module in order to forward cell broadcast messages
+ as they are received. Cell broadcast alerts are delivered in some
+ locations to warn you of emergency situations. Malicious apps may
+ interfere with the performance or operation of your device when an
+ emergency cell broadcast is received.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readCellBroadcasts">read cell broadcast messages</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e2f2b2c64b77..a8b534061aeb 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -747,6 +747,7 @@
<java-symbol type="string" name="config_default_dns_server" />
<java-symbol type="string" name="config_ethernet_iface_regex" />
<java-symbol type="array" name="config_ethernet_interfaces" />
+ <java-symbol type="string" name="cellbroadcast_default_package" />
<java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
<java-symbol type="string" name="config_mms_user_agent" />
<java-symbol type="string" name="config_mms_user_agent_profile_url" />
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 682416c58c72..3586216ad421 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -23,8 +23,6 @@ import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
-import java.util.List;
-
/**
* Stub implementation of IAccessibilityServiceConnection so each test doesn't need to implement
* all of the methods
@@ -73,7 +71,7 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
return null;
}
- public List<AccessibilityWindowInfo> getWindows() {
+ public AccessibilityWindowInfo.WindowListSparseArray getWindows() {
return null;
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 89523d6bbefb..51136b993e57 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -127,6 +127,7 @@ applications that come with the platform
<permission name="android.permission.ACCESS_IMS_CALL_SERVICE"/>
<permission name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"/>
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
+ <permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
<permission name="android.permission.BIND_IMS_SERVICE"/>
<permission name="android.permission.BIND_TELEPHONY_DATA_SERVICE"/>
<permission name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 68480ced0692..f3d68755a513 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -940,9 +940,8 @@ public class LocationManager {
@Nullable LocationRequest locationRequest,
@NonNull LocationListener listener,
@Nullable Looper looper) {
- requestLocationUpdates(locationRequest,
- new LocationListenerTransport(looper == null ? new Handler() : new Handler(looper),
- listener));
+ Handler handler = looper == null ? new Handler() : new Handler(looper);
+ requestLocationUpdates(locationRequest, new HandlerExecutor(handler), listener);
}
/**
@@ -969,7 +968,31 @@ public class LocationManager {
@Nullable LocationRequest locationRequest,
@NonNull @CallbackExecutor Executor executor,
@NonNull LocationListener listener) {
- requestLocationUpdates(locationRequest, new LocationListenerTransport(executor, listener));
+ synchronized (mListeners) {
+ LocationListenerTransport transport = mListeners.get(listener);
+ if (transport != null) {
+ transport.unregister();
+ } else {
+ transport = new LocationListenerTransport(listener);
+ mListeners.put(listener, transport);
+ }
+ transport.register(executor);
+
+ boolean registered = false;
+ try {
+ mService.requestLocationUpdates(locationRequest, transport, null,
+ mContext.getPackageName());
+ registered = true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ if (!registered) {
+ // allow gc after exception
+ transport.unregister();
+ mListeners.remove(listener);
+ }
+ }
+ }
}
/**
@@ -1009,23 +1032,6 @@ public class LocationManager {
}
}
- private void requestLocationUpdates(@Nullable LocationRequest request,
- @NonNull LocationListenerTransport transport) {
- synchronized (mListeners) {
- LocationListenerTransport oldTransport = mListeners.put(transport.getKey(), transport);
- if (oldTransport != null) {
- oldTransport.unregisterListener();
- }
-
- try {
- mService.requestLocationUpdates(request, transport, null,
- mContext.getPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
/**
* Set the last known location with a new location.
*
@@ -1079,7 +1085,7 @@ public class LocationManager {
if (transport == null) {
return;
}
- transport.unregisterListener();
+ transport.unregister();
try {
mService.removeUpdates(transport, null, mContext.getPackageName());
@@ -2237,49 +2243,45 @@ public class LocationManager {
private class LocationListenerTransport extends ILocationListener.Stub {
- private final Executor mExecutor;
- @Nullable private volatile LocationListener mListener;
+ private final LocationListener mListener;
+ @Nullable private volatile Executor mExecutor = null;
- private LocationListenerTransport(@NonNull Handler handler,
- @NonNull LocationListener listener) {
- Preconditions.checkArgument(handler != null, "invalid null handler");
+ private LocationListenerTransport(@NonNull LocationListener listener) {
Preconditions.checkArgument(listener != null, "invalid null listener");
-
- mExecutor = new HandlerExecutor(handler);
mListener = listener;
}
- private LocationListenerTransport(@NonNull Executor executor,
- @NonNull LocationListener listener) {
- Preconditions.checkArgument(executor != null, "invalid null executor");
- Preconditions.checkArgument(listener != null, "invalid null listener");
-
- mExecutor = executor;
- mListener = listener;
+ public LocationListener getKey() {
+ return mListener;
}
- private LocationListener getKey() {
- return mListener;
+ public void register(@NonNull Executor executor) {
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ mExecutor = executor;
}
- private void unregisterListener() {
- mListener = null;
+ public void unregister() {
+ mExecutor = null;
}
@Override
public void onLocationChanged(Location location) {
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ return;
+ }
+
try {
- mExecutor.execute(() -> {
+ currentExecutor.execute(() -> {
try {
- LocationListener listener = mListener;
- if (listener == null) {
+ if (currentExecutor != mExecutor) {
return;
}
// we may be under the binder identity if a direct executor is used
long identity = Binder.clearCallingIdentity();
try {
- listener.onLocationChanged(location);
+ mListener.onLocationChanged(location);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2295,18 +2297,22 @@ public class LocationManager {
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ return;
+ }
+
try {
- mExecutor.execute(() -> {
+ currentExecutor.execute(() -> {
try {
- LocationListener listener = mListener;
- if (listener == null) {
+ if (currentExecutor != mExecutor) {
return;
}
// we may be under the binder identity if a direct executor is used
long identity = Binder.clearCallingIdentity();
try {
- listener.onStatusChanged(provider, status, extras);
+ mListener.onStatusChanged(provider, status, extras);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2322,18 +2328,22 @@ public class LocationManager {
@Override
public void onProviderEnabled(String provider) {
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ return;
+ }
+
try {
- mExecutor.execute(() -> {
+ currentExecutor.execute(() -> {
try {
- LocationListener listener = mListener;
- if (listener == null) {
+ if (currentExecutor != mExecutor) {
return;
}
// we may be under the binder identity if a direct executor is used
long identity = Binder.clearCallingIdentity();
try {
- listener.onProviderEnabled(provider);
+ mListener.onProviderEnabled(provider);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2349,18 +2359,22 @@ public class LocationManager {
@Override
public void onProviderDisabled(String provider) {
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ return;
+ }
+
try {
- mExecutor.execute(() -> {
+ currentExecutor.execute(() -> {
try {
- LocationListener listener = mListener;
- if (listener == null) {
+ if (currentExecutor != mExecutor) {
return;
}
// we may be under the binder identity if a direct executor is used
long identity = Binder.clearCallingIdentity();
try {
- listener.onProviderDisabled(provider);
+ mListener.onProviderDisabled(provider);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index a107dd793551..c205bb49228c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -15,11 +15,11 @@
*/
package com.android.systemui.car;
-import android.content.Context;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -34,8 +34,8 @@ import javax.inject.Singleton;
public class CarNotificationEntryManager extends NotificationEntryManager {
@Inject
- public CarNotificationEntryManager(Context context) {
- super(context);
+ public CarNotificationEntryManager(NotificationData notificationData) {
+ super(notificationData);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
index ea39317fb045..81ca9eaf8e36 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
@@ -111,8 +111,9 @@ public class RecentLocationAccesses {
for (int op : LOCATION_OPS) {
final String permission = AppOpsManager.opToPermission(op);
final int permissionFlags = pm.getPermissionFlags(permission, packageName, user);
- if (PermissionChecker.checkPermission(mContext, permission, -1, uid, packageName)
- == PermissionChecker.PERMISSION_GRANTED) {
+ if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
+ PermissionChecker.PID_UNKNOWN, uid, packageName)
+ == PermissionChecker.PERMISSION_GRANTED) {
if ((permissionFlags
& PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) == 0) {
showApp = false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
index 60c9984e5ed4..104cc8f9841c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
@@ -110,9 +110,9 @@ public class RecentLocationApps {
final String permission = AppOpsManager.opToPermission(op);
final int permissionFlags = pm.getPermissionFlags(permission, packageName,
user);
- if (PermissionChecker.checkPermission(mContext, permission, -1, uid,
- packageName)
- == PermissionChecker.PERMISSION_GRANTED) {
+ if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
+ PermissionChecker.PID_UNKNOWN, uid, packageName)
+ == PermissionChecker.PERMISSION_GRANTED) {
if ((permissionFlags
& PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)
== 0) {
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 8c2e43170cc2..b8372adfa074 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -114,7 +114,6 @@ public class SecureSettings {
Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED,
Settings.Secure.VR_DISPLAY_MODE,
Settings.Secure.NOTIFICATION_BADGING,
- Settings.Secure.NOTIFICATION_BUBBLES,
Settings.Secure.NOTIFICATION_DISMISS_RTL,
Settings.Secure.QS_AUTO_ADDED_TILES,
Settings.Secure.SCREENSAVER_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index f160edc6e446..976f3365ab98 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -157,7 +157,6 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.VR_DISPLAY_MODE, new DiscreteValueValidator(new String[] {"0", "1"}));
VALIDATORS.put(Secure.NOTIFICATION_BADGING, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Secure.NOTIFICATION_BUBBLES, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.NOTIFICATION_DISMISS_RTL, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.QS_AUTO_ADDED_TILES, TILE_LIST_VALIDATOR);
VALIDATORS.put(Secure.SCREENSAVER_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 720266a72423..16c96e67c85a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -4385,8 +4385,7 @@ public class SettingsProvider extends ContentProvider {
if (currentVersion == 182) {
// Remove secure bubble settings.
- getSecureSettingsLocked(userId).deleteSettingLocked(
- Secure.NOTIFICATION_BUBBLES);
+ getSecureSettingsLocked(userId).deleteSettingLocked("notification_bubbles");
// Add global bubble settings.
getGlobalSettingsLocked().insertSettingLocked(Global.NOTIFICATION_BUBBLES,
diff --git a/packages/SystemUI/res/drawable/auth_dialog_lock.xml b/packages/SystemUI/res/drawable/auth_dialog_lock.xml
new file mode 100644
index 000000000000..8146c16e4aaf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/auth_dialog_lock.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright (C) 2019 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32dp"
+ android:height="32dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?android:attr/colorAccent"
+ android:pathData="M12,15m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
+ <path
+ android:fillColor="?android:attr/colorAccent"
+ android:pathData="M18,8h-1.5V5.5C16.5,3.01 14.49,1 12,1S7.5,3.01 7.5,5.5V8H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10C20,8.9 19.1,8 18,8zM9.5,5.5C9.5,4.12 10.62,3 12,3c1.38,0 2.5,1.12 2.5,2.5V8h-5V5.5zM18,20H6V10h1.5h9H18V20z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
new file mode 100644
index 000000000000..c3fa39e5a87f
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
@@ -0,0 +1,113 @@
+<!--
+ ~ Copyright (C) 2019 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.
+ -->
+
+<com.android.systemui.biometrics.AuthCredentialPatternView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:elevation="@dimen/biometric_dialog_elevation">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <ImageView
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:background="@drawable/auth_dialog_lock"/>
+
+ <TextView
+ android:id="@+id/title"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="12dp"
+ android:textSize="20sp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:gravity="center"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/colorError"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPattern"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="40dp"
+ android:layout_marginRight="40dp"
+ android:layout_gravity="center"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ style="@style/LockPatternStyleBiometricPrompt"/>
+
+ </LinearLayout>
+
+</com.android.systemui.biometrics.AuthCredentialPatternView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_container_view.xml b/packages/SystemUI/res/layout/auth_container_view.xml
index 23199aacc093..3db01a4e7f3a 100644
--- a/packages/SystemUI/res/layout/auth_container_view.xml
+++ b/packages/SystemUI/res/layout/auth_container_view.xml
@@ -34,7 +34,7 @@
android:elevation="@dimen/biometric_dialog_elevation"/>
<ScrollView
- android:id="@+id/scrollview"
+ android:id="@+id/biometric_scrollview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
new file mode 100644
index 000000000000..4aed0333e9ca
--- /dev/null
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -0,0 +1,101 @@
+<!--
+ ~ Copyright (C) 2019 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.
+ -->
+
+<com.android.systemui.biometrics.AuthCredentialPasswordView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+ android:elevation="@dimen/biometric_dialog_elevation">
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <ImageView
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:background="@drawable/auth_dialog_lock"/>
+
+ <TextView
+ android:id="@+id/title"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="12dp"
+ android:textSize="20sp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:gravity="center"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/colorError"/>
+
+ <EditText
+ android:id="@+id/lockPassword"
+ android:layout_marginBottom="20dp"
+ android:layout_marginLeft="100dp"
+ android:layout_marginRight="100dp"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:inputType="textPassword"
+ android:maxLength="500"
+ android:textSize="16sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:imeOptions="flagForceAscii"
+ style="@style/LockPatternStyleBiometricPrompt"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="5"/>
+
+</com.android.systemui.biometrics.AuthCredentialPasswordView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
new file mode 100644
index 000000000000..c9edcd606277
--- /dev/null
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -0,0 +1,97 @@
+<!--
+ ~ Copyright (C) 2019 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.
+ -->
+
+<com.android.systemui.biometrics.AuthCredentialPatternView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+ android:elevation="@dimen/biometric_dialog_elevation">
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <ImageView
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:background="@drawable/auth_dialog_lock"/>
+
+ <TextView
+ android:id="@+id/title"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="12dp"
+ android:textSize="20sp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:gravity="center"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="3"/>
+
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/colorError"/>
+
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPattern"
+ android:layout_marginBottom="20dp"
+ android:layout_marginLeft="40dp"
+ android:layout_marginRight="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ style="@style/LockPatternStyleBiometricPrompt"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+</com.android.systemui.biometrics.AuthCredentialPatternView> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1079206c81ce..d722d618e416 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1009,10 +1009,15 @@
<!-- Biometric Dialog values -->
<dimen name="biometric_dialog_biometric_icon_size">64dp</dimen>
<dimen name="biometric_dialog_corner_size">4dp</dimen>
+ <!-- Y translation when showing/dismissing the dialog-->
<dimen name="biometric_dialog_animation_translation_offset">350dp</dimen>
<dimen name="biometric_dialog_border_padding">4dp</dimen>
<dimen name="biometric_dialog_elevation">1dp</dimen>
<dimen name="biometric_dialog_icon_padding">16dp</dimen>
+ <!-- Y translation for biometric contents when transitioning to device credential UI -->
+ <dimen name="biometric_dialog_medium_to_large_translation_offset">100dp</dimen>
+ <!-- Y translation for credential contents when animating in -->
+ <dimen name="biometric_dialog_credential_translation_offset">60dp</dimen>
<!-- Wireless Charging Animation values -->
<dimen name="wireless_charging_dots_radius_start">0dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c5547800360d..8335c116c95f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -311,6 +311,21 @@
<!-- Talkback string when a biometric is authenticated [CHAR LIMIT=NONE] -->
<string name="biometric_dialog_authenticated">Authenticated</string>
+ <!-- Button text shown on BiometricPrompt giving the user the option to use an alternate form of authentication (Pin) [CHAR LIMIT=30] -->
+ <string name="biometric_dialog_use_pin">Use PIN</string>
+ <!-- Button text shown on BiometricPrompt giving the user the option to use an alternate form of authentication (Pattern) [CHAR LIMIT=30] -->
+ <string name="biometric_dialog_use_pattern">Use pattern</string>
+ <!-- Button text shown on BiometricPrompt giving the user the option to use an alternate form of authentication (Pass) [CHAR LIMIT=30] -->
+ <string name="biometric_dialog_use_password">Use password</string>
+ <!-- Error string shown when the user enters an incorrect PIN [CHAR LIMIT=40]-->
+ <string name="biometric_dialog_wrong_pin">Wrong PIN</string>
+ <!-- Error string shown when the user enters an incorrect pattern [CHAR LIMIT=40]-->
+ <string name="biometric_dialog_wrong_pattern">Wrong pattern</string>
+ <!-- Error string shown when the user enters an incorrect password [CHAR LIMIT=40]-->
+ <string name="biometric_dialog_wrong_password">Wrong password</string>
+ <!-- Error string shown when the user enters too many incorrect attempts [CHAR LIMIT=120]-->
+ <string name="biometric_dialog_credential_too_many_attempts">Too many incorrect attempts.\nTry again in <xliff:g id="number">%d</xliff:g> seconds.</string>
+
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
<!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6374191c4d7b..96fbcbba6e8b 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -314,6 +314,12 @@
<item name="*android:errorColor">?android:attr/colorError</item>
</style>
+ <style name="LockPatternStyleBiometricPrompt">
+ <item name="*android:regularColor">?android:attr/colorForeground</item>
+ <item name="*android:successColor">?android:attr/colorForeground</item>
+ <item name="*android:errorColor">?android:attr/colorError</item>
+ </style>
+
<style name="qs_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
<item name="lightIconTheme">@style/QSIconTheme</item>
<item name="darkIconTheme">@style/QSIconTheme</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index a00a8e7aa4e9..b9fe9334d14c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -76,7 +76,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
// How much you need to drag the bouncer to trigger an auth retry (in dps.)
private static final float MIN_DRAG_SIZE = 10;
// How much to scale the default slop by, to avoid accidental drags.
- private static final float SLOP_SCALE = 2f;
+ private static final float SLOP_SCALE = 4f;
private KeyguardSecurityModel mSecurityModel;
private LockPatternUtils mLockPatternUtils;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 73bbce9c5b35..d20cd72f0712 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -16,8 +16,6 @@
package com.android.systemui.biometrics;
-import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -25,6 +23,7 @@ import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Bundle;
@@ -34,7 +33,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.Button;
import android.widget.ImageView;
@@ -42,10 +41,13 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
* Contains the Biometric views (title, subtitle, icon, buttons, etc) and its controllers.
@@ -97,6 +99,7 @@ public abstract class AuthBiometricView extends LinearLayout {
int ACTION_BUTTON_NEGATIVE = 3;
int ACTION_BUTTON_TRY_AGAIN = 4;
int ACTION_ERROR = 5;
+ int ACTION_USE_DEVICE_CREDENTIAL = 6;
/**
* When an action has occurred. The caller will only invoke this when the callback should
@@ -145,6 +148,14 @@ public abstract class AuthBiometricView extends LinearLayout {
public int getDelayAfterError() {
return BiometricPrompt.HIDE_DIALOG_DELAY;
}
+
+ public int getMediumToLargeAnimationDurationMs() {
+ return AuthDialog.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS;
+ }
+
+ public int getAnimateCredentialStartDelayMs() {
+ return AuthDialog.ANIMATE_CREDENTIAL_START_DELAY_MS;
+ }
}
private final Injector mInjector;
@@ -154,8 +165,9 @@ public abstract class AuthBiometricView extends LinearLayout {
private final int mTextColorHint;
private AuthPanelController mPanelController;
- private Bundle mBundle;
+ private Bundle mBiometricPromptBundle;
private boolean mRequireConfirmation;
+ private int mUserId;
@AuthDialog.DialogSize int mSize = AuthDialog.SIZE_UNKNOWN;
private TextView mTitleView;
@@ -212,6 +224,9 @@ public abstract class AuthBiometricView extends LinearLayout {
} else if (mSize == AuthDialog.SIZE_SMALL) {
Log.w(TAG, "Ignoring background click during small dialog");
return;
+ } else if (mSize == AuthDialog.SIZE_LARGE) {
+ Log.w(TAG, "Ignoring background click during large dialog");
+ return;
}
mCallback.onAction(Callback.ACTION_USER_CANCELED);
};
@@ -256,7 +271,7 @@ public abstract class AuthBiometricView extends LinearLayout {
}
public void setBiometricPromptBundle(Bundle bundle) {
- mBundle = bundle;
+ mBiometricPromptBundle = bundle;
}
public void setCallback(Callback callback) {
@@ -267,6 +282,10 @@ public abstract class AuthBiometricView extends LinearLayout {
backgroundView.setOnClickListener(mBackgroundClickListener);
}
+ public void setUserId(int userId) {
+ mUserId = userId;
+ }
+
public void setRequireConfirmation(boolean requireConfirmation) {
mRequireConfirmation = requireConfirmation;
}
@@ -287,7 +306,7 @@ public abstract class AuthBiometricView extends LinearLayout {
final int newHeight = mIconView.getHeight() + 2 * (int) iconPadding;
mPanelController.updateForContentDimensions(mMediumWidth, newHeight,
- false /* animate */);
+ 0 /* animateDurationMs */);
mSize = newSize;
} else if (mSize == AuthDialog.SIZE_SMALL && newSize == AuthDialog.SIZE_MEDIUM) {
@@ -305,10 +324,8 @@ public abstract class AuthBiometricView extends LinearLayout {
// Animate the text
final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(0, 1);
- opacityAnimator.setDuration(AuthDialog.ANIMATE_DURATION_MS);
opacityAnimator.addUpdateListener((animation) -> {
final float opacity = (float) animation.getAnimatedValue();
-
mTitleView.setAlpha(opacity);
mIndicatorView.setAlpha(opacity);
mNegativeButton.setAlpha(opacity);
@@ -324,7 +341,7 @@ public abstract class AuthBiometricView extends LinearLayout {
// Choreograph together
final AnimatorSet as = new AnimatorSet();
- as.setDuration(AuthDialog.ANIMATE_DURATION_MS);
+ as.setDuration(AuthDialog.ANIMATE_SMALL_TO_MEDIUM_DURATION_MS);
as.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -355,11 +372,73 @@ public abstract class AuthBiometricView extends LinearLayout {
as.start();
// Animate the panel
mPanelController.updateForContentDimensions(mMediumWidth, mMediumHeight,
- true /* animate */);
+ AuthDialog.ANIMATE_SMALL_TO_MEDIUM_DURATION_MS);
} else if (newSize == AuthDialog.SIZE_MEDIUM) {
mPanelController.updateForContentDimensions(mMediumWidth, mMediumHeight,
- false /* animate */);
+ 0 /* animateDurationMs */);
mSize = newSize;
+ } else if (newSize == AuthDialog.SIZE_LARGE) {
+ final boolean isManagedProfile = Utils.isManagedProfile(mContext, mUserId);
+
+ // If it's a managed profile, animate the contents and panel down, since the credential
+ // contents will be shown on the same "layer" as the background. If it's not a managed
+ // profile, animate the contents up and expand the panel to full-screen - the credential
+ // contents will be shown on the same "layer" as the panel.
+ final float translationY = isManagedProfile ?
+ -getResources().getDimension(
+ R.dimen.biometric_dialog_animation_translation_offset)
+ : getResources().getDimension(
+ R.dimen.biometric_dialog_medium_to_large_translation_offset);
+ final AuthBiometricView biometricView = this;
+
+ // Translate at full duration
+ final ValueAnimator translationAnimator = ValueAnimator.ofFloat(
+ biometricView.getY(), biometricView.getY() - translationY);
+ translationAnimator.setDuration(mInjector.getMediumToLargeAnimationDurationMs());
+ translationAnimator.addUpdateListener((animation) -> {
+ final float translation = (float) animation.getAnimatedValue();
+ biometricView.setTranslationY(translation);
+ });
+ translationAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (biometricView.getParent() != null) {
+ ((ViewGroup) biometricView.getParent()).removeView(biometricView);
+ }
+ mSize = newSize;
+ }
+ });
+
+ // Opacity to 0 in half duration
+ final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(1, 0);
+ opacityAnimator.setDuration(mInjector.getMediumToLargeAnimationDurationMs() / 2);
+ opacityAnimator.addUpdateListener((animation) -> {
+ final float opacity = (float) animation.getAnimatedValue();
+ biometricView.setAlpha(opacity);
+ });
+
+ if (!isManagedProfile) {
+ mPanelController.setUseFullScreen(true);
+ mPanelController.updateForContentDimensions(
+ mPanelController.getContainerWidth(),
+ mPanelController.getContainerHeight(),
+ mInjector.getMediumToLargeAnimationDurationMs());
+ }
+
+ // Start the animations together
+ AnimatorSet as = new AnimatorSet();
+ List<Animator> animators = new ArrayList<>();
+ animators.add(translationAnimator);
+ animators.add(opacityAnimator);
+ if (isManagedProfile) {
+ animators.add(mPanelController.getTranslationAnimator(translationY));
+ animators.add(mPanelController.getAlphaAnimator(0));
+ }
+ as.playTogether(animators);
+ as.setDuration(isManagedProfile ? mInjector.getMediumToLargeAnimationDurationMs()
+ : mInjector.getMediumToLargeAnimationDurationMs() * 2 / 3);
+ as.start();
} else {
Log.e(TAG, "Unknown transition from: " + mSize + " to: " + newSize);
}
@@ -528,7 +607,11 @@ public abstract class AuthBiometricView extends LinearLayout {
if (mState == STATE_PENDING_CONFIRMATION) {
mCallback.onAction(Callback.ACTION_USER_CANCELED);
} else {
- mCallback.onAction(Callback.ACTION_BUTTON_NEGATIVE);
+ if (isDeviceCredentialAllowed()) {
+ startTransitionToCredentialUI();
+ } else {
+ mCallback.onAction(Callback.ACTION_BUTTON_NEGATIVE);
+ }
}
});
@@ -544,6 +627,16 @@ public abstract class AuthBiometricView extends LinearLayout {
});
}
+ /**
+ * Kicks off the animation process and invokes the callback.
+ */
+ void startTransitionToCredentialUI() {
+ updateSize(AuthDialog.SIZE_LARGE);
+ mHandler.postDelayed(() -> {
+ mCallback.onAction(Callback.ACTION_USE_DEVICE_CREDENTIAL);
+ }, mInjector.getAnimateCredentialStartDelayMs());
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -556,11 +649,37 @@ public abstract class AuthBiometricView extends LinearLayout {
*/
@VisibleForTesting
void onAttachedToWindowInternal() {
- setText(mTitleView, mBundle.getString(BiometricPrompt.KEY_TITLE));
- setText(mNegativeButton, mBundle.getString(BiometricPrompt.KEY_NEGATIVE_TEXT));
+ setText(mTitleView, mBiometricPromptBundle.getString(BiometricPrompt.KEY_TITLE));
+
+ final String negativeText;
+ if (isDeviceCredentialAllowed()) {
+
+ final @Utils.CredentialType int credentialType =
+ Utils.getCredentialType(mContext, mUserId);
+ switch(credentialType) {
+ case Utils.CREDENTIAL_PIN:
+ negativeText = getResources().getString(R.string.biometric_dialog_use_pin);
+ break;
+ case Utils.CREDENTIAL_PATTERN:
+ negativeText = getResources().getString(R.string.biometric_dialog_use_pattern);
+ break;
+ case Utils.CREDENTIAL_PASSWORD:
+ negativeText = getResources().getString(R.string.biometric_dialog_use_password);
+ break;
+ default:
+ negativeText = getResources().getString(R.string.biometric_dialog_use_password);
+ break;
+ }
- setTextOrHide(mSubtitleView, mBundle.getString(BiometricPrompt.KEY_SUBTITLE));
- setTextOrHide(mDescriptionView, mBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
+ } else {
+ negativeText = mBiometricPromptBundle.getString(BiometricPrompt.KEY_NEGATIVE_TEXT);
+ }
+ setText(mNegativeButton, negativeText);
+
+ setTextOrHide(mSubtitleView,
+ mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE));
+ setTextOrHide(mDescriptionView,
+ mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
if (mSavedState == null) {
updateState(STATE_AUTHENTICATING_ANIMATING_IN);
@@ -655,4 +774,8 @@ public abstract class AuthBiometricView extends LinearLayout {
}
}
}
+
+ private boolean isDeviceCredentialAllowed() {
+ return Utils.isDeviceCredentialAllowed(mBiometricPromptBundle);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 6555c75f677a..a9359d4ff0db 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -24,7 +24,9 @@ import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricPrompt;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -36,6 +38,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
@@ -72,17 +75,20 @@ public class AuthContainerView extends LinearLayout
@interface ContainerState {}
final Config mConfig;
+ private final Injector mInjector;
private final IBinder mWindowToken = new Binder();
private final WindowManager mWindowManager;
private final AuthPanelController mPanelController;
private final Interpolator mLinearOutSlowIn;
@VisibleForTesting final BiometricCallback mBiometricCallback;
+ private final CredentialCallback mCredentialCallback;
- private final ViewGroup mContainerView;
- private final AuthBiometricView mBiometricView;
+ @VisibleForTesting final FrameLayout mFrameLayout;
+ @VisibleForTesting @Nullable AuthBiometricView mBiometricView;
+ @VisibleForTesting @Nullable AuthCredentialView mCredentialView;
private final ImageView mBackgroundView;
- private final ScrollView mScrollView;
+ @VisibleForTesting final ScrollView mBiometricScrollView;
private final View mPanelView;
private final float mTranslationY;
@@ -145,7 +151,31 @@ public class AuthContainerView extends LinearLayout
public AuthContainerView build(int modalityMask) {
mConfig.mModalityMask = modalityMask;
- return new AuthContainerView(mConfig);
+ return new AuthContainerView(mConfig, new Injector());
+ }
+ }
+
+ public static class Injector {
+ ScrollView getBiometricScrollView(FrameLayout parent) {
+ return parent.findViewById(R.id.biometric_scrollview);
+ }
+
+ FrameLayout inflateContainerView(LayoutInflater factory, ViewGroup root) {
+ return (FrameLayout) factory.inflate(
+ R.layout.auth_container_view, root, false /* attachToRoot */);
+ }
+
+ AuthPanelController getPanelController(Context context, View panelView,
+ boolean isManagedProfile) {
+ return new AuthPanelController(context, panelView, isManagedProfile);
+ }
+
+ ImageView getBackgroundView(FrameLayout parent) {
+ return parent.findViewById(R.id.background);
+ }
+
+ View getPanelView(FrameLayout parent) {
+ return parent.findViewById(R.id.panel);
}
}
@@ -155,7 +185,7 @@ public class AuthContainerView extends LinearLayout
public void onAction(int action) {
switch (action) {
case AuthBiometricView.Callback.ACTION_AUTHENTICATED:
- animateAway(AuthDialogCallback.DISMISSED_AUTHENTICATED);
+ animateAway(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED);
break;
case AuthBiometricView.Callback.ACTION_USER_CANCELED:
animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
@@ -169,17 +199,30 @@ public class AuthContainerView extends LinearLayout
case AuthBiometricView.Callback.ACTION_ERROR:
animateAway(AuthDialogCallback.DISMISSED_ERROR);
break;
+ case AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL:
+ mConfig.mCallback.onDeviceCredentialPressed();
+ addCredentialView(false /* animatePanel */, true /* animateContents */);
+ break;
default:
Log.e(TAG, "Unhandled action: " + action);
}
}
}
+ final class CredentialCallback implements AuthCredentialView.Callback {
+ @Override
+ public void onCredentialMatched() {
+ animateAway(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
+ }
+ }
+
@VisibleForTesting
- AuthContainerView(Config config) {
+ AuthContainerView(Config config, Injector injector) {
super(config.mContext);
mConfig = config;
+ mInjector = injector;
+
mWindowManager = mContext.getSystemService(WindowManager.class);
mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
@@ -187,51 +230,48 @@ public class AuthContainerView extends LinearLayout
.getDimension(R.dimen.biometric_dialog_animation_translation_offset);
mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
mBiometricCallback = new BiometricCallback();
+ mCredentialCallback = new CredentialCallback();
final LayoutInflater factory = LayoutInflater.from(mContext);
- mContainerView = (ViewGroup) factory.inflate(
- R.layout.auth_container_view, this, false /* attachToRoot */);
-
- mPanelView = mContainerView.findViewById(R.id.panel);
- mPanelController = new AuthPanelController(mContext, mPanelView);
-
- // TODO: Update with new controllers if multi-modal authentication can occur simultaneously
- if (config.mModalityMask == BiometricAuthenticator.TYPE_FINGERPRINT) {
- mBiometricView = (AuthBiometricFingerprintView)
- factory.inflate(R.layout.auth_biometric_fingerprint_view, null, false);
- } else if (config.mModalityMask == BiometricAuthenticator.TYPE_FACE) {
- mBiometricView = (AuthBiometricFaceView)
- factory.inflate(R.layout.auth_biometric_face_view, null, false);
- } else {
- Log.e(TAG, "Unsupported modality mask: " + config.mModalityMask);
- mBiometricView = null;
- mBackgroundView = null;
- mScrollView = null;
- return;
+ mFrameLayout = mInjector.inflateContainerView(factory, this);
+
+ final boolean isManagedProfile = Utils.isManagedProfile(mContext, mConfig.mUserId);
+
+ mPanelView = mInjector.getPanelView(mFrameLayout);
+ mPanelController = mInjector.getPanelController(mContext, mPanelView, isManagedProfile);
+
+ // Inflate biometric view only if necessary.
+ if (Utils.isBiometricAllowed(mConfig.mBiometricPromptBundle)) {
+ if (config.mModalityMask == BiometricAuthenticator.TYPE_FINGERPRINT) {
+ mBiometricView = (AuthBiometricFingerprintView)
+ factory.inflate(R.layout.auth_biometric_fingerprint_view, null, false);
+ } else if (config.mModalityMask == BiometricAuthenticator.TYPE_FACE) {
+ mBiometricView = (AuthBiometricFaceView)
+ factory.inflate(R.layout.auth_biometric_face_view, null, false);
+ } else {
+ Log.e(TAG, "Unsupported biometric modality: " + config.mModalityMask);
+ mBiometricView = null;
+ mBackgroundView = null;
+ mBiometricScrollView = null;
+ return;
+ }
}
- mBackgroundView = mContainerView.findViewById(R.id.background);
+ mBiometricScrollView = mInjector.getBiometricScrollView(mFrameLayout);
+ mBackgroundView = mInjector.getBackgroundView(mFrameLayout);
+
- UserManager userManager = mContext.getSystemService(UserManager.class);
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- if (userManager.isManagedProfile(mConfig.mUserId)) {
+ if (isManagedProfile) {
final Drawable image = getResources().getDrawable(R.drawable.work_challenge_background,
mContext.getTheme());
+ final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
image.setColorFilter(dpm.getOrganizationColorForUser(mConfig.mUserId),
PorterDuff.Mode.DARKEN);
mBackgroundView.setScaleType(ImageView.ScaleType.CENTER_CROP);
mBackgroundView.setImageDrawable(image);
}
- mBiometricView.setRequireConfirmation(mConfig.mRequireConfirmation);
- mBiometricView.setPanelController(mPanelController);
- mBiometricView.setBiometricPromptBundle(config.mBiometricPromptBundle);
- mBiometricView.setCallback(mBiometricCallback);
- mBiometricView.setBackgroundView(mBackgroundView);
-
- mScrollView = mContainerView.findViewById(R.id.scrollview);
- mScrollView.addView(mBiometricView);
- addView(mContainerView);
+ addView(mFrameLayout);
setOnKeyListener((v, keyCode, event) -> {
if (keyCode != KeyEvent.KEYCODE_BACK) {
@@ -248,6 +288,53 @@ public class AuthContainerView extends LinearLayout
}
@Override
+ public boolean isAllowDeviceCredentials() {
+ return Utils.isDeviceCredentialAllowed(mConfig.mBiometricPromptBundle);
+ }
+
+ private void addBiometricView() {
+ mBiometricView.setRequireConfirmation(mConfig.mRequireConfirmation);
+ mBiometricView.setPanelController(mPanelController);
+ mBiometricView.setBiometricPromptBundle(mConfig.mBiometricPromptBundle);
+ mBiometricView.setCallback(mBiometricCallback);
+ mBiometricView.setBackgroundView(mBackgroundView);
+ mBiometricView.setUserId(mConfig.mUserId);
+ mBiometricScrollView.addView(mBiometricView);
+ }
+
+ /**
+ * Adds the credential view. When going from biometric to credential view, the biometric
+ * view starts the panel expansion animation. If the credential view is being shown first,
+ * it should own the panel expansion.
+ * @param animatePanel if the credential view needs to own the panel expansion animation
+ */
+ private void addCredentialView(boolean animatePanel, boolean animateContents) {
+ final LayoutInflater factory = LayoutInflater.from(mContext);
+ final int credentialType = Utils.getCredentialType(mContext, mConfig.mUserId);
+ switch (credentialType) {
+ case Utils.CREDENTIAL_PATTERN:
+ mCredentialView = (AuthCredentialView) factory.inflate(
+ R.layout.auth_credential_pattern_view, null, false);
+ break;
+ case Utils.CREDENTIAL_PIN:
+ case Utils.CREDENTIAL_PASSWORD:
+ mCredentialView = (AuthCredentialView) factory.inflate(
+ R.layout.auth_credential_password_view, null, false);
+ break;
+ default:
+ throw new IllegalStateException("Unknown credential type: " + credentialType);
+ }
+
+ mCredentialView.setContainerView(this);
+ mCredentialView.setUser(mConfig.mUserId);
+ mCredentialView.setCallback(mCredentialCallback);
+ mCredentialView.setBiometricPromptBundle(mConfig.mBiometricPromptBundle);
+ mCredentialView.setPanelController(mPanelController, animatePanel);
+ mCredentialView.setShouldAnimateContents(animateContents);
+ mFrameLayout.addView(mCredentialView);
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mPanelController.setContainerDimensions(getMeasuredWidth(), getMeasuredHeight());
@@ -256,8 +343,22 @@ public class AuthContainerView extends LinearLayout
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
+ onAttachedToWindowInternal();
+ }
+
+ @VisibleForTesting
+ void onAttachedToWindowInternal() {
mWakefulnessLifecycle.addObserver(this);
+ if (Utils.isBiometricAllowed(mConfig.mBiometricPromptBundle)) {
+ addBiometricView();
+ } else if (Utils.isDeviceCredentialAllowed(mConfig.mBiometricPromptBundle)) {
+ addCredentialView(true /* animatePanel */, false /* animateContents */);
+ } else {
+ throw new IllegalStateException("Unknown configuration: "
+ + Utils.getAuthenticators(mConfig.mBiometricPromptBundle));
+ }
+
if (mConfig.mSkipIntro) {
mContainerState = STATE_SHOWING;
} else {
@@ -265,7 +366,7 @@ public class AuthContainerView extends LinearLayout
// The background panel and content are different views since we need to be able to
// animate them separately in other places.
mPanelView.setY(mTranslationY);
- mScrollView.setY(mTranslationY);
+ mBiometricScrollView.setY(mTranslationY);
setAlpha(0f);
postOnAnimation(() -> {
@@ -276,12 +377,21 @@ public class AuthContainerView extends LinearLayout
.withLayer()
.withEndAction(this::onDialogAnimatedIn)
.start();
- mScrollView.animate()
+ mBiometricScrollView.animate()
.translationY(0)
.setDuration(ANIMATION_DURATION_SHOW_MS)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.start();
+ if (mCredentialView != null && mCredentialView.isAttachedToWindow()) {
+ mCredentialView.setY(mTranslationY);
+ mCredentialView.animate()
+ .translationY(0)
+ .setDuration(ANIMATION_DURATION_SHOW_MS)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ }
animate()
.alpha(1f)
.setDuration(ANIMATION_DURATION_SHOW_MS)
@@ -305,7 +415,9 @@ public class AuthContainerView extends LinearLayout
@Override
public void show(WindowManager wm, @Nullable Bundle savedState) {
- mBiometricView.restoreState(savedState);
+ if (mBiometricView != null) {
+ mBiometricView.restoreState(savedState);
+ }
wm.addView(this, getLayoutParams(mWindowToken));
}
@@ -346,7 +458,15 @@ public class AuthContainerView extends LinearLayout
@Override
public void onSaveState(@NonNull Bundle outState) {
outState.putInt(AuthDialog.KEY_CONTAINER_STATE, mContainerState);
- mBiometricView.onSaveState(outState);
+ // In the case where biometric and credential are both allowed, we can assume that
+ // biometric isn't showing if credential is showing since biometric is shown first.
+ outState.putBoolean(AuthDialog.KEY_BIOMETRIC_SHOWING,
+ mBiometricView != null && mCredentialView == null);
+ outState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, mCredentialView != null);
+
+ if (mBiometricView != null) {
+ mBiometricView.onSaveState(outState);
+ }
}
@Override
@@ -354,6 +474,11 @@ public class AuthContainerView extends LinearLayout
return mConfig.mOpPackageName;
}
+ @Override
+ public void animateToCredentialUI() {
+ mBiometricView.startTransitionToCredentialUI();
+ }
+
@VisibleForTesting
void animateAway(int reason) {
animateAway(true /* sendReason */, reason);
@@ -391,12 +516,20 @@ public class AuthContainerView extends LinearLayout
.withLayer()
.withEndAction(endActionRunnable)
.start();
- mScrollView.animate()
+ mBiometricScrollView.animate()
.translationY(mTranslationY)
.setDuration(ANIMATION_DURATION_AWAY_MS)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.start();
+ if (mCredentialView != null && mCredentialView.isAttachedToWindow()) {
+ mCredentialView.animate()
+ .translationY(mTranslationY)
+ .setDuration(ANIMATION_DURATION_AWAY_MS)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ }
animate()
.alpha(0f)
.setDuration(ANIMATION_DURATION_AWAY_MS)
@@ -431,7 +564,9 @@ public class AuthContainerView extends LinearLayout
return;
}
mContainerState = STATE_SHOWING;
- mBiometricView.onDialogAnimatedIn();
+ if (mBiometricView != null) {
+ mBiometricView.onDialogAnimatedIn();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index d10a3fede412..4c2afb0a14ca 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -23,6 +23,8 @@ import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.hardware.biometrics.Authenticator;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
@@ -105,6 +107,15 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
}
@Override
+ public void onDeviceCredentialPressed() {
+ try {
+ mReceiver.onDeviceCredentialPressed();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException when handling credential button", e);
+ }
+ }
+
+ @Override
public void onDismissed(@DismissedReason int reason) {
switch (reason) {
case AuthDialogCallback.DISMISSED_USER_CANCELED:
@@ -116,11 +127,12 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
break;
case AuthDialogCallback.DISMISSED_BUTTON_POSITIVE:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
break;
- case AuthDialogCallback.DISMISSED_AUTHENTICATED:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+ case AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED:
+ sendResultAndCleanUp(
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
break;
case AuthDialogCallback.DISMISSED_ERROR:
@@ -131,6 +143,10 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
break;
+ case AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED:
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED);
+ break;
+
default:
Log.e(TAG, "Unhandled reason: " + reason);
break;
@@ -185,16 +201,19 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int type, boolean requireConfirmation, int userId, String opPackageName) {
+ public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName) {
+ final int authenticators = Utils.getAuthenticators(bundle);
+
if (DEBUG) {
- Log.d(TAG, "showBiometricDialog, type: " + type
+ Log.d(TAG, "showAuthenticationDialog, authenticators: " + authenticators
+ + ", biometricModality: " + biometricModality
+ ", requireConfirmation: " + requireConfirmation);
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
- args.argi1 = type;
+ args.argi1 = biometricModality;
args.arg3 = requireConfirmation;
args.argi2 = userId;
args.arg4 = opPackageName;
@@ -204,6 +223,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
Log.w(TAG, "mCurrentDialog: " + mCurrentDialog);
skipAnimation = true;
}
+
showDialog(args, skipAnimation, null /* savedState */);
}
@@ -227,14 +247,21 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
}
@Override
- public void onBiometricError(String error) {
- if (DEBUG) Log.d(TAG, "onBiometricError: " + error);
- mCurrentDialog.onError(error);
+ public void onBiometricError(int errorCode, String error) {
+ if (DEBUG) Log.d(TAG, "onBiometricError: " + errorCode + ", " + error);
+
+ final boolean isLockout = errorCode == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
+ || errorCode == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) {
+ mCurrentDialog.animateToCredentialUI();
+ } else {
+ mCurrentDialog.onError(error);
+ }
}
@Override
- public void hideBiometricDialog() {
- if (DEBUG) Log.d(TAG, "hideBiometricDialog");
+ public void hideAuthenticationDialog() {
+ if (DEBUG) Log.d(TAG, "hideAuthenticationDialog");
mCurrentDialog.dismissFromSystemServer();
}
@@ -262,7 +289,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
}
if (DEBUG) {
- Log.d(TAG, "showDialog, "
+ Log.d(TAG, "showDialog: " + args
+ " savedState: " + savedState
+ " mCurrentDialog: " + mCurrentDialog
+ " newDialog: " + newDialog
@@ -306,6 +333,15 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
// to send its pending callback immediately.
if (savedState.getInt(AuthDialog.KEY_CONTAINER_STATE)
!= AuthContainerView.STATE_ANIMATING_OUT) {
+ final boolean credentialShowing =
+ savedState.getBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING);
+ if (credentialShowing) {
+ // TODO: Clean this up
+ Bundle bundle = (Bundle) mCurrentDialogArgs.arg1;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
+ Authenticator.TYPE_CREDENTIAL);
+ }
+
showDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
new file mode 100644
index 000000000000..8df072e9b99e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 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.biometrics;
+
+import android.content.Context;
+import android.text.InputType;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.R;
+
+/**
+ * Pin and Password UI
+ */
+public class AuthCredentialPasswordView extends AuthCredentialView
+ implements TextView.OnEditorActionListener {
+
+ private static final String TAG = "BiometricPrompt/AuthCredentialPasswordView";
+
+ private final InputMethodManager mImm;
+ private EditText mPasswordField;
+
+ public AuthCredentialPasswordView(Context context,
+ AttributeSet attrs) {
+ super(context, attrs);
+ mImm = mContext.getSystemService(InputMethodManager.class);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mPasswordField = findViewById(R.id.lockPassword);
+ mPasswordField.setOnEditorActionListener(this);
+ mPasswordField.setOnKeyListener((v, keyCode, event) -> {
+ if (keyCode != KeyEvent.KEYCODE_BACK) {
+ return false;
+ }
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ mContainerView.animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ }
+ return true;
+ });
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (mCredentialType == Utils.CREDENTIAL_PIN) {
+ mPasswordField.setInputType(
+ InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
+ }
+
+ // Wait a bit to focus the field so the focusable flag on the window is already set then.
+ post(() -> {
+ mPasswordField.requestFocus();
+ mImm.showSoftInput(mPasswordField, InputMethodManager.SHOW_IMPLICIT);
+ });
+ }
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ // Check if this was the result of hitting the enter key
+ final boolean isSoftImeEvent = event == null
+ && (actionId == EditorInfo.IME_NULL
+ || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT);
+ final boolean isKeyboardEnterKey = event != null
+ && KeyEvent.isConfirmKey(event.getKeyCode())
+ && event.getAction() == KeyEvent.ACTION_DOWN;
+ if (isSoftImeEvent || isKeyboardEnterKey) {
+ checkPasswordAndUnlock();
+ return true;
+ }
+ return false;
+ }
+
+ private void checkPasswordAndUnlock() {
+ final byte[] password = LockPatternUtils.charSequenceToByteArray(mPasswordField.getText());
+ if (password == null || password.length == 0) {
+ return;
+ }
+
+ mPendingLockCheck = LockPatternChecker.checkPassword(mLockPatternUtils,
+ password, mUserId, this::onCredentialChecked);
+ }
+
+ @Override
+ protected void onCredentialChecked(boolean matched, int timeoutMs) {
+ super.onCredentialChecked(matched, timeoutMs);
+
+ if (matched) {
+ mImm.hideSoftInputFromWindow(getWindowToken(), 0 /* flags */);
+ } else {
+ mPasswordField.setText("");
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
new file mode 100644
index 000000000000..6c36f8263237
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 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.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.systemui.R;
+
+import java.util.List;
+
+/**
+ * Pattern UI
+ */
+public class AuthCredentialPatternView extends AuthCredentialView {
+
+ private LockPatternView mLockPatternView;
+
+ private class UnlockPatternListener implements LockPatternView.OnPatternListener {
+
+ @Override
+ public void onPatternStart() {
+
+ }
+
+ @Override
+ public void onPatternCleared() {
+
+ }
+
+ @Override
+ public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
+
+ }
+
+ @Override
+ public void onPatternDetected(List<LockPatternView.Cell> pattern) {
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ }
+
+ mLockPatternView.setEnabled(false);
+
+ if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
+ // Pattern size is less than the minimum, do not count it as a failed attempt.
+ onPatternChecked(false /* matched */, 0 /* timeoutMs */);
+ return;
+ }
+
+ mPendingLockCheck = LockPatternChecker.checkPattern(
+ mLockPatternUtils,
+ pattern,
+ mUserId,
+ this::onPatternChecked);
+ }
+
+ private void onPatternChecked(boolean matched, int timeoutMs) {
+ AuthCredentialPatternView.this.onCredentialChecked(matched, timeoutMs);
+ if (timeoutMs > 0) {
+ mLockPatternView.setEnabled(false);
+ } else {
+ mLockPatternView.setEnabled(true);
+ }
+ }
+ }
+
+ @Override
+ protected void onErrorTimeoutFinish() {
+ super.onErrorTimeoutFinish();
+ mLockPatternView.setEnabled(true);
+ }
+
+ public AuthCredentialPatternView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mLockPatternView = findViewById(R.id.lockPattern);
+ mLockPatternView.setOnPatternListener(new UnlockPatternListener());
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(mUserId));
+ mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
new file mode 100644
index 000000000000..8c8611e49dfb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2019 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.biometrics;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+
+/**
+ * Abstract base class for Pin, Pattern, or Password authentication, for
+ * {@link BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)}
+ */
+public abstract class AuthCredentialView extends LinearLayout {
+
+ private static final String TAG = "BiometricPrompt/AuthCredentialView";
+ private static final int ERROR_DURATION_MS = 3000;
+
+ private final AccessibilityManager mAccessibilityManager;
+
+ protected final Handler mHandler;
+
+ private Bundle mBiometricPromptBundle;
+ private AuthPanelController mPanelController;
+ private boolean mShouldAnimatePanel;
+ private boolean mShouldAnimateContents;
+
+ private TextView mTitleView;
+ private TextView mSubtitleView;
+ private TextView mDescriptionView;
+ protected TextView mErrorView;
+
+ protected @Utils.CredentialType int mCredentialType;
+ protected final LockPatternUtils mLockPatternUtils;
+ protected AuthContainerView mContainerView;
+ protected Callback mCallback;
+ protected AsyncTask<?, ?, ?> mPendingLockCheck;
+ protected int mUserId;
+ protected ErrorTimer mErrorTimer;
+
+ interface Callback {
+ void onCredentialMatched();
+ }
+
+ protected static class ErrorTimer extends CountDownTimer {
+ private final TextView mErrorView;
+ private final Context mContext;
+
+ /**
+ * @param millisInFuture The number of millis in the future from the call
+ * to {@link #start()} until the countdown is done and {@link
+ * #onFinish()}
+ * is called.
+ * @param countDownInterval The interval along the way to receive
+ * {@link #onTick(long)} callbacks.
+ */
+ public ErrorTimer(Context context, long millisInFuture, long countDownInterval,
+ TextView errorView) {
+ super(millisInFuture, countDownInterval);
+ mErrorView = errorView;
+ mContext = context;
+ }
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ final int secondsCountdown = (int) (millisUntilFinished / 1000);
+ mErrorView.setText(mContext.getString(
+ R.string.biometric_dialog_credential_too_many_attempts, secondsCountdown));
+ }
+
+ @Override
+ public void onFinish() {
+ mErrorView.setText("");
+ }
+ }
+
+ protected final Runnable mClearErrorRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mErrorView.setText("");
+ }
+ };
+
+ public AuthCredentialView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mLockPatternUtils = new LockPatternUtils(mContext);
+ mHandler = new Handler(Looper.getMainLooper());
+ mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
+ }
+
+ protected void showError(String error) {
+ mHandler.removeCallbacks(mClearErrorRunnable);
+ mErrorView.setText(error);
+ mHandler.postDelayed(mClearErrorRunnable, ERROR_DURATION_MS);
+ }
+
+ private void setTextOrHide(TextView view, String string) {
+ if (TextUtils.isEmpty(string)) {
+ view.setVisibility(View.GONE);
+ } else {
+ view.setText(string);
+ }
+
+ Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
+ }
+
+ private void setText(TextView view, String string) {
+ view.setText(string);
+ }
+
+ void setUser(int user) {
+ mUserId = user;
+ }
+
+ void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ void setBiometricPromptBundle(Bundle bundle) {
+ mBiometricPromptBundle = bundle;
+ }
+
+ void setPanelController(AuthPanelController panelController, boolean animatePanel) {
+ mPanelController = panelController;
+ mShouldAnimatePanel = animatePanel;
+ }
+
+ void setShouldAnimateContents(boolean animateContents) {
+ mShouldAnimateContents = animateContents;
+ }
+
+ void setContainerView(AuthContainerView containerView) {
+ mContainerView = containerView;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mCredentialType = Utils.getCredentialType(mContext, mUserId);
+
+ setText(mTitleView, mBiometricPromptBundle.getString(BiometricPrompt.KEY_TITLE));
+ setTextOrHide(mSubtitleView,
+ mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE));
+ setTextOrHide(mDescriptionView,
+ mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
+
+ // Only animate this if we're transitioning from a biometric view.
+ if (mShouldAnimateContents) {
+ setTranslationY(getResources()
+ .getDimension(R.dimen.biometric_dialog_credential_translation_offset));
+ setAlpha(0);
+
+ postOnAnimation(() -> {
+ animate().translationY(0)
+ .setDuration(AuthDialog.ANIMATE_CREDENTIAL_INITIAL_DURATION_MS)
+ .alpha(1.f)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+ .withLayer()
+ .start();
+ });
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mErrorTimer != null) {
+ mErrorTimer.cancel();
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mTitleView = findViewById(R.id.title);
+ mSubtitleView = findViewById(R.id.subtitle);
+ mDescriptionView = findViewById(R.id.description);
+ mErrorView = findViewById(R.id.error);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ if (mShouldAnimatePanel) {
+ // Credential view is always full screen.
+ mPanelController.setUseFullScreen(true);
+ mPanelController.updateForContentDimensions(mPanelController.getContainerWidth(),
+ mPanelController.getContainerHeight(), 0 /* animateDurationMs */);
+ mShouldAnimatePanel = false;
+ }
+ }
+
+ protected void onErrorTimeoutFinish() {}
+
+ protected void onCredentialChecked(boolean matched, int timeoutMs) {
+ if (matched) {
+ mClearErrorRunnable.run();
+ mCallback.onCredentialMatched();
+ } else {
+ if (timeoutMs > 0) {
+ mHandler.removeCallbacks(mClearErrorRunnable);
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline(mUserId, timeoutMs);
+ mErrorTimer = new ErrorTimer(mContext,
+ deadline - SystemClock.elapsedRealtime(),
+ LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS,
+ mErrorView) {
+ @Override
+ public void onFinish() {
+ onErrorTimeoutFinish();
+ mClearErrorRunnable.run();
+ }
+ };
+ mErrorTimer.start();
+ } else {
+ final int error;
+ switch (mCredentialType) {
+ case Utils.CREDENTIAL_PIN:
+ error = R.string.biometric_dialog_wrong_pin;
+ break;
+ case Utils.CREDENTIAL_PATTERN:
+ error = R.string.biometric_dialog_wrong_pattern;
+ break;
+ case Utils.CREDENTIAL_PASSWORD:
+ error = R.string.biometric_dialog_wrong_password;
+ break;
+ default:
+ error = R.string.biometric_dialog_wrong_password;
+ break;
+ }
+ showError(getResources().getString(error));
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
index edb29538874c..ca95f9d736fc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
@@ -31,6 +31,8 @@ import java.lang.annotation.RetentionPolicy;
public interface AuthDialog {
String KEY_CONTAINER_STATE = "container_state";
+ String KEY_BIOMETRIC_SHOWING = "biometric_showing";
+ String KEY_CREDENTIAL_SHOWING = "credential_showing";
String KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY = "try_agian_visibility";
String KEY_BIOMETRIC_STATE = "state";
@@ -40,17 +42,38 @@ public interface AuthDialog {
String KEY_BIOMETRIC_DIALOG_SIZE = "size";
int SIZE_UNKNOWN = 0;
+ /**
+ * Minimal UI, showing only biometric icon.
+ */
int SIZE_SMALL = 1;
+ /**
+ * Normal-sized biometric UI, showing title, icon, buttons, etc.
+ */
int SIZE_MEDIUM = 2;
+ /**
+ * Full-screen credential UI.
+ */
int SIZE_LARGE = 3;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SIZE_UNKNOWN, SIZE_SMALL, SIZE_MEDIUM, SIZE_LARGE})
@interface DialogSize {}
/**
- * Animation duration, e.g. small to medium dialog, icon translation, etc.
+ * Animation duration, from small to medium dialog, including back panel, icon translation, etc
+ */
+ int ANIMATE_SMALL_TO_MEDIUM_DURATION_MS = 150;
+ /**
+ * Animation duration from medium to large dialog, including biometric fade out, back panel, etc
+ */
+ int ANIMATE_MEDIUM_TO_LARGE_DURATION_MS = 450;
+ /**
+ * Delay before notifying {@link AuthCredentialView} to start animating in.
+ */
+ int ANIMATE_CREDENTIAL_START_DELAY_MS = ANIMATE_MEDIUM_TO_LARGE_DURATION_MS * 2 / 3;
+ /**
+ * Animation duration when sliding in credential UI
*/
- int ANIMATE_DURATION_MS = 150;
+ int ANIMATE_CREDENTIAL_INITIAL_DURATION_MS = 150;
/**
* Show the dialog.
@@ -101,4 +124,14 @@ public interface AuthDialog {
* Get the client's package name
*/
String getOpPackageName();
+
+ /**
+ * Animate to credential UI. Typically called after biometric is locked out.
+ */
+ void animateToCredentialUI();
+
+ /**
+ * @return true if device credential is allowed.
+ */
+ boolean isAllowDeviceCredentials();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
index 70752f5f860e..12bb1228a53b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
@@ -27,17 +27,18 @@ public interface AuthDialogCallback {
int DISMISSED_USER_CANCELED = 1;
int DISMISSED_BUTTON_NEGATIVE = 2;
int DISMISSED_BUTTON_POSITIVE = 3;
-
- int DISMISSED_AUTHENTICATED = 4;
+ int DISMISSED_BIOMETRIC_AUTHENTICATED = 4;
int DISMISSED_ERROR = 5;
int DISMISSED_BY_SYSTEM_SERVER = 6;
+ int DISMISSED_CREDENTIAL_AUTHENTICATED = 7;
@IntDef({DISMISSED_USER_CANCELED,
DISMISSED_BUTTON_NEGATIVE,
DISMISSED_BUTTON_POSITIVE,
- DISMISSED_AUTHENTICATED,
+ DISMISSED_BIOMETRIC_AUTHENTICATED,
DISMISSED_ERROR,
- DISMISSED_BY_SYSTEM_SERVER})
+ DISMISSED_BY_SYSTEM_SERVER,
+ DISMISSED_CREDENTIAL_AUTHENTICATED})
@interface DismissedReason {}
/**
@@ -50,4 +51,9 @@ public interface AuthDialogCallback {
* Invoked when the "try again" button is clicked
*/
void onTryAgainPressed();
+
+ /**
+ * Invoked when the "use password" button is clicked
+ */
+ void onDeviceCredentialPressed();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
index 55ba0491dc1e..2b8b586961ff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
@@ -16,15 +16,17 @@
package com.android.systemui.biometrics;
+import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Outline;
import android.util.Log;
import android.view.View;
import android.view.ViewOutlineProvider;
+import android.view.animation.AccelerateDecelerateInterpolator;
import com.android.systemui.R;
-import com.android.systemui.biometrics.AuthDialog;
/**
* Controls the back panel and its animations for the BiometricPrompt UI.
@@ -36,8 +38,9 @@ public class AuthPanelController extends ViewOutlineProvider {
private final Context mContext;
private final View mPanelView;
- private final float mCornerRadius;
- private final int mBiometricMargin;
+ private final boolean mIsManagedProfile;
+
+ private boolean mUseFullScreen;
private int mContainerWidth;
private int mContainerHeight;
@@ -45,14 +48,23 @@ public class AuthPanelController extends ViewOutlineProvider {
private int mContentWidth;
private int mContentHeight;
+ private float mCornerRadius;
+ private int mMargin;
+
@Override
public void getOutline(View view, Outline outline) {
final int left = (mContainerWidth - mContentWidth) / 2;
final int right = mContainerWidth - left;
+
+ // If the content fits within the container, shrink the height to wrap the content.
+ // Otherwise, set the outline to be the display size minus the margin - the content within
+ // is scrollable.
final int top = mContentHeight < mContainerHeight
- ? mContainerHeight - mContentHeight - mBiometricMargin
- : mBiometricMargin;
- final int bottom = mContainerHeight - mBiometricMargin;
+ ? mContainerHeight - mContentHeight - mMargin
+ : mMargin;
+
+ // TODO(b/139954942) Likely don't need to "+1" after we resolve the navbar styling.
+ final int bottom = mContainerHeight - mMargin + 1;
outline.setRoundRect(left, top, right, bottom, mCornerRadius);
}
@@ -64,11 +76,34 @@ public class AuthPanelController extends ViewOutlineProvider {
mContainerHeight = containerHeight;
}
- public void updateForContentDimensions(int contentWidth, int contentHeight, boolean animate) {
+ public void setUseFullScreen(boolean fullScreen) {
+ mUseFullScreen = fullScreen;
+ }
+
+ public ValueAnimator getTranslationAnimator(float relativeTranslationY) {
+ final ValueAnimator animator = ValueAnimator.ofFloat(
+ mPanelView.getY(), mPanelView.getY() - relativeTranslationY);
+ animator.addUpdateListener(animation -> {
+ final float translation = (float) animation.getAnimatedValue();
+ mPanelView.setTranslationY(translation);
+ });
+ return animator;
+ }
+
+ public ValueAnimator getAlphaAnimator(float alpha) {
+ final ValueAnimator animator = ValueAnimator.ofFloat(mPanelView.getAlpha(), alpha);
+ animator.addUpdateListener(animation -> {
+ mPanelView.setAlpha((float) animation.getAnimatedValue());
+ });
+ return animator;
+ }
+
+ public void updateForContentDimensions(int contentWidth, int contentHeight,
+ int animateDurationMs) {
if (DEBUG) {
Log.v(TAG, "Content Width: " + contentWidth
+ " Height: " + contentHeight
- + " Animate: " + animate);
+ + " Animate: " + animateDurationMs);
}
if (mContainerWidth == 0 || mContainerHeight == 0) {
@@ -76,27 +111,86 @@ public class AuthPanelController extends ViewOutlineProvider {
return;
}
- if (animate) {
+ final int margin = mUseFullScreen ? 0 : (int) mContext.getResources()
+ .getDimension(R.dimen.biometric_dialog_border_padding);
+ final float cornerRadius = mUseFullScreen ? 0 : mContext.getResources()
+ .getDimension(R.dimen.biometric_dialog_corner_size);
+
+ // When going to full-screen for managed profiles, fade away so the managed profile
+ // background behind this view becomes visible.
+ final boolean shouldFadeAway = mUseFullScreen && mIsManagedProfile;
+ final int alpha = shouldFadeAway ? 0 : 255;
+ final float elevation = shouldFadeAway ? 0 :
+ mContext.getResources().getDimension(R.dimen.biometric_dialog_elevation);
+
+ if (animateDurationMs > 0) {
+ // Animate margin
+ ValueAnimator marginAnimator = ValueAnimator.ofInt(mMargin, margin);
+ marginAnimator.addUpdateListener((animation) -> {
+ mMargin = (int) animation.getAnimatedValue();
+ });
+
+ // Animate corners
+ ValueAnimator cornerAnimator = ValueAnimator.ofFloat(mCornerRadius, cornerRadius);
+ cornerAnimator.addUpdateListener((animation) -> {
+ mCornerRadius = (float) animation.getAnimatedValue();
+ });
+
+ // Animate height
ValueAnimator heightAnimator = ValueAnimator.ofInt(mContentHeight, contentHeight);
- heightAnimator.setDuration(AuthDialog.ANIMATE_DURATION_MS);
heightAnimator.addUpdateListener((animation) -> {
mContentHeight = (int) animation.getAnimatedValue();
mPanelView.invalidateOutline();
});
heightAnimator.start();
+
+ // Animate width
+ ValueAnimator widthAnimator = ValueAnimator.ofInt(mContentWidth, contentWidth);
+ widthAnimator.addUpdateListener((animation) -> {
+ mContentWidth = (int) animation.getAnimatedValue();
+ });
+
+ // Animate background
+ ValueAnimator alphaAnimator = ValueAnimator.ofInt(
+ mPanelView.getBackground().getAlpha(), alpha);
+ alphaAnimator.addUpdateListener((animation) -> {
+ if (shouldFadeAway) {
+ mPanelView.getBackground().setAlpha((int) animation.getAnimatedValue());
+ }
+ });
+
+ // Play together
+ AnimatorSet as = new AnimatorSet();
+ as.setDuration(animateDurationMs);
+ as.setInterpolator(new AccelerateDecelerateInterpolator());
+ as.playTogether(cornerAnimator, widthAnimator, marginAnimator, alphaAnimator);
+ as.start();
+
} else {
+ mMargin = margin;
+ mCornerRadius = cornerRadius;
mContentWidth = contentWidth;
mContentHeight = contentHeight;
+ mPanelView.getBackground().setAlpha(alpha);
mPanelView.invalidateOutline();
}
}
- AuthPanelController(Context context, View panelView) {
+ int getContainerWidth() {
+ return mContainerWidth;
+ }
+
+ int getContainerHeight() {
+ return mContainerHeight;
+ }
+
+ AuthPanelController(Context context, View panelView, boolean isManagedProfile) {
mContext = context;
mPanelView = panelView;
+ mIsManagedProfile = isManagedProfile;
mCornerRadius = context.getResources()
.getDimension(R.dimen.biometric_dialog_corner_size);
- mBiometricMargin = (int) context.getResources()
+ mMargin = (int) context.getResources()
.getDimension(R.dimen.biometric_dialog_border_padding);
mPanelView.setOutlineProvider(this);
mPanelView.setClipToOutline(true);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
index e00cf6abafaa..d6f830dd2e7a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
@@ -18,14 +18,36 @@ package com.android.systemui.biometrics;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
+import android.annotation.IntDef;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.hardware.biometrics.Authenticator;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
+import android.os.UserManager;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
public class Utils {
+
+ public static final int CREDENTIAL_PIN = 1;
+ public static final int CREDENTIAL_PATTERN = 2;
+ public static final int CREDENTIAL_PASSWORD = 3;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD})
+ @interface CredentialType {}
+
+
static float dpToPixels(Context context, float dp) {
return dp * ((float) context.getResources().getDisplayMetrics().densityDpi
/ DisplayMetrics.DENSITY_DEFAULT);
@@ -46,4 +68,41 @@ public class Utils {
view.sendAccessibilityEventUnchecked(event);
view.notifySubtreeAccessibilityStateChanged(view, view, CONTENT_CHANGE_TYPE_SUBTREE);
}
+
+ static boolean isDeviceCredentialAllowed(Bundle biometricPromptBundle) {
+ final int authenticators = getAuthenticators(biometricPromptBundle);
+ return (authenticators & Authenticator.TYPE_CREDENTIAL) != 0;
+ }
+
+ static boolean isBiometricAllowed(Bundle biometricPromptBundle) {
+ final int authenticators = getAuthenticators(biometricPromptBundle);
+ return (authenticators & Authenticator.TYPE_BIOMETRIC) != 0;
+ }
+
+ static int getAuthenticators(Bundle biometricPromptBundle) {
+ return biometricPromptBundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
+ }
+
+ static @CredentialType int getCredentialType(Context context, int userId) {
+ final LockPatternUtils lpu = new LockPatternUtils(context);
+ switch (lpu.getKeyguardStoredPasswordQuality(userId)) {
+ case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+ return CREDENTIAL_PATTERN;
+ case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
+ return CREDENTIAL_PIN;
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
+ case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
+ return CREDENTIAL_PASSWORD;
+ default:
+ return CREDENTIAL_PASSWORD;
+ }
+ }
+
+ static boolean isManagedProfile(Context context, int userId) {
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ return userManager.isManagedProfile(userId);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 134d4b87a159..36e04fe42ced 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -270,12 +270,13 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
default void onRotationProposal(int rotation, boolean isValid) { }
- default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int type, boolean requireConfirmation, int userId, String opPackageName) { }
+ default void showAuthenticationDialog(Bundle bundle,
+ IBiometricServiceReceiverInternal receiver, int biometricModality,
+ boolean requireConfirmation, int userId, String opPackageName) { }
default void onBiometricAuthenticated(boolean authenticated, String failureReason) { }
default void onBiometricHelp(String message) { }
- default void onBiometricError(String error) { }
- default void hideBiometricDialog() { }
+ default void onBiometricError(int errorCode, String error) { }
+ default void hideAuthenticationDialog() { }
/**
* @see IStatusBar#onDisplayReady(int)
@@ -740,13 +741,13 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int type, boolean requireConfirmation, int userId, String opPackageName) {
+ public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
- args.argi1 = type;
+ args.argi1 = biometricModality;
args.arg3 = requireConfirmation;
args.argi2 = userId;
args.arg4 = opPackageName;
@@ -773,14 +774,14 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
@Override
- public void onBiometricError(String error) {
+ public void onBiometricError(int errorCode, String error) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget();
+ mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, errorCode, 0, error).sendToTarget();
}
}
@Override
- public void hideBiometricDialog() {
+ public void hideAuthenticationDialog() {
synchronized (mLock) {
mHandler.obtainMessage(MSG_BIOMETRIC_HIDE).sendToTarget();
}
@@ -1032,10 +1033,10 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
SomeArgs someArgs = (SomeArgs) msg.obj;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showBiometricDialog(
+ mCallbacks.get(i).showAuthenticationDialog(
(Bundle) someArgs.arg1,
(IBiometricServiceReceiverInternal) someArgs.arg2,
- someArgs.argi1 /* type */,
+ someArgs.argi1 /* biometricModality */,
(boolean) someArgs.arg3 /* requireConfirmation */,
someArgs.argi2 /* userId */,
(String) someArgs.arg4 /* opPackageName */);
@@ -1060,12 +1061,12 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
break;
case MSG_BIOMETRIC_ERROR:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onBiometricError((String) msg.obj);
+ mCallbacks.get(i).onBiometricError(msg.arg1, (String) msg.obj);
}
break;
case MSG_BIOMETRIC_HIDE:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).hideBiometricDialog();
+ mCallbacks.get(i).hideAuthenticationDialog();
}
break;
case MSG_SHOW_CHARGING_ANIMATION:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 90301c59dc68..148a1a87f305 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -20,7 +20,6 @@ import static android.service.notification.NotificationListenerService.REASON_ER
import android.annotation.Nullable;
import android.app.Notification;
-import android.content.Context;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
@@ -124,8 +123,8 @@ public class NotificationEntryManager implements
}
@Inject
- public NotificationEntryManager(Context context) {
- mNotificationData = new NotificationData(context);
+ public NotificationEntryManager(NotificationData notificationData) {
+ mNotificationData = notificationData;
}
/** Adds a {@link NotificationEntryListener}. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
new file mode 100644
index 000000000000..480cb78efbba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import android.content.Context
+import android.provider.DeviceConfig
+
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
+import com.android.systemui.util.DeviceConfigProxy
+
+import javax.inject.Inject
+
+private var sUsePeopleFiltering: Boolean? = null
+
+/**
+ * Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config.
+ */
+class NotificationSectionsFeatureManager @Inject constructor(
+ val proxy: DeviceConfigProxy,
+ val context: Context
+) {
+
+ fun isFilteringEnabled(): Boolean {
+ return usePeopleFiltering(proxy)
+ }
+
+ fun getNotificationBuckets(): IntArray {
+ return when {
+ isFilteringEnabled() ->
+ intArrayOf(BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT)
+ NotificationUtils.useNewInterruptionModel(context) ->
+ intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
+ else ->
+ intArrayOf(BUCKET_ALERTING)
+ }
+ }
+
+ fun getNumberOfBuckets(): Int {
+ return getNotificationBuckets().size
+ }
+
+ @VisibleForTesting
+ fun clearCache() {
+ sUsePeopleFiltering = null
+ }
+}
+
+private fun usePeopleFiltering(proxy: DeviceConfigProxy): Boolean {
+ if (sUsePeopleFiltering == null) {
+ sUsePeopleFiltering = proxy.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, false)
+ }
+
+ return sUsePeopleFiltering!!
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index dfbbf987dd96..1af47dd0f4c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -36,7 +36,6 @@ public class NotificationUtils {
private static final int[] sLocationOffset = new int[2];
@Nullable private static Boolean sUseNewInterruptionModel = null;
- @Nullable private static Boolean sUsePeopleFiltering = null;
public static boolean isGrayscale(ImageView v, ContrastColorUtil colorUtil) {
Object isGrayscale = v.getTag(R.id.icon_is_grayscale);
@@ -88,17 +87,4 @@ public class NotificationUtils {
}
return sUseNewInterruptionModel;
}
-
- /**
- * Caches and returns the value of the people filtering setting. Cannot change except through
- * process restarts.
- */
- public static boolean usePeopleFiltering(Context context) {
- if (sUsePeopleFiltering == null) {
- sUsePeopleFiltering = context.getResources().getBoolean(
- R.bool.config_usePeopleFiltering);
- }
-
- return sUsePeopleFiltering;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index aacb2dd0682f..cf0fbbb5d9c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -24,7 +24,6 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
-import android.content.Context;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.SnoozeCriterion;
@@ -35,7 +34,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -46,6 +45,8 @@ import java.util.Comparator;
import java.util.List;
import java.util.Objects;
+import javax.inject.Inject;
+
/**
* The list of currently displaying notifications.
*/
@@ -73,8 +74,9 @@ public class NotificationData {
private final Ranking mTmpRanking = new Ranking();
private final boolean mUsePeopleFiltering;
- public NotificationData(Context context) {
- mUsePeopleFiltering = NotificationUtils.usePeopleFiltering(context);
+ @Inject
+ public NotificationData(NotificationSectionsFeatureManager sectionsFeatureManager) {
+ mUsePeopleFiltering = sectionsFeatureManager.isFilteringEnabled();
}
public void setHeadsUpManager(HeadsUpManager headsUpManager) {
@@ -483,19 +485,6 @@ public class NotificationData {
}
/**
- * Get the current set of buckets for notification entries, as defined here
- */
- public static int[] getNotificationBuckets(Context context) {
- if (NotificationUtils.usePeopleFiltering(context)) {
- return new int[]{BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT};
- } else if (NotificationUtils.useNewInterruptionModel(context)) {
- return new int[]{BUCKET_ALERTING, BUCKET_SILENT};
- } else {
- return new int[]{BUCKET_ALERTING};
- }
- }
-
- /**
* Provides access to keyguard state and user settings dependent data.
*/
public interface KeyguardEnvironment {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index ec0c6348fd89..b4f7b59349d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -16,10 +16,9 @@
package com.android.systemui.statusbar.notification.stack;
-import android.content.Context;
import android.util.MathUtils;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -52,8 +51,8 @@ public class NotificationRoundnessManager implements OnHeadsUpChangedListener {
@Inject
NotificationRoundnessManager(
KeyguardBypassController keyguardBypassController,
- Context context) {
- int numberOfSections = NotificationData.getNotificationBuckets(context).length;
+ NotificationSectionsFeatureManager sectionsFeatureManager) {
+ int numberOfSections = sectionsFeatureManager.getNumberOfBuckets();
mFirstInSectionViews = new ActivatableNotificationView[numberOfSections];
mLastInSectionViews = new ActivatableNotificationView[numberOfSections];
mTmpFirstInSectionViews = new ActivatableNotificationView[numberOfSections];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index a67018ef9710..f5705c5f643b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -109,11 +109,11 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -517,7 +517,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
StatusBarStateController statusBarStateController,
HeadsUpManagerPhone headsUpManager,
KeyguardBypassController keyguardBypassController,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager,
+ NotificationSectionsFeatureManager sectionsFeatureManager) {
super(context, attrs, 0, 0);
Resources res = getResources();
@@ -531,7 +532,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mKeyguardBypassController = keyguardBypassController;
mFalsingManager = falsingManager;
- int[] buckets = NotificationData.getNotificationBuckets(context);
+ int[] buckets = sectionsFeatureManager.getNotificationBuckets();
mSectionsManager =
new NotificationSectionsManager(
this,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 68eca8d68d90..cac33044ea13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -117,6 +117,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
+ private final boolean mShowLeftAffordance;
+ private final boolean mShowCameraAffordance;
+
private KeyguardAffordanceView mRightAffordanceView;
private KeyguardAffordanceView mLeftAffordanceView;
private ViewGroup mIndicationArea;
@@ -184,6 +187,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mShowLeftAffordance = getResources().getBoolean(R.bool.config_keyguardShowLeftAffordance);
+ mShowCameraAffordance = getResources()
+ .getBoolean(R.bool.config_keyguardShowCameraAffordance);
}
private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
@@ -371,8 +377,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
// Things are not set up yet; reply hazy, ask again later
return;
}
- mRightAffordanceView.setVisibility(!mDozing && mRightButton.getIcon().isVisible
- ? View.VISIBLE : View.GONE);
+ mRightAffordanceView.setVisibility(!mDozing && mShowCameraAffordance
+ && mRightButton.getIcon().isVisible ? View.VISIBLE : View.GONE);
}
/**
@@ -384,8 +390,12 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
private void updateLeftAffordanceIcon() {
+ if (!mShowLeftAffordance || mDozing) {
+ mLeftAffordanceView.setVisibility(GONE);
+ return;
+ }
IconState state = mLeftButton.getIcon();
- mLeftAffordanceView.setVisibility(!mDozing && state.isVisible ? View.VISIBLE : View.GONE);
+ mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE);
if (state.drawable != mLeftAffordanceView.getDrawable()
|| state.tint != mLeftAffordanceView.shouldTint()) {
mLeftAffordanceView.setImageDrawable(state.drawable, state.tint);
@@ -767,10 +777,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
@Override
public IconState getIcon() {
mLeftIsVoiceAssist = canLaunchVoiceAssist();
- final boolean showAffordance =
- getResources().getBoolean(R.bool.config_keyguardShowLeftAffordance);
if (mLeftIsVoiceAssist) {
- mIconState.isVisible = mUserSetupComplete && showAffordance;
+ mIconState.isVisible = mUserSetupComplete && mShowLeftAffordance;
if (mLeftAssistIcon == null) {
mIconState.drawable = mContext.getDrawable(R.drawable.ic_mic_26dp);
} else {
@@ -779,7 +787,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mIconState.contentDescription = mContext.getString(
R.string.accessibility_voice_assist_button);
} else {
- mIconState.isVisible = mUserSetupComplete && showAffordance && isPhoneVisible();
+ mIconState.isVisible = mUserSetupComplete && mShowLeftAffordance
+ && isPhoneVisible();
mIconState.drawable = mContext.getDrawable(
com.android.internal.R.drawable.ic_phone);
mIconState.contentDescription = mContext.getString(
@@ -802,7 +811,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
public IconState getIcon() {
boolean isCameraDisabled = (mStatusBar != null) && !mStatusBar.isCameraAllowedByAdmin();
mIconState.isVisible = !isCameraDisabled
- && getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance)
+ && mShowCameraAffordance
&& mUserSetupComplete
&& resolveCameraIntent() != null;
mIconState.drawable = mContext.getDrawable(R.drawable.ic_camera_alt_24dp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index da5931a50ba8..b1825c8ce667 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -25,7 +25,6 @@ import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.Dependency.BG_HANDLER;
import static com.android.systemui.Dependency.MAIN_HANDLER;
@@ -3936,20 +3935,17 @@ public class StatusBar extends SystemUI implements DemoMode,
}
boolean isCameraAllowedByAdmin() {
- // TODO(b/140060745)
- return whitelistIpcs(() -> {
- if (mDevicePolicyManager.getCameraDisabled(null,
- mLockscreenUserManager.getCurrentUserId())) {
- return false;
- } else if (mStatusBarKeyguardViewManager == null
- || (isKeyguardShowing() && isKeyguardSecure())) {
- // Check if the admin has disabled the camera specifically for the keyguard
- return (mDevicePolicyManager.getKeyguardDisabledFeatures(null,
- mLockscreenUserManager.getCurrentUserId())
- & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0;
- }
- return true;
- });
+ if (mDevicePolicyManager.getCameraDisabled(null,
+ mLockscreenUserManager.getCurrentUserId())) {
+ return false;
+ } else if (mStatusBarKeyguardViewManager == null
+ || (isKeyguardShowing() && isKeyguardSecure())) {
+ // Check if the admin has disabled the camera specifically for the keyguard
+ return (mDevicePolicyManager.getKeyguardDisabledFeatures(null,
+ mLockscreenUserManager.getCurrentUserId())
+ & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0;
+ }
+ return true;
}
private boolean isGoingToSleep() {
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index cd1f0cc13297..47b56e097ec9 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -92,7 +92,7 @@ public class UsbPermissionActivity extends AlertActivity
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
} else {
boolean hasRecordPermission =
- PermissionChecker.checkPermission(
+ PermissionChecker.checkPermissionForPreflight(
this, android.Manifest.permission.RECORD_AUDIO, -1, aInfo.uid,
mPackageName)
== android.content.pm.PackageManager.PERMISSION_GRANTED;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
index 7a09137b1ff8..2c85424bac79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
@@ -70,7 +71,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testOnAuthenticationSucceeded_noConfirmationRequired_sendsActionAuthenticated() {
- initDialog(mContext, mCallback, new MockInjector());
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
// The onAuthenticated runnable is posted when authentication succeeds.
mBiometricView.onAuthenticationSucceeded();
@@ -81,7 +82,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testOnAuthenticationSucceeded_confirmationRequired_updatesDialogContents() {
- initDialog(mContext, mCallback, new MockInjector());
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
mBiometricView.setRequireConfirmation(true);
mBiometricView.onAuthenticationSucceeded();
@@ -97,7 +98,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testPositiveButton_sendsActionAuthenticated() {
Button button = new Button(mContext);
- initDialog(mContext, mCallback, new MockInjector() {
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
public Button getPositiveButton() {
return button;
@@ -114,7 +115,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testNegativeButton_beforeAuthentication_sendsActionButtonNegative() {
Button button = new Button(mContext);
- initDialog(mContext, mCallback, new MockInjector() {
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
public Button getNegativeButton() {
return button;
@@ -131,7 +132,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testNegativeButton_whenPendingConfirmation_sendsActionUserCanceled() {
Button button = new Button(mContext);
- initDialog(mContext, mCallback, new MockInjector() {
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
public Button getNegativeButton() {
return button;
@@ -149,7 +150,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testTryAgainButton_sendsActionTryAgain() {
Button button = new Button(mContext);
- initDialog(mContext, mCallback, new MockInjector() {
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
public Button getTryAgainButton() {
return button;
@@ -165,7 +166,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testError_sendsActionError() {
- initDialog(mContext, mCallback, new MockInjector());
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
final String testError = "testError";
mBiometricView.onError(testError);
waitForIdleSync();
@@ -176,7 +177,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testBackgroundClicked_sendsActionUserCanceled() {
- initDialog(mContext, mCallback, new MockInjector());
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
View view = new View(mContext);
mBiometricView.setBackgroundView(view);
@@ -186,7 +187,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testBackgroundClicked_afterAuthenticated_neverSendsUserCanceled() {
- initDialog(mContext, mCallback, new MockInjector());
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
View view = new View(mContext);
mBiometricView.setBackgroundView(view);
@@ -197,8 +198,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testBackgroundClicked_whenSmallDialog_neverSendsUserCanceled() {
- initDialog(mContext, mCallback, new MockInjector());
- mBiometricView.setPanelController(mPanelController);
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
mBiometricView.updateSize(AuthDialog.SIZE_SMALL);
View view = new View(mContext);
@@ -213,7 +213,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
Button tryAgainButton = new Button(mContext);
TextView indicatorView = new TextView(mContext);
- initDialog(mContext, mCallback, new MockInjector() {
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
public Button getTryAgainButton() {
return tryAgainButton;
@@ -249,16 +249,18 @@ public class AuthBiometricViewTest extends SysuiTestCase {
// Create new dialog and restore the previous state into it
Button tryAgainButton2 = new Button(mContext);
TextView indicatorView2 = new TextView(mContext);
- initDialog(mContext, mCallback, state, new MockInjector() {
- @Override
- public Button getTryAgainButton() {
- return tryAgainButton2;
- }
- @Override
- public TextView getIndicatorView() {
- return indicatorView2;
- }
- });
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, state,
+ new MockInjector() {
+ @Override
+ public Button getTryAgainButton() {
+ return tryAgainButton2;
+ }
+
+ @Override
+ public TextView getIndicatorView() {
+ return indicatorView2;
+ }
+ });
mBiometricView.setRequireConfirmation(requireConfirmation);
waitForIdleSync();
@@ -271,26 +273,51 @@ public class AuthBiometricViewTest extends SysuiTestCase {
// dialog size is known.
}
- private Bundle buildBiometricPromptBundle() {
+ @Test
+ public void testNegativeButton_whenDeviceCredentialAllowed() throws InterruptedException {
+ Button negativeButton = new Button(mContext);
+ initDialog(mContext, true /* allowDeviceCredential */, mCallback, new MockInjector() {
+ @Override
+ public Button getNegativeButton() {
+ return negativeButton;
+ }
+ });
+
+ negativeButton.performClick();
+ waitForIdleSync();
+
+ verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL);
+ }
+
+ private Bundle buildBiometricPromptBundle(boolean allowDeviceCredential) {
Bundle bundle = new Bundle();
bundle.putCharSequence(BiometricPrompt.KEY_TITLE, "Title");
- bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, "Negative");
+ int authenticators = Authenticator.TYPE_BIOMETRIC;
+ if (allowDeviceCredential) {
+ authenticators |= Authenticator.TYPE_CREDENTIAL;
+ } else {
+ bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, "Negative");
+ }
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
return bundle;
}
- private void initDialog(Context context, AuthBiometricView.Callback callback,
+ private void initDialog(Context context, boolean allowDeviceCredential,
+ AuthBiometricView.Callback callback,
Bundle savedState, MockInjector injector) {
mBiometricView = new TestableBiometricView(context, null, injector);
- mBiometricView.setBiometricPromptBundle(buildBiometricPromptBundle());
+ mBiometricView.setBiometricPromptBundle(buildBiometricPromptBundle(allowDeviceCredential));
mBiometricView.setCallback(callback);
mBiometricView.restoreState(savedState);
mBiometricView.onFinishInflateInternal();
mBiometricView.onAttachedToWindowInternal();
+
+ mBiometricView.setPanelController(mPanelController);
}
- private void initDialog(Context context, AuthBiometricView.Callback callback,
- MockInjector injector) {
- initDialog(context, callback, null /* savedState */, injector);
+ private void initDialog(Context context, boolean allowDeviceCredential,
+ AuthBiometricView.Callback callback, MockInjector injector) {
+ initDialog(context, allowDeviceCredential, callback, null /* savedState */, injector);
}
private class MockInjector extends AuthBiometricView.Injector {
@@ -338,6 +365,16 @@ public class AuthBiometricViewTest extends SysuiTestCase {
public int getDelayAfterError() {
return 0; // Keep this at 0 for tests to invoke callback immediately.
}
+
+ @Override
+ public int getMediumToLargeAnimationDurationMs() {
+ return 0;
+ }
+
+ @Override
+ public int getAnimateCredentialStartDelayMs() {
+ return 0;
+ }
}
private class TestableBiometricView extends AuthBiometricView {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
index d4fc3f842e9d..990f74ae33c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
@@ -16,12 +16,30 @@
package com.android.systemui.biometrics;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.content.Context;
+import android.hardware.biometrics.Authenticator;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ScrollView;
import com.android.systemui.SysuiTestCase;
@@ -43,22 +61,21 @@ public class AuthContainerViewTest extends SysuiTestCase {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
-
- AuthContainerView.Config config = new AuthContainerView.Config();
- config.mContext = mContext;
- config.mCallback = mCallback;
- mAuthContainer = new TestableAuthContainer(config);
}
@Test
public void testActionAuthenticated_sendsDismissedAuthenticated() {
+ initializeContainer(Authenticator.TYPE_BIOMETRIC);
+
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_AUTHENTICATED);
- verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_AUTHENTICATED));
+ verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED));
}
@Test
public void testActionUserCanceled_sendsDismissedUserCanceled() {
+ initializeContainer(Authenticator.TYPE_BIOMETRIC);
+
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_USER_CANCELED);
verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_USER_CANCELED));
@@ -66,6 +83,8 @@ public class AuthContainerViewTest extends SysuiTestCase {
@Test
public void testActionButtonNegative_sendsDismissedButtonNegative() {
+ initializeContainer(Authenticator.TYPE_BIOMETRIC);
+
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE);
verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE));
@@ -73,6 +92,8 @@ public class AuthContainerViewTest extends SysuiTestCase {
@Test
public void testActionTryAgain_sendsTryAgain() {
+ initializeContainer(Authenticator.TYPE_BIOMETRIC);
+
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN);
verify(mCallback).onTryAgainPressed();
@@ -80,14 +101,77 @@ public class AuthContainerViewTest extends SysuiTestCase {
@Test
public void testActionError_sendsDismissedError() {
+ initializeContainer(Authenticator.TYPE_BIOMETRIC);
+
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_ERROR);
verify(mCallback).onDismissed(AuthDialogCallback.DISMISSED_ERROR);
}
+ @Test
+ public void testActionUseDeviceCredential_sendsOnDeviceCredentialPressed() {
+ initializeContainer(
+ Authenticator.TYPE_BIOMETRIC | Authenticator.TYPE_CREDENTIAL);
+
+ mAuthContainer.mBiometricCallback.onAction(
+ AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL);
+ verify(mCallback).onDeviceCredentialPressed();
+
+ // Credential view is attached to the frame layout
+ waitForIdleSync();
+ assertNotNull(mAuthContainer.mCredentialView);
+ verify(mAuthContainer.mFrameLayout).addView(eq(mAuthContainer.mCredentialView));
+ }
+
+ @Test
+ public void testAnimateToCredentialUI_invokesStartTransitionToCredentialUI() {
+ initializeContainer(
+ Authenticator.TYPE_BIOMETRIC | Authenticator.TYPE_CREDENTIAL);
+
+ mAuthContainer.mBiometricView = mock(AuthBiometricView.class);
+ mAuthContainer.animateToCredentialUI();
+ verify(mAuthContainer.mBiometricView).startTransitionToCredentialUI();
+ }
+
+ @Test
+ public void testShowBiometricUI() {
+ initializeContainer(Authenticator.TYPE_BIOMETRIC);
+
+ assertNotEquals(null, mAuthContainer.mBiometricView);
+
+ mAuthContainer.onAttachedToWindowInternal();
+ verify(mAuthContainer.mBiometricScrollView).addView(mAuthContainer.mBiometricView);
+ // Credential view is not added
+ verify(mAuthContainer.mFrameLayout, never()).addView(any());
+ }
+
+ @Test
+ public void testShowCredentialUI_doesNotInflateBiometricUI() {
+ initializeContainer(Authenticator.TYPE_CREDENTIAL);
+
+ mAuthContainer.onAttachedToWindowInternal();
+
+ assertNull(null, mAuthContainer.mBiometricView);
+ assertNotNull(mAuthContainer.mCredentialView);
+ verify(mAuthContainer.mFrameLayout).addView(mAuthContainer.mCredentialView);
+ }
+
+ private void initializeContainer(int authenticators) {
+ AuthContainerView.Config config = new AuthContainerView.Config();
+ config.mContext = mContext;
+ config.mCallback = mCallback;
+ config.mModalityMask |= BiometricAuthenticator.TYPE_FINGERPRINT;
+
+ Bundle bundle = new Bundle();
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ config.mBiometricPromptBundle = bundle;
+
+ mAuthContainer = new TestableAuthContainer(config);
+ }
+
private class TestableAuthContainer extends AuthContainerView {
TestableAuthContainer(AuthContainerView.Config config) {
- super(config);
+ super(config, new MockInjector());
}
@Override
@@ -95,4 +179,32 @@ public class AuthContainerViewTest extends SysuiTestCase {
mConfig.mCallback.onDismissed(reason);
}
}
+
+ private final class MockInjector extends AuthContainerView.Injector {
+ @Override
+ public ScrollView getBiometricScrollView(FrameLayout parent) {
+ return mock(ScrollView.class);
+ }
+
+ @Override
+ public FrameLayout inflateContainerView(LayoutInflater factory, ViewGroup root) {
+ return mock(FrameLayout.class);
+ }
+
+ @Override
+ public AuthPanelController getPanelController(Context context, View view,
+ boolean isManagedProfile) {
+ return mock(AuthPanelController.class);
+ }
+
+ @Override
+ public ImageView getBackgroundView(FrameLayout parent) {
+ return mock(ImageView.class);
+ }
+
+ @Override
+ public View getPanelView(FrameLayout parent) {
+ return mock(View.class);
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index eb7be4fa6332..dcdb5c3f8e9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -22,8 +22,11 @@ import static junit.framework.TestCase.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -33,6 +36,8 @@ import android.app.IActivityTaskManager;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.hardware.biometrics.Authenticator;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
@@ -69,7 +74,7 @@ public class AuthControllerTest extends SysuiTestCase {
@Mock
private AuthDialog mDialog2;
- private TestableBiometricDialogImpl mBiometricDialogImpl;
+ private TestableAuthController mAuthController;
@Before
@@ -90,78 +95,90 @@ public class AuthControllerTest extends SysuiTestCase {
when(mDialog1.getOpPackageName()).thenReturn("Dialog1");
when(mDialog2.getOpPackageName()).thenReturn("Dialog2");
- mBiometricDialogImpl = new TestableBiometricDialogImpl(new MockInjector());
- mBiometricDialogImpl.mContext = context;
- mBiometricDialogImpl.mComponents = mContext.getComponents();
+ when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
+ when(mDialog2.isAllowDeviceCredentials()).thenReturn(false);
- mBiometricDialogImpl.start();
+ mAuthController = new TestableAuthController(new MockInjector());
+ mAuthController.mContext = context;
+ mAuthController.mComponents = mContext.getComponents();
+
+ mAuthController.start();
}
// Callback tests
@Test
public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
}
@Test
public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
}
@Test
public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE);
+ verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
}
@Test
public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onDismissed(AuthDialogCallback.DISMISSED_AUTHENTICATED);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED);
+ verify(mReceiver).onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
}
@Test
public void testSendsReasonError_whenDismissedByError() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onDismissed(AuthDialogCallback.DISMISSED_ERROR);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
}
@Test
- public void testSendsReasonDismissedBySystemServer_whenDismissedByServer() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER);
+ public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
}
+ @Test
+ public void testSendsReasonCredentialConfirmed_whenDeviceCredentialAuthenticated()
+ throws Exception {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
+ verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED);
+ }
+
// Statusbar tests
@Test
public void testShowInvoked_whenSystemRequested()
throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
verify(mDialog1).show(any(), any());
}
@Test
- public void testOnAuthenticationSucceededInvoked_whenSystemRequested() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onBiometricAuthenticated(true, null /* failureReason */);
+ public void testOnAuthenticationSucceededInvoked_whenSystemRequested() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onBiometricAuthenticated(true, null /* failureReason */);
verify(mDialog1).onAuthenticationSucceeded();
}
@Test
- public void testOnAuthenticationFailedInvoked_whenSystemRequested() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
+ public void testOnAuthenticationFailedInvoked_whenSystemRequested() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
final String failureReason = "failure reason";
- mBiometricDialogImpl.onBiometricAuthenticated(false, failureReason);
+ mAuthController.onBiometricAuthenticated(false, failureReason);
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(mDialog1).onAuthenticationFailed(captor.capture());
@@ -170,10 +187,10 @@ public class AuthControllerTest extends SysuiTestCase {
}
@Test
- public void testOnHelpInvoked_whenSystemRequested() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
+ public void testOnHelpInvoked_whenSystemRequested() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
final String helpMessage = "help";
- mBiometricDialogImpl.onBiometricHelp(helpMessage);
+ mAuthController.onBiometricHelp(helpMessage);
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(mDialog1).onHelp(captor.capture());
@@ -182,10 +199,11 @@ public class AuthControllerTest extends SysuiTestCase {
}
@Test
- public void testOnErrorInvoked_whenSystemRequested() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
+ public void testOnErrorInvoked_whenSystemRequested() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ final int error = 1;
final String errMessage = "error message";
- mBiometricDialogImpl.onBiometricError(errMessage);
+ mAuthController.onBiometricError(error, errMessage);
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(mDialog1).onError(captor.capture());
@@ -194,30 +212,82 @@ public class AuthControllerTest extends SysuiTestCase {
}
@Test
- public void testDismissWithoutCallbackInvoked_whenSystemRequested() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.hideBiometricDialog();
+ public void testErrorLockout_whenCredentialAllowed_AnimatesToCredentialUI() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
+ final String errorString = "lockout";
+
+ when(mDialog1.isAllowDeviceCredentials()).thenReturn(true);
+
+ mAuthController.onBiometricError(error, errorString);
+ verify(mDialog1, never()).onError(anyString());
+ verify(mDialog1).animateToCredentialUI();
+ }
+
+ @Test
+ public void testErrorLockoutPermanent_whenCredentialAllowed_AnimatesToCredentialUI() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ final String errorString = "lockout_permanent";
+
+ when(mDialog1.isAllowDeviceCredentials()).thenReturn(true);
+
+ mAuthController.onBiometricError(error, errorString);
+ verify(mDialog1, never()).onError(anyString());
+ verify(mDialog1).animateToCredentialUI();
+ }
+
+ @Test
+ public void testErrorLockout_whenCredentialNotAllowed_sendsOnError() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
+ final String errorString = "lockout";
+
+ when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
+
+ mAuthController.onBiometricError(error, errorString);
+ verify(mDialog1).onError(eq(errorString));
+ verify(mDialog1, never()).animateToCredentialUI();
+ }
+
+ @Test
+ public void testErrorLockoutPermanent_whenCredentialNotAllowed_sendsOnError() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ final String errorString = "lockout_permanent";
+
+ when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
+
+ mAuthController.onBiometricError(error, errorString);
+ verify(mDialog1).onError(eq(errorString));
+ verify(mDialog1, never()).animateToCredentialUI();
+ }
+
+ @Test
+ public void testDismissWithoutCallbackInvoked_whenSystemRequested() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.hideAuthenticationDialog();
verify(mDialog1).dismissFromSystemServer();
}
@Test
- public void testClientNotified_whenDismissedBySystemServer() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.hideBiometricDialog();
+ public void testClientNotified_whenDismissedBySystemServer() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.hideAuthenticationDialog();
verify(mDialog1).dismissFromSystemServer();
- assertNotNull(mBiometricDialogImpl.mCurrentDialog);
- assertNotNull(mBiometricDialogImpl.mReceiver);
+ assertNotNull(mAuthController.mCurrentDialog);
+ assertNotNull(mAuthController.mReceiver);
}
// Corner case tests
@Test
- public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
+ public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
verify(mDialog1).show(any(), any());
- showDialog(BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
// First dialog should be dismissed without animation
verify(mDialog1).dismissWithoutCallback(eq(false) /* animate */);
@@ -227,11 +297,20 @@ public class AuthControllerTest extends SysuiTestCase {
}
@Test
- public void testConfigurationPersists_whenOnConfigurationChanged() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
+ public void testConfigurationPersists_whenOnConfigurationChanged() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
verify(mDialog1).show(any(), any());
- mBiometricDialogImpl.onConfigurationChanged(new Configuration());
+ // Return that the UI is in "showing" state
+ doAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ Bundle savedState = (Bundle) args[0];
+ savedState.putInt(
+ AuthDialog.KEY_CONTAINER_STATE, AuthContainerView.STATE_SHOWING);
+ return null; // onSaveState returns void
+ }).when(mDialog1).onSaveState(any());
+
+ mAuthController.onConfigurationChanged(new Configuration());
ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(mDialog1).onSaveState(captor.capture());
@@ -248,37 +327,63 @@ public class AuthControllerTest extends SysuiTestCase {
}
@Test
+ public void testConfigurationPersists_whenBiometricFallbackToCredential() {
+ showDialog(Authenticator.TYPE_CREDENTIAL | Authenticator.TYPE_BIOMETRIC,
+ BiometricPrompt.TYPE_FACE);
+ verify(mDialog1).show(any(), any());
+
+ // Pretend that the UI is now showing device credential UI.
+ doAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ Bundle savedState = (Bundle) args[0];
+ savedState.putInt(
+ AuthDialog.KEY_CONTAINER_STATE, AuthContainerView.STATE_SHOWING);
+ savedState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, true);
+ return null; // onSaveState returns void
+ }).when(mDialog1).onSaveState(any());
+
+ mAuthController.onConfigurationChanged(new Configuration());
+
+ // Check that the new dialog was initialized to the credential UI.
+ ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mDialog2).show(any(), captor.capture());
+ assertEquals(Authenticator.TYPE_CREDENTIAL,
+ mAuthController.mLastBiometricPromptBundle
+ .getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+ }
+
+ @Test
public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>();
ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
taskInfo.topActivity = mock(ComponentName.class);
when(taskInfo.topActivity.getPackageName()).thenReturn("other_package");
tasks.add(taskInfo);
- when(mBiometricDialogImpl.mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
+ when(mAuthController.mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
- mBiometricDialogImpl.mTaskStackListener.onTaskStackChanged();
+ mAuthController.mTaskStackListener.onTaskStackChanged();
waitForIdleSync();
- assertNull(mBiometricDialogImpl.mCurrentDialog);
- assertNull(mBiometricDialogImpl.mReceiver);
+ assertNull(mAuthController.mCurrentDialog);
+ assertNull(mAuthController.mReceiver);
verify(mDialog1).dismissWithoutCallback(true /* animate */);
verify(mReceiver).onDialogDismissed(eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL));
}
// Helpers
- private void showDialog(int type) {
- mBiometricDialogImpl.showBiometricDialog(createTestDialogBundle(),
+ private void showDialog(int authenticators, int biometricModality) {
+ mAuthController.showAuthenticationDialog(createTestDialogBundle(authenticators),
mReceiver /* receiver */,
- type,
+ biometricModality,
true /* requireConfirmation */,
0 /* userId */,
"testPackage");
}
- private Bundle createTestDialogBundle() {
+ private Bundle createTestDialogBundle(int authenticators) {
Bundle bundle = new Bundle();
bundle.putCharSequence(BiometricPrompt.KEY_TITLE, "Title");
@@ -290,13 +395,16 @@ public class AuthControllerTest extends SysuiTestCase {
// by user settings, and should be tested in BiometricService.
bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true);
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+
return bundle;
}
- private final class TestableBiometricDialogImpl extends AuthController {
+ private final class TestableAuthController extends AuthController {
private int mBuildCount = 0;
+ private Bundle mLastBiometricPromptBundle;
- public TestableBiometricDialogImpl(Injector injector) {
+ public TestableAuthController(Injector injector) {
super(injector);
}
@@ -304,6 +412,9 @@ public class AuthControllerTest extends SysuiTestCase {
protected AuthDialog buildDialog(Bundle biometricPromptBundle,
boolean requireConfirmation, int userId, int type, String opPackageName,
boolean skipIntro) {
+
+ mLastBiometricPromptBundle = biometricPromptBundle;
+
AuthDialog dialog;
if (mBuildCount == 0) {
dialog = mDialog1;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index b252a0d95f94..1bd01e166ddb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -367,12 +367,13 @@ public class CommandQueueTest extends SysuiTestCase {
}
@Test
- public void testShowBiometricDialog() {
+ public void testShowAuthenticationDialog() {
Bundle bundle = new Bundle();
String packageName = "test";
- mCommandQueue.showBiometricDialog(bundle, null /* receiver */, 1, true, 3, packageName);
+ mCommandQueue.showAuthenticationDialog(bundle, null /* receiver */, 1, true, 3,
+ packageName);
waitForIdleSync();
- verify(mCallbacks).showBiometricDialog(eq(bundle), eq(null), eq(1), eq(true), eq(3),
+ verify(mCallbacks).showAuthenticationDialog(eq(bundle), eq(null), eq(1), eq(true), eq(3),
eq(packageName));
}
@@ -394,16 +395,17 @@ public class CommandQueueTest extends SysuiTestCase {
@Test
public void testOnBiometricError() {
+ final int errorCode = 1;
String errorMessage = "test_error_message";
- mCommandQueue.onBiometricError(errorMessage);
+ mCommandQueue.onBiometricError(errorCode, errorMessage);
waitForIdleSync();
- verify(mCallbacks).onBiometricError(eq(errorMessage));
+ verify(mCallbacks).onBiometricError(eq(errorCode), eq(errorMessage));
}
@Test
- public void testHideBiometricDialog() {
- mCommandQueue.hideBiometricDialog();
+ public void testHideAuthenticationDialog() {
+ mCommandQueue.hideAuthenticationDialog();
waitForIdleSync();
- verify(mCallbacks).hideBiometricDialog();
+ verify(mCallbacks).hideAuthenticationDialog();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 30e02e6b46d2..52f7e679b49c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -41,7 +41,6 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.Handler;
@@ -145,8 +144,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
private class TestableNotificationEntryManager extends NotificationEntryManager {
private final CountDownLatch mCountDownLatch;
- TestableNotificationEntryManager(Context context) {
- super(context);
+ TestableNotificationEntryManager() {
+ super(new NotificationData(mock(NotificationSectionsFeatureManager.class)));
mCountDownLatch = new CountDownLatch(1);
}
@@ -250,7 +249,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntry.expandedIcon = mock(StatusBarIconView.class);
- mEntryManager = new TestableNotificationEntryManager(mContext);
+ mEntryManager = new TestableNotificationEntryManager();
Dependency.get(InitController.class).executePostInitTasks();
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
mEntryManager.addNotificationEntryListener(mEntryListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
index 8d496a72e3b2..6d275419ee94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
@@ -46,6 +46,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import com.android.systemui.util.DeviceConfigProxyFake;
import org.junit.Before;
import org.junit.Test;
@@ -73,7 +74,9 @@ public class NotificationListControllerTest extends SysuiTestCase {
private DeviceProvisionedListener mProvisionedListener;
// TODO: Remove this once EntryManager no longer needs to be mocked
- private NotificationData mNotificationData = new NotificationData(mContext);
+ private NotificationData mNotificationData =
+ new NotificationData(new NotificationSectionsFeatureManager(
+ new DeviceConfigProxyFake(), mContext));
private int mNextNotifId = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
new file mode 100644
index 000000000000..b3d0d22445b6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import android.provider.DeviceConfig
+import android.provider.Settings
+import android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL
+import android.testing.AndroidTestingRunner
+
+import androidx.test.filters.SmallTest
+
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.DeviceConfigProxyFake
+
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class NotificationSectionsFeatureManagerTest : SysuiTestCase() {
+ var manager: NotificationSectionsFeatureManager? = null
+ val proxyFake = DeviceConfigProxyFake()
+
+ @Before
+ public fun setup() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ NOTIFICATION_NEW_INTERRUPTION_MODEL, 1)
+ manager = NotificationSectionsFeatureManager(proxyFake, mContext)
+ manager!!.clearCache()
+ }
+
+ @Test
+ public fun testPeopleFilteringOff_newInterruptionModelOn() {
+ proxyFake.setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, "false", false)
+
+ assertFalse("People filtering should be disabled", manager!!.isFilteringEnabled())
+ assertTrue("Expecting 2 buckets when people filtering is disabled",
+ manager!!.getNumberOfBuckets() == 2)
+ }
+
+ @Test
+ public fun testPeopleFilteringOn_newInterruptionModelOn() {
+ proxyFake.setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, "true", false)
+
+ assertTrue("People filtering should be enabled", manager!!.isFilteringEnabled())
+ assertTrue("Expecting 3 buckets when people filtering is enabled",
+ manager!!.getNumberOfBuckets() == 3)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index 657ec61dfd11..5fbacb1d7adf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -50,7 +50,6 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Person;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -78,6 +77,7 @@ import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -139,7 +139,8 @@ public class NotificationDataTest extends SysuiTestCase {
mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mNotificationData = new TestableNotificationData(mContext);
+ mNotificationData = new TestableNotificationData(
+ mock(NotificationSectionsFeatureManager.class));
mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
mRow = new NotificationTestHelper(getContext()).createRow();
Dependency.get(InitController.class).executePostInitTasks();
@@ -631,8 +632,8 @@ public class NotificationDataTest extends SysuiTestCase {
}
public static class TestableNotificationData extends NotificationData {
- public TestableNotificationData(Context context) {
- super(context);
+ public TestableNotificationData(NotificationSectionsFeatureManager sectionsFeatureManager) {
+ super(sectionsFeatureManager);
}
public static final String OVERRIDE_RANK = "r";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index addceb5def6e..3f467eae1d57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -32,10 +32,12 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.util.DeviceConfigProxy;
import org.junit.Assert;
import org.junit.Before;
@@ -64,7 +66,9 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mRoundnessManager = new NotificationRoundnessManager(mBypassController, mContext);
+ mRoundnessManager = new NotificationRoundnessManager(
+ mBypassController,
+ new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext));
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
mFirst = testHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 31054260eb15..11ae0cc34787 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -55,7 +55,6 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -65,6 +64,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -79,6 +79,7 @@ import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarTest.TestableNotificationEntryManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.DeviceConfigProxyFake;
import org.junit.After;
import org.junit.Before;
@@ -144,11 +145,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mDependency.injectMockDependency(ShadeController.class);
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
- mEntryManager = new TestableNotificationEntryManager(mContext);
+ mEntryManager = new TestableNotificationEntryManager(mNotificationData);
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
Dependency.get(InitController.class).executePostInitTasks();
- mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager,
- mNotificationData);
+ mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager);
NotificationShelf notificationShelf = mock(NotificationShelf.class);
@@ -166,7 +166,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mock(SysuiStatusBarStateController.class),
mHeadsUpManager,
mKeyguardBypassController,
- new FalsingManagerFake());
+ new FalsingManagerFake(),
+ new NotificationSectionsFeatureManager(new DeviceConfigProxyFake(), mContext));
mStackScroller = spy(mStackScrollerInternal);
mStackScroller.setShelf(notificationShelf);
mStackScroller.setStatusBar(mBar);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 144935212c27..2631ced77972 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -236,7 +236,7 @@ public class StatusBarTest extends SysuiTestCase {
mMetricsLogger = new FakeMetricsLogger();
TestableNotificationEntryManager entryManager = new TestableNotificationEntryManager(
- mContext);
+ mNotificationData);
NotificationLogger notificationLogger = new NotificationLogger(mNotificationListener,
Dependency.get(UiOffloadThread.class), entryManager, mStatusBarStateController,
mExpansionStateLogger);
@@ -354,7 +354,7 @@ public class StatusBarTest extends SysuiTestCase {
mStatusBar.putComponent(StatusBar.class, mStatusBar);
Dependency.get(InitController.class).executePostInitTasks();
entryManager.setUpForTest(mock(NotificationPresenter.class), mStackScroller,
- mHeadsUpManager, mNotificationData);
+ mHeadsUpManager);
entryManager.addNotificationEntryListener(mEntryListener);
notificationLogger.setUpWithContainer(mStackScroller);
}
@@ -865,16 +865,14 @@ public class StatusBarTest extends SysuiTestCase {
public static class TestableNotificationEntryManager extends NotificationEntryManager {
- public TestableNotificationEntryManager(Context context) {
- super(context);
+ public TestableNotificationEntryManager(NotificationData notificationData) {
+ super(notificationData);
}
public void setUpForTest(NotificationPresenter presenter,
NotificationListContainer listContainer,
- HeadsUpManagerPhone headsUpManager,
- NotificationData notificationData) {
+ HeadsUpManagerPhone headsUpManager) {
super.setUpWithPresenter(presenter, listContainer, headsUpManager);
- mNotificationData = notificationData;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 4f021ad3cee0..514eb77c3201 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -350,9 +350,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
protected abstract boolean hasRightsToCurrentUserLocked();
+ @Nullable
@Override
- public List<AccessibilityWindowInfo> getWindows() {
- ensureWindowsAvailableTimed(Display.DEFAULT_DISPLAY);
+ public AccessibilityWindowInfo.WindowListSparseArray getWindows() {
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return null;
@@ -362,38 +362,39 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
if (!permissionGranted) {
return null;
}
- List<AccessibilityWindowInfo> internalWindowList =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- if (internalWindowList == null) {
- return null;
- }
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return null;
}
- List<AccessibilityWindowInfo> returnedWindowList = new ArrayList<>();
- final int windowCount = internalWindowList.size();
- for (int i = 0; i < windowCount; i++) {
- AccessibilityWindowInfo window = internalWindowList.get(i);
- AccessibilityWindowInfo windowClone =
- AccessibilityWindowInfo.obtain(window);
- windowClone.setConnectionId(mId);
- returnedWindowList.add(windowClone);
+ final AccessibilityWindowInfo.WindowListSparseArray allWindows =
+ new AccessibilityWindowInfo.WindowListSparseArray();
+ final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked();
+ final int displayListCounts = displayList.size();
+ if (displayListCounts > 0) {
+ for (int i = 0; i < displayListCounts; i++) {
+ final int displayId = displayList.get(i);
+ ensureWindowsAvailableTimedLocked(displayId);
+
+ final List<AccessibilityWindowInfo> windowList = getWindowsByDisplayLocked(
+ displayId);
+ if (windowList != null) {
+ allWindows.put(displayId, windowList);
+ }
+ }
}
- return returnedWindowList;
+ return allWindows;
}
}
@Override
public AccessibilityWindowInfo getWindow(int windowId) {
- int displayId = Display.INVALID_DISPLAY;
synchronized (mLock) {
+ int displayId = Display.INVALID_DISPLAY;
if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked(
mSystemSupport.getCurrentUserIdLocked(), windowId);
}
- }
- ensureWindowsAvailableTimed(displayId);
- synchronized (mLock) {
+ ensureWindowsAvailableTimedLocked(displayId);
+
if (!hasRightsToCurrentUserLocked()) {
return null;
}
@@ -1316,35 +1317,33 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
*
* @param displayId The logical display id.
*/
- private void ensureWindowsAvailableTimed(int displayId) {
- synchronized (mLock) {
- if (mA11yWindowManager.getWindowListLocked(displayId) != null) {
- return;
- }
- // If we have no registered callback, update the state we
- // we may have to register one but it didn't happen yet.
- if (!mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
- // Invokes client change to make sure tracking window enabled.
- mSystemSupport.onClientChangeLocked(false);
- }
- // We have no windows but do not care about them, done.
- if (!mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
+ private void ensureWindowsAvailableTimedLocked(int displayId) {
+ if (mA11yWindowManager.getWindowListLocked(displayId) != null) {
+ return;
+ }
+ // If we have no registered callback, update the state we
+ // we may have to register one but it didn't happen yet.
+ if (!mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
+ // Invokes client change to make sure tracking window enabled.
+ mSystemSupport.onClientChangeLocked(false);
+ }
+ // We have no windows but do not care about them, done.
+ if (!mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
+ return;
+ }
+
+ // Wait for the windows with a timeout.
+ final long startMillis = SystemClock.uptimeMillis();
+ while (mA11yWindowManager.getWindowListLocked(displayId) == null) {
+ final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
+ final long remainMillis = WAIT_WINDOWS_TIMEOUT_MILLIS - elapsedMillis;
+ if (remainMillis <= 0) {
return;
}
-
- // Wait for the windows with a timeout.
- final long startMillis = SystemClock.uptimeMillis();
- while (mA11yWindowManager.getWindowListLocked(displayId) == null) {
- final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
- final long remainMillis = WAIT_WINDOWS_TIMEOUT_MILLIS - elapsedMillis;
- if (remainMillis <= 0) {
- return;
- }
- try {
- mLock.wait(remainMillis);
- } catch (InterruptedException ie) {
- /* ignore */
- }
+ try {
+ mLock.wait(remainMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
}
}
}
@@ -1442,6 +1441,24 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
interrogatingPid, interrogatingTid);
}
+ private List<AccessibilityWindowInfo> getWindowsByDisplayLocked(int displayId) {
+ final List<AccessibilityWindowInfo> internalWindowList =
+ mA11yWindowManager.getWindowListLocked(displayId);
+ if (internalWindowList == null) {
+ return null;
+ }
+ final List<AccessibilityWindowInfo> returnedWindowList = new ArrayList<>();
+ final int windowCount = internalWindowList.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = internalWindowList.get(i);
+ AccessibilityWindowInfo windowClone =
+ AccessibilityWindowInfo.obtain(window);
+ windowClone.setConnectionId(mId);
+ returnedWindowList.add(windowClone);
+ }
+ return returnedWindowList;
+ }
+
public ComponentName getComponentName() {
return mComponentName;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 0038a27db384..cb858ac11b00 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -1472,11 +1472,27 @@ public class AccessibilityWindowManager {
public int getDisplayIdByUserIdAndWindowIdLocked(int userId, int windowId) {
final IBinder windowToken = getWindowTokenForUserAndWindowIdLocked(userId, windowId);
final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken);
-
return displayId;
}
/**
+ * Returns the display list including all displays which are tracking windows.
+ *
+ * @return The display list.
+ */
+ public ArrayList<Integer> getDisplayListLocked() {
+ final ArrayList<Integer> displayList = new ArrayList<>();
+ final int count = mDisplayWindowsObservers.size();
+ for (int i = 0; i < count; i++) {
+ final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
+ if (observer != null) {
+ displayList.add(observer.mDisplayId);
+ }
+ }
+ return displayList;
+ }
+
+ /**
* Gets current input focused window token from window manager, and returns its windowId.
*
* @param userId The userId
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
index 7e8fb295d036..3dfe59e142a6 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
@@ -408,9 +408,6 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen
cancelGesture();
}
- public boolean firstTapDetected() {
- return mFirstTapDetected;
- }
@Override
public void onLongPress(MotionEvent e) {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index c60e35e2cc6d..b62e260aacad 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -411,24 +411,12 @@ public class TouchExplorer extends BaseEventStreamTransformation
// we resent the delayed callback and wait again.
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
-
// If a touch exploration gesture is in progress send events for its end.
if (mState.isTouchExploring()) {
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
}
- // Avoid duplicated TYPE_TOUCH_INTERACTION_START event when 2nd tap of double
- // tap.
- if (!mGestureDetector.firstTapDetected() && mState.isClear()) {
- mSendTouchExplorationEndDelayed.forceSendAndRemove();
- mSendTouchInteractionEndDelayed.forceSendAndRemove();
- mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
- } else {
- // Let gesture to handle to avoid duplicated TYPE_TOUCH_INTERACTION_END event.
- mSendTouchInteractionEndDelayed.cancel();
- }
-
- if (!mGestureDetector.firstTapDetected() && !mState.isTouchExploring()) {
+ if (mState.isClear()) {
if (!mSendHoverEnterAndMoveDelayed.isPending()) {
// Queue a delayed transition to STATE_TOUCH_EXPLORING.
// If we do not detect that this is a gesture, delegation or drag the transition
@@ -441,6 +429,12 @@ public class TouchExplorer extends BaseEventStreamTransformation
// Cache the event until we discern exploration from gesturing.
mSendHoverEnterAndMoveDelayed.addEvent(event, rawEvent);
}
+ mSendTouchExplorationEndDelayed.forceSendAndRemove();
+ mSendTouchInteractionEndDelayed.forceSendAndRemove();
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
+ } else {
+ // Avoid duplicated TYPE_TOUCH_INTERACTION_START event when 2nd tap of double tap.
+ mSendTouchInteractionEndDelayed.cancel();
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index f91cf0cc1255..0f8a3b54acc5 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -274,8 +274,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE;
static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
- PhoneStateListener.LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER
- | PhoneStateListener.LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER;
+ PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
+ | PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS;
private static final int MSG_USER_SWITCHED = 1;
private static final int MSG_UPDATE_DEFAULT_SUB = 2;
@@ -1932,7 +1932,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mOutgoingCallEmergencyNumber[phoneId] = emergencyNumber;
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER)
+ PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onOutgoingEmergencyCall(emergencyNumber);
@@ -1957,7 +1957,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mOutgoingSmsEmergencyNumber[phoneId] = emergencyNumber;
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER)
+ PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onOutgoingEmergencySms(emergencyNumber);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ee98af4020ad..7cbd1fcebb94 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9736,6 +9736,10 @@ public class ActivityManagerService extends IActivityManager.Stub
sb.append("Foreground: ")
.append(process.isInterestingToUserLocked() ? "Yes" : "No")
.append("\n");
+ if (process.startTime > 0) {
+ long runtimeMillis = SystemClock.elapsedRealtime() - process.startTime;
+ sb.append("Process-Runtime: ").append(runtimeMillis).append("\n");
+ }
}
if (activityShortComponentName != null) {
sb.append("Activity: ").append(activityShortComponentName).append("\n");
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index 21d4925722d0..bc4707f04724 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -33,7 +33,6 @@
},
{
"name": "FrameworksMockingServicesTests",
- "file_patterns": ["AppCompactor\\.java"],
"options": [
{
"include-filter": "com.android.server.am."
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 8e4474c462ab..175419117898 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1184,7 +1184,7 @@ class UserController implements Handler.Callback {
updateStartedUserArrayLU();
}
needStart = true;
- t.traceBegin("updateStateStopping");
+ t.traceEnd();
}
if (uss.state == UserState.STATE_BOOTING) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 3d341ef67d6f..4f1db3c96faf 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -26,19 +26,17 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import android.app.ActivityManager;
import android.app.IActivityManager;
-import android.app.KeyguardManager;
import android.app.UserSwitchObserver;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
+import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -95,11 +93,8 @@ public class BiometricService extends SystemService {
private static final int MSG_ON_READY_FOR_AUTHENTICATION = 8;
private static final int MSG_AUTHENTICATE = 9;
private static final int MSG_CANCEL_AUTHENTICATION = 10;
- private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS = 11;
- private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR = 12;
- private static final int MSG_REGISTER_CANCELLATION_CALLBACK = 13;
- private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 14;
-
+ private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 11;
+ private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12;
private static final int[] FEATURE_ID = {
TYPE_FINGERPRINT,
TYPE_IRIS,
@@ -132,19 +127,19 @@ public class BiometricService extends SystemService {
*/
static final int STATE_AUTH_PENDING_CONFIRM = 5;
/**
- * Biometric authentication was canceled, but the device is now showing ConfirmDeviceCredential
- */
- static final int STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC = 6;
- /**
* Biometric authenticated, waiting for SysUI to finish animation
*/
- static final int STATE_AUTHENTICATED_PENDING_SYSUI = 7;
+ static final int STATE_AUTHENTICATED_PENDING_SYSUI = 6;
/**
* Biometric error, waiting for SysUI to finish animation
*/
- static final int STATE_ERROR_PENDING_SYSUI = 8;
+ static final int STATE_ERROR_PENDING_SYSUI = 7;
+ /**
+ * Device credential in AuthController is showing
+ */
+ static final int STATE_SHOWING_DEVICE_CREDENTIAL = 8;
- final class AuthSession implements IBinder.DeathRecipient {
+ final class AuthSession {
// Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
// <Biometric>Services before we can start authenticating. Pairs that have been returned
// are moved to mModalitiesMatched.
@@ -184,14 +179,10 @@ public class BiometricService extends SystemService {
// Timestamp when hardware authentication occurred
private long mAuthenticatedTimeMs;
- // TODO(b/123378871): Remove when moved.
- private IBiometricConfirmDeviceCredentialCallback mConfirmDeviceCredentialCallback;
-
AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
int userId, IBiometricServiceReceiver receiver, String opPackageName,
Bundle bundle, int callingUid, int callingPid, int callingUserId,
- int modality, boolean requireConfirmation,
- IBiometricConfirmDeviceCredentialCallback callback) {
+ int modality, boolean requireConfirmation) {
mModalitiesWaiting = modalities;
mToken = token;
mSessionId = sessionId;
@@ -204,25 +195,12 @@ public class BiometricService extends SystemService {
mCallingUserId = callingUserId;
mModality = modality;
mRequireConfirmation = requireConfirmation;
- mConfirmDeviceCredentialCallback = callback;
-
- if (isFromConfirmDeviceCredential()) {
- try {
- token.linkToDeath(this, 0 /* flags */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to link to death", e);
- }
- }
}
boolean isCrypto() {
return mSessionId != 0;
}
- boolean isFromConfirmDeviceCredential() {
- return mBundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
- }
-
boolean containsCookie(int cookie) {
if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) {
return true;
@@ -233,23 +211,8 @@ public class BiometricService extends SystemService {
return false;
}
- // TODO(b/123378871): Remove when moved.
- @Override
- public void binderDied() {
- mHandler.post(() -> {
- Slog.e(TAG, "Binder died, killing ConfirmDeviceCredential");
- if (mConfirmDeviceCredentialCallback == null) {
- Slog.e(TAG, "Callback is null");
- return;
- }
-
- try {
- mConfirmDeviceCredentialCallback.cancel();
- mConfirmDeviceCredentialCallback = null;
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send cancel", e);
- }
- });
+ boolean isAllowDeviceCredential() {
+ return Utils.isDeviceCredentialAllowed(mBundle);
}
}
@@ -275,7 +238,7 @@ public class BiometricService extends SystemService {
// Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
// polymorphism :/
- final ArrayList<Authenticator> mAuthenticators = new ArrayList<>();
+ final ArrayList<AuthenticatorWrapper> mAuthenticators = new ArrayList<>();
// The current authentication session, null if idle/done. We need to track both the current
// and pending sessions since errors may be sent to either.
@@ -284,14 +247,6 @@ public class BiometricService extends SystemService {
@VisibleForTesting
AuthSession mPendingAuthSession;
- // TODO(b/123378871): Remove when moved.
- // When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the
- // client (app) receiver. BiometricService internally launches CDCA which invokes
- // BiometricService to start authentication (normal path). When auth is success/rejected,
- // CDCA will use an aidl method to poke BiometricService - the result will then be forwarded
- // to this receiver.
- private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver;
-
@VisibleForTesting
final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -361,8 +316,7 @@ public class BiometricService extends SystemService {
(Bundle) args.arg5 /* bundle */,
args.argi2 /* callingUid */,
args.argi3 /* callingPid */,
- args.argi4 /* callingUserId */,
- (IBiometricConfirmDeviceCredentialCallback) args.arg6 /* callback */);
+ args.argi4 /* callingUserId */);
args.recycle();
break;
}
@@ -376,28 +330,13 @@ public class BiometricService extends SystemService {
break;
}
- case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS: {
- handleOnConfirmDeviceCredentialSuccess();
- break;
- }
-
- case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR: {
- SomeArgs args = (SomeArgs) msg.obj;
- handleOnConfirmDeviceCredentialError(
- args.argi1 /* error */,
- (String) args.arg1 /* errorMsg */);
- args.recycle();
+ case MSG_ON_AUTHENTICATION_TIMED_OUT: {
+ handleAuthenticationTimedOut((String) msg.obj /* errorMessage */);
break;
}
- case MSG_REGISTER_CANCELLATION_CALLBACK: {
- handleRegisterCancellationCallback(
- (IBiometricConfirmDeviceCredentialCallback) msg.obj /* callback */);
- break;
- }
-
- case MSG_ON_AUTHENTICATION_TIMED_OUT: {
- handleAuthenticationTimedOut((String) msg.obj /* errorMessage */);
+ case MSG_ON_DEVICE_CREDENTIAL_PRESSED: {
+ handleOnDeviceCredentialPressed();
break;
}
@@ -408,11 +347,11 @@ public class BiometricService extends SystemService {
}
};
- private final class Authenticator {
+ private final class AuthenticatorWrapper {
final int mType;
final BiometricAuthenticator mAuthenticator;
- Authenticator(int type, BiometricAuthenticator authenticator) {
+ AuthenticatorWrapper(int type, BiometricAuthenticator authenticator) {
mType = type;
mAuthenticator = authenticator;
}
@@ -620,6 +559,11 @@ public class BiometricService extends SystemService {
public void onTryAgainPressed() {
mHandler.sendEmptyMessage(MSG_ON_TRY_AGAIN_PRESSED);
}
+
+ @Override
+ public void onDeviceCredentialPressed() {
+ mHandler.sendEmptyMessage(MSG_ON_DEVICE_CREDENTIAL_PRESSED);
+ }
};
@@ -642,18 +586,12 @@ public class BiometricService extends SystemService {
@Override // Binder call
public void authenticate(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
- IBiometricConfirmDeviceCredentialCallback callback)
+ IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle)
throws RemoteException {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
- // TODO(b/123378871): Remove when moved.
- if (callback != null) {
- checkInternalPermission();
- }
-
// In the BiometricServiceBase, check do the AppOps and foreground check.
if (userId == callingUserId) {
// Check the USE_BIOMETRIC permission here.
@@ -670,12 +608,12 @@ public class BiometricService extends SystemService {
return;
}
- final boolean isFromConfirmDeviceCredential =
- bundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
- if (isFromConfirmDeviceCredential) {
+ if (bundle.get(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED) != null) {
checkInternalPermission();
}
+ Utils.combineAuthenticatorBundles(bundle);
+
// Check the usage of this in system server. Need to remove this check if it becomes
// a public API.
final boolean useDefaultTitle =
@@ -689,39 +627,6 @@ public class BiometricService extends SystemService {
}
}
- // Launch CDC instead if necessary. CDC will return results through an AIDL call, since
- // we can't get activity results. Store the receiver somewhere so we can forward the
- // result back to the client.
- // TODO(b/123378871): Remove when moved.
- if (bundle.getBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL)) {
- mHandler.post(() -> {
- final KeyguardManager kgm = getContext().getSystemService(
- KeyguardManager.class);
- if (!kgm.isDeviceSecure()) {
- try {
- receiver.onError(
- BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
- getContext().getString(
- R.string.biometric_error_device_not_secured));
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- return;
- }
- mConfirmDeviceCredentialReceiver = receiver;
- // Use this so we don't need to duplicate logic..
- final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */,
- null /* description */, userId);
- // Then give it the bundle to do magic behavior..
- intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle);
- // Create a new task with this activity located at the root.
- intent.setFlags(
- Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- getContext().startActivityAsUser(intent, UserHandle.CURRENT);
- });
- return;
- }
-
SomeArgs args = SomeArgs.obtain();
args.arg1 = token;
args.arg2 = sessionId;
@@ -732,41 +637,11 @@ public class BiometricService extends SystemService {
args.argi2 = callingUid;
args.argi3 = callingPid;
args.argi4 = callingUserId;
- args.arg6 = callback;
mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget();
}
@Override // Binder call
- public void onConfirmDeviceCredentialSuccess() {
- checkInternalPermission();
-
- mHandler.sendEmptyMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS);
- }
-
- @Override // Binder call
- public void onConfirmDeviceCredentialError(int error, String message) {
- checkInternalPermission();
-
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = error;
- args.arg1 = message;
- mHandler.obtainMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR, args).sendToTarget();
- }
-
- @Override // Binder call
- public void registerCancellationCallback(
- IBiometricConfirmDeviceCredentialCallback callback) {
- // TODO(b/123378871): Remove when moved.
- // This callback replaces the one stored in the current session. If the session is null
- // we can ignore this, since it means ConfirmDeviceCredential was launched by something
- // else (not BiometricPrompt)
- checkInternalPermission();
-
- mHandler.obtainMessage(MSG_REGISTER_CANCELLATION_CALLBACK, callback).sendToTarget();
- }
-
- @Override // Binder call
public void cancelAuthentication(IBinder token, String opPackageName)
throws RemoteException {
checkPermission();
@@ -972,8 +847,8 @@ public class BiometricService extends SystemService {
// Cache the authenticators
for (int featureId : FEATURE_ID) {
if (hasFeature(featureId)) {
- Authenticator authenticator =
- new Authenticator(featureId, getAuthenticator(featureId));
+ AuthenticatorWrapper authenticator =
+ new AuthenticatorWrapper(featureId, getAuthenticator(featureId));
mAuthenticators.add(authenticator);
}
}
@@ -1011,7 +886,7 @@ public class BiometricService extends SystemService {
int modality = TYPE_NONE;
int firstHwAvailable = TYPE_NONE;
- for (Authenticator authenticatorWrapper : mAuthenticators) {
+ for (AuthenticatorWrapper authenticatorWrapper : mAuthenticators) {
modality = authenticatorWrapper.getType();
BiometricAuthenticator authenticator = authenticatorWrapper.getAuthenticator();
if (authenticator.isHardwareDetected()) {
@@ -1108,7 +983,7 @@ public class BiometricService extends SystemService {
}
private void logDialogDismissed(int reason) {
- if (reason == BiometricPrompt.DISMISSED_REASON_CONFIRMED) {
+ if (reason == BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED) {
// Explicit auth, authentication confirmed.
// Latency in this case is authenticated -> confirmed. <Biometric>Service
// should have the first half (first acquired -> authenticated).
@@ -1254,49 +1129,6 @@ public class BiometricService extends SystemService {
}
}
- private void handleOnConfirmDeviceCredentialSuccess() {
- if (mConfirmDeviceCredentialReceiver == null) {
- Slog.w(TAG, "handleOnConfirmDeviceCredentialSuccess null!");
- return;
- }
- try {
- mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded();
- if (mCurrentAuthSession != null) {
- mCurrentAuthSession = null;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException", e);
- }
- mConfirmDeviceCredentialReceiver = null;
- }
-
- private void handleOnConfirmDeviceCredentialError(int error, String message) {
- if (mConfirmDeviceCredentialReceiver == null) {
- Slog.w(TAG, "handleOnConfirmDeviceCredentialError null! Error: "
- + error + " " + message);
- return;
- }
- try {
- mConfirmDeviceCredentialReceiver.onError(error, message);
- if (mCurrentAuthSession != null) {
- mCurrentAuthSession = null;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException", e);
- }
- mConfirmDeviceCredentialReceiver = null;
- }
-
- private void handleRegisterCancellationCallback(
- IBiometricConfirmDeviceCredentialCallback callback) {
- if (mCurrentAuthSession == null) {
- Slog.d(TAG, "Current auth session null");
- return;
- }
- Slog.d(TAG, "Updating cancel callback");
- mCurrentAuthSession.mConfirmDeviceCredentialCallback = callback;
- }
-
private void handleOnError(int cookie, int error, String message) {
Slog.d(TAG, "handleOnError: " + error + " cookie: " + cookie);
// Errors can either be from the current auth session or the pending auth session.
@@ -1307,34 +1139,34 @@ public class BiometricService extends SystemService {
// of their intended receivers.
try {
if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
-
mCurrentAuthSession.mErrorEscrow = error;
mCurrentAuthSession.mErrorStringEscrow = message;
- if (mCurrentAuthSession.isFromConfirmDeviceCredential()) {
- // If we were invoked by ConfirmDeviceCredential, do not delete the current
- // auth session since we still need to respond to cancel signal while
- if (DEBUG) Slog.d(TAG, "From CDC, transition to CANCELED_SHOWING_CDC state");
-
- // Send the error to ConfirmDeviceCredential so that it goes to Pin/Pattern/Pass
- // screen
- mCurrentAuthSession.mClientReceiver.onError(error, message);
- mCurrentAuthSession.mState = STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC;
- mStatusBarService.hideBiometricDialog();
- } else if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
- mCurrentAuthSession.mState = STATE_ERROR_PENDING_SYSUI;
- if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
- mStatusBarService.hideBiometricDialog();
+ if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
+ final boolean errorLockout = error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
+ || error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ if (mCurrentAuthSession.isAllowDeviceCredential() && errorLockout) {
+ // SystemUI handles transition from biometric to device credential.
+ mCurrentAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
+ mStatusBarService.onBiometricError(error, message);
} else {
- mStatusBarService.onBiometricError(message);
+ mCurrentAuthSession.mState = STATE_ERROR_PENDING_SYSUI;
+ if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ mStatusBarService.hideAuthenticationDialog();
+ } else {
+ mStatusBarService.onBiometricError(error, message);
+ }
}
} else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
// In the "try again" state, we should forward canceled errors to
// the client and and clean up. The only error we should get here is
// ERROR_CANCELED due to another client kicking us out.
mCurrentAuthSession.mClientReceiver.onError(error, message);
- mStatusBarService.hideBiometricDialog();
+ mStatusBarService.hideAuthenticationDialog();
mCurrentAuthSession = null;
+ } else if (mCurrentAuthSession.mState == STATE_SHOWING_DEVICE_CREDENTIAL) {
+ Slog.d(TAG, "Biometric canceled, ignoring from state: "
+ + mCurrentAuthSession.mState);
} else {
Slog.e(TAG, "Impossible session error state: "
+ mCurrentAuthSession.mState);
@@ -1342,12 +1174,38 @@ public class BiometricService extends SystemService {
} else if (mPendingAuthSession != null
&& mPendingAuthSession.containsCookie(cookie)) {
if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
- mPendingAuthSession.mClientReceiver.onError(error, message);
- mPendingAuthSession = null;
+ // If any error is received while preparing the auth session (lockout, etc),
+ // and if device credential is allowed, just show the credential UI.
+ if (mPendingAuthSession.isAllowDeviceCredential()) {
+ int authenticators = mPendingAuthSession.mBundle
+ .getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, 0);
+ // Disallow biometric and notify SystemUI to show the authentication prompt.
+ authenticators &= ~Authenticator.TYPE_BIOMETRIC;
+ mPendingAuthSession.mBundle.putInt(
+ BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
+ authenticators);
+
+ mCurrentAuthSession = mPendingAuthSession;
+ mCurrentAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
+ mPendingAuthSession = null;
+
+ mStatusBarService.showAuthenticationDialog(
+ mCurrentAuthSession.mBundle,
+ mInternalReceiver,
+ 0 /* biometricModality */,
+ false /* requireConfirmation */,
+ mCurrentAuthSession.mUserId,
+ mCurrentAuthSession.mOpPackageName);
+ } else {
+ mPendingAuthSession.mClientReceiver.onError(error, message);
+ mPendingAuthSession = null;
+ }
} else {
Slog.e(TAG, "Impossible pending session error state: "
+ mPendingAuthSession.mState);
}
+ } else {
+ Slog.e(TAG, "Unknown cookie: " + cookie);
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
@@ -1385,9 +1243,12 @@ public class BiometricService extends SystemService {
try {
switch (reason) {
- case BiometricPrompt.DISMISSED_REASON_CONFIRMED:
- case BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED:
- mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow);
+ case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED:
+ case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED:
+ case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED:
+ if (mCurrentAuthSession.mTokenEscrow != null) {
+ mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow);
+ }
mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
break;
@@ -1439,12 +1300,37 @@ public class BiometricService extends SystemService {
mCurrentAuthSession.mCallingUid,
mCurrentAuthSession.mCallingPid,
mCurrentAuthSession.mCallingUserId,
- mCurrentAuthSession.mModality,
- mCurrentAuthSession.mConfirmDeviceCredentialCallback);
+ mCurrentAuthSession.mModality);
}
+ private void handleOnDeviceCredentialPressed() {
+ Slog.d(TAG, "onDeviceCredentialPressed");
+ if (mCurrentAuthSession == null) {
+ Slog.e(TAG, "Auth session null");
+ return;
+ }
+
+ // Cancel authentication. Skip the token/package check since we are cancelling
+ // from system server. The interface is permission protected so this is fine.
+ cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+
+ mCurrentAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
+ }
+
+ /**
+ * Invoked when each service has notified that its client is ready to be started. When
+ * all biometrics are ready, this invokes the SystemUI dialog through StatusBar.
+ */
private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmation,
int userId) {
+ if (mPendingAuthSession == null) {
+ // Only should happen if a biometric was locked out when authenticate() was invoked.
+ // In that case, if device credentials are allowed, the UI is already showing. If not
+ // allowed, the error has already been returned to the caller.
+ Slog.w(TAG, "Pending auth session null");
+ return;
+ }
+
Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
@@ -1486,7 +1372,7 @@ public class BiometricService extends SystemService {
}
if (!continuing) {
- mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
+ mStatusBarService.showAuthenticationDialog(mCurrentAuthSession.mBundle,
mInternalReceiver, modality, requireConfirmation, userId,
mCurrentAuthSession.mOpPackageName);
}
@@ -1498,16 +1384,21 @@ public class BiometricService extends SystemService {
private void handleAuthenticate(IBinder token, long sessionId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
- int callingUid, int callingPid, int callingUserId,
- IBiometricConfirmDeviceCredentialCallback callback) {
+ int callingUid, int callingPid, int callingUserId) {
mHandler.post(() -> {
final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
final int modality = result.first;
final int error = result.second;
- // Check for errors, notify callback, and return
- if (error != BiometricConstants.BIOMETRIC_SUCCESS) {
+ final boolean credentialAllowed = Utils.isDeviceCredentialAllowed(bundle);
+
+ if (error != BiometricConstants.BIOMETRIC_SUCCESS && credentialAllowed) {
+ // If there's a problem but device credential is allowed, only show credential UI.
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
+ Authenticator.TYPE_CREDENTIAL);
+ } else if (error != BiometricConstants.BIOMETRIC_SUCCESS) {
+ // Check for errors, notify callback, and return
try {
final String hardwareUnavailable =
getContext().getString(R.string.biometric_error_hw_unavailable);
@@ -1535,7 +1426,7 @@ public class BiometricService extends SystemService {
// Start preparing for authentication. Authentication starts when
// all modalities requested have invoked onReadyForAuthentication.
authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
- callingUid, callingPid, callingUserId, modality, callback);
+ callingUid, callingPid, callingUserId, modality);
});
}
@@ -1550,8 +1441,7 @@ public class BiometricService extends SystemService {
*/
private void authenticateInternal(IBinder token, long sessionId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
- int callingUid, int callingPid, int callingUserId, int modality,
- IBiometricConfirmDeviceCredentialCallback callback) {
+ int callingUid, int callingPid, int callingUserId, int modality) {
try {
boolean requireConfirmation = bundle.getBoolean(
BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
@@ -1565,27 +1455,49 @@ public class BiometricService extends SystemService {
// with the cookie. Once all cookies are received, we can show the prompt
// and let the services start authenticating. The cookie should be non-zero.
final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
+ final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
Slog.d(TAG, "Creating auth session. Modality: " + modality
- + ", cookie: " + cookie);
- final HashMap<Integer, Integer> authenticators = new HashMap<>();
- authenticators.put(modality, cookie);
- mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
- receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
- modality, requireConfirmation, callback);
- mPendingAuthSession.mState = STATE_AUTH_CALLED;
- // No polymorphism :(
- if ((modality & TYPE_FINGERPRINT) != 0) {
- mFingerprintService.prepareForAuthentication(token, sessionId, userId,
- mInternalReceiver, opPackageName, cookie,
- callingUid, callingPid, callingUserId);
- }
- if ((modality & TYPE_IRIS) != 0) {
- Slog.w(TAG, "Iris unsupported");
+ + ", cookie: " + cookie
+ + ", authenticators: " + authenticators);
+ final HashMap<Integer, Integer> modalities = new HashMap<>();
+
+ // If it's only device credential, we don't need to wait - LockSettingsService is
+ // always ready to check credential (SystemUI invokes that path).
+ if ((authenticators & ~Authenticator.TYPE_CREDENTIAL) != 0) {
+ modalities.put(modality, cookie);
}
- if ((modality & TYPE_FACE) != 0) {
- mFaceService.prepareForAuthentication(requireConfirmation,
- token, sessionId, userId, mInternalReceiver, opPackageName,
- cookie, callingUid, callingPid, callingUserId);
+ mPendingAuthSession = new AuthSession(modalities, token, sessionId, userId,
+ receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
+ modality, requireConfirmation);
+
+ if (authenticators == Authenticator.TYPE_CREDENTIAL) {
+ mPendingAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
+ mCurrentAuthSession = mPendingAuthSession;
+ mPendingAuthSession = null;
+
+ mStatusBarService.showAuthenticationDialog(
+ mCurrentAuthSession.mBundle,
+ mInternalReceiver,
+ 0 /* biometricModality */,
+ false /* requireConfirmation */,
+ mCurrentAuthSession.mUserId,
+ mCurrentAuthSession.mOpPackageName);
+ } else {
+ mPendingAuthSession.mState = STATE_AUTH_CALLED;
+ // No polymorphism :(
+ if ((modality & TYPE_FINGERPRINT) != 0) {
+ mFingerprintService.prepareForAuthentication(token, sessionId, userId,
+ mInternalReceiver, opPackageName, cookie,
+ callingUid, callingPid, callingUserId);
+ }
+ if ((modality & TYPE_IRIS) != 0) {
+ Slog.w(TAG, "Iris unsupported");
+ }
+ if ((modality & TYPE_FACE) != 0) {
+ mFaceService.prepareForAuthentication(requireConfirmation,
+ token, sessionId, userId, mInternalReceiver, opPackageName,
+ cookie, callingUid, callingPid, callingUserId);
+ }
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to start authentication", e);
@@ -1598,20 +1510,7 @@ public class BiometricService extends SystemService {
return;
}
- if (mCurrentAuthSession != null
- && mCurrentAuthSession.mState == STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC) {
- if (DEBUG) Slog.d(TAG, "Cancel received while ConfirmDeviceCredential showing");
- try {
- mCurrentAuthSession.mConfirmDeviceCredentialCallback.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to cancel ConfirmDeviceCredential", e);
- }
-
- // TODO(b/123378871): Remove when moved. Piggy back on this for now to clean up.
- handleOnConfirmDeviceCredentialError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
- getContext().getString(R.string.biometric_error_canceled));
- } else if (mCurrentAuthSession != null
- && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+ if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
// We need to check the current authenticators state. If we're pending confirm
// or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client,
// since we won't be getting an onError from the driver.
@@ -1624,24 +1523,12 @@ public class BiometricService extends SystemService {
);
mCurrentAuthSession = null;
- mStatusBarService.hideBiometricDialog();
+ mStatusBarService.hideAuthenticationDialog();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
} else {
- boolean fromCDC = false;
- if (mCurrentAuthSession != null) {
- fromCDC = mCurrentAuthSession.mBundle.getBoolean(
- BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
- }
-
- if (fromCDC) {
- if (DEBUG) Slog.d(TAG, "Cancelling from CDC");
- cancelInternal(token, opPackageName, false /* fromClient */);
- } else {
- cancelInternal(token, opPackageName, true /* fromClient */);
- }
-
+ cancelInternal(token, opPackageName, true /* fromClient */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 4fa29ac541f9..ed5f9de01bcf 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -17,10 +17,15 @@
package com.android.server.biometrics;
import android.content.Context;
+import android.hardware.biometrics.Authenticator;
+import android.hardware.biometrics.BiometricPrompt;
import android.os.Build;
+import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
+import com.android.internal.annotations.VisibleForTesting;
+
public class Utils {
public static boolean isDebugEnabled(Context context, int targetUserId) {
if (targetUserId == UserHandle.USER_NULL) {
@@ -38,4 +43,43 @@ public class Utils {
}
return true;
}
+
+ /**
+ * Combine {@link BiometricPrompt#KEY_ALLOW_DEVICE_CREDENTIAL} with
+ * {@link BiometricPrompt#KEY_AUTHENTICATORS_ALLOWED}, as the former is not flexible
+ * enough.
+ */
+ public static void combineAuthenticatorBundles(Bundle bundle) {
+ boolean biometricEnabled = true; // enabled by default
+ boolean credentialEnabled = bundle.getBoolean(
+ BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, false);
+ if (bundle.get(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED) != null) {
+ final int authenticatorFlags =
+ bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
+ biometricEnabled = (authenticatorFlags & Authenticator.TYPE_BIOMETRIC) != 0;
+ // Using both KEY_ALLOW_DEVICE_CREDENTIAL and KEY_AUTHENTICATORS_ALLOWED together
+ // is not supported. Default to overwriting.
+ credentialEnabled = (authenticatorFlags & Authenticator.TYPE_CREDENTIAL) != 0;
+ }
+
+ bundle.remove(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL);
+
+ int authenticators = 0;
+ if (biometricEnabled) {
+ authenticators |= Authenticator.TYPE_BIOMETRIC;
+ }
+ if (credentialEnabled) {
+ authenticators |= Authenticator.TYPE_CREDENTIAL;
+ }
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ }
+
+ /**
+ * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
+ * @return true if device credential allowed.
+ */
+ public static boolean isDeviceCredentialAllowed(Bundle bundle) {
+ final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
+ return (authenticators & Authenticator.TYPE_CREDENTIAL) != 0;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 4f4baab204e6..61d4d4bf9ac1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -850,36 +850,25 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
mAddress, Constants.ADDR_BROADCAST, systemAudioStatusOn));
if (systemAudioStatusOn) {
+ // If TV sends out SAM Request with a path of a non-CEC device, which should not show
+ // up in the CEC device list and not under the current AVR device, the AVR would switch
+ // to ARC.
int sourcePhysicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
- if (sourcePhysicalAddress != getActiveSource().physicalAddress) {
- // If the Active Source recorded by the current device is not synced up with TV,
- // update the Active Source internally.
- if (sourcePhysicalAddress == mService.getPhysicalAddress()) {
- // If the active path is the current device itself, update with local info
- if (mService.playback() != null) {
- setActiveSource(mService.playback().mAddress, sourcePhysicalAddress);
- } else {
- setActiveSource(mAddress, sourcePhysicalAddress);
- }
- } else {
- // If it's not the current device, look for the device info from the list
- for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
- if (info.getPhysicalAddress() == sourcePhysicalAddress) {
- setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress());
- break;
- }
- }
- }
- // If the Active path from TV's System Audio Mode request does not belong to any
- // device in the local device list, record the new Active physicalAddress with an
- // unregistered logical address first. Then query the Active Source again.
- if (sourcePhysicalAddress != getActiveSource().physicalAddress) {
- setActiveSource(Constants.ADDR_UNREGISTERED, sourcePhysicalAddress);
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildRequestActiveSource(mAddress));
+ if (HdmiUtils.getLocalPortFromPhysicalAddress(
+ sourcePhysicalAddress, getDeviceInfo().getPhysicalAddress())
+ != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE) {
+ return true;
+ }
+ boolean isDeviceInCecDeviceList = false;
+ for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
+ if (info.getPhysicalAddress() == sourcePhysicalAddress) {
+ isDeviceInCecDeviceList = true;
+ break;
}
}
- switchInputOnReceivingNewActivePath(sourcePhysicalAddress);
+ if (!isDeviceInCecDeviceList) {
+ switchInputOnReceivingNewActivePath(sourcePhysicalAddress);
+ }
}
return true;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c3354e1d7e0f..1794df3b602e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -311,6 +311,11 @@ public class HdmiControlService extends SystemService {
private IHdmiControlCallback mDisplayStatusCallback = null;
@Nullable
+ // Save callback when the device is still under logcial address allocation
+ // Invoke once new local device is ready.
+ private IHdmiControlCallback mOtpCallbackPendingAddressAllocation = null;
+
+ @Nullable
private HdmiCecController mCecController;
// HDMI port information. Stored in the unmodifiable list to keep the static information
@@ -785,17 +790,21 @@ public class HdmiControlService extends SystemService {
// Address allocation completed for all devices. Notify each device.
if (allocatingDevices.size() == ++finished[0]) {
mAddressAllocated = true;
- // Reinvoke the saved display status callback once the local device is ready.
- if (mDisplayStatusCallback != null) {
- queryDisplayStatus(mDisplayStatusCallback);
- mDisplayStatusCallback = null;
- }
if (initiatedBy != INITIATED_BY_HOTPLUG) {
// In case of the hotplug we don't call onInitializeCecComplete()
// since we reallocate the logical address only.
onInitializeCecComplete(initiatedBy);
}
notifyAddressAllocated(allocatedDevices, initiatedBy);
+ // Reinvoke the saved display status callback once the local device is ready.
+ if (mDisplayStatusCallback != null) {
+ queryDisplayStatus(mDisplayStatusCallback);
+ mDisplayStatusCallback = null;
+ }
+ if (mOtpCallbackPendingAddressAllocation != null) {
+ oneTouchPlay(mOtpCallbackPendingAddressAllocation);
+ mOtpCallbackPendingAddressAllocation = null;
+ }
mCecMessageBuffer.processMessages();
}
}
@@ -2246,8 +2255,16 @@ public class HdmiControlService extends SystemService {
}
@ServiceThreadOnly
- private void oneTouchPlay(final IHdmiControlCallback callback) {
+ @VisibleForTesting
+ protected void oneTouchPlay(final IHdmiControlCallback callback) {
assertRunOnServiceThread();
+ if (!mAddressAllocated) {
+ mOtpCallbackPendingAddressAllocation = callback;
+ Slog.d(TAG, "Local device is under address allocation. "
+ + "Save OTP callback for later process.");
+ return;
+ }
+
HdmiCecLocalDeviceSource source = playback();
if (source == null) {
source = audioSystem();
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index c8fc5fc96e59..4962af176f18 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -77,7 +77,6 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
broadcastActiveSource();
queryDevicePowerStatus();
- mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
return true;
}
@@ -99,6 +98,7 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
}
private void queryDevicePowerStatus() {
+ mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
mTargetAddress));
}
diff --git a/services/core/java/com/android/server/locksettings/TEST_MAPPING b/services/core/java/com/android/server/locksettings/TEST_MAPPING
index c1cba5f7f22d..56f5cc034f05 100644
--- a/services/core/java/com/android/server/locksettings/TEST_MAPPING
+++ b/services/core/java/com/android/server/locksettings/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-devicepolicy": [
{
"name": "CtsDevicePolicyManagerTestCases",
"options": [
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a8ba82a74065..3341991aa0ae 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -94,7 +94,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
-import static com.android.server.notification.PreferencesHelper.DEFAULT_ALLOW_BUBBLE;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG_CRITICAL;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG_NORMAL;
@@ -1428,10 +1427,8 @@ public class NotificationManagerService extends SystemService {
private final class SettingsObserver extends ContentObserver {
private final Uri NOTIFICATION_BADGING_URI
= Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
- private final Uri NOTIFICATION_BUBBLES_URI_GLOBAL
+ private final Uri NOTIFICATION_BUBBLES_URI
= Settings.Global.getUriFor(Settings.Global.NOTIFICATION_BUBBLES);
- private final Uri NOTIFICATION_BUBBLES_URI_SECURE
- = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BUBBLES);
private final Uri NOTIFICATION_LIGHT_PULSE_URI
= Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
private final Uri NOTIFICATION_RATE_LIMIT_URI
@@ -1449,9 +1446,7 @@ public class NotificationManagerService extends SystemService {
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
false, this, UserHandle.USER_ALL);
- resolver.registerContentObserver(NOTIFICATION_BUBBLES_URI_GLOBAL,
- false, this, UserHandle.USER_ALL);
- resolver.registerContentObserver(NOTIFICATION_BUBBLES_URI_SECURE,
+ resolver.registerContentObserver(NOTIFICATION_BUBBLES_URI,
false, this, UserHandle.USER_ALL);
update(null);
}
@@ -1478,41 +1473,9 @@ public class NotificationManagerService extends SystemService {
if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
mPreferencesHelper.updateBadgingEnabled();
}
- // In QPR we moved the setting to Global rather than Secure so that the setting
- // applied to work profiles. Unfortunately we need to maintain both to pass CTS without
- // a change to CTS outside of a normal letter release.
- if (uri == null || NOTIFICATION_BUBBLES_URI_GLOBAL.equals(uri)) {
- syncBubbleSettings(resolver, NOTIFICATION_BUBBLES_URI_GLOBAL);
+ if (uri == null || NOTIFICATION_BUBBLES_URI.equals(uri)) {
mPreferencesHelper.updateBubblesEnabled();
}
- if (NOTIFICATION_BUBBLES_URI_SECURE.equals(uri)) {
- syncBubbleSettings(resolver, NOTIFICATION_BUBBLES_URI_SECURE);
- }
- }
-
- private void syncBubbleSettings(ContentResolver resolver, Uri settingToFollow) {
- boolean followSecureSetting = settingToFollow.equals(NOTIFICATION_BUBBLES_URI_SECURE);
-
- int secureSettingValue = Settings.Secure.getInt(resolver,
- Settings.Secure.NOTIFICATION_BUBBLES, DEFAULT_ALLOW_BUBBLE ? 1 : 0);
- int globalSettingValue = Settings.Global.getInt(resolver,
- Settings.Global.NOTIFICATION_BUBBLES, DEFAULT_ALLOW_BUBBLE ? 1 : 0);
-
- if (globalSettingValue == secureSettingValue) {
- return;
- }
-
- if (followSecureSetting) {
- // Global => secure
- Settings.Global.putInt(resolver,
- Settings.Global.NOTIFICATION_BUBBLES,
- secureSettingValue);
- } else {
- // Secure => Global
- Settings.Secure.putInt(resolver,
- Settings.Secure.NOTIFICATION_BADGING,
- globalSettingValue);
- }
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 83bea9dd1efc..9e7b46485d4c 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -738,6 +738,25 @@ public final class OverlayManagerService extends SystemService {
}
@Override
+ public void invalidateCachesForOverlay(@Nullable String packageName, int userId)
+ throws RemoteException {
+ if (packageName == null) {
+ return;
+ }
+
+ enforceChangeOverlayPackagesPermission("invalidateCachesForOverlay");
+ userId = handleIncomingUser(userId, "invalidateCachesForOverlay");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ mImpl.removeIdmapForOverlay(packageName, userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void onShellCommand(@NonNull final FileDescriptor in,
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
@NonNull final String[] args, @NonNull final ShellCallback callback,
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 934511bf88d1..019c9528f8ab 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -651,6 +651,11 @@ final class OverlayManagerServiceImpl {
return mDefaultOverlays;
}
+ void removeIdmapForOverlay(String packageName, int userId) {
+ final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
+ removeIdmapIfPossible(oi);
+ }
+
List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
final int userId) {
final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 8897eca85d7a..3439d3841973 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -609,13 +609,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int type, boolean requireConfirmation, int userId, String opPackageName) {
+ public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName) {
enforceBiometricDialog();
if (mBar != null) {
try {
- mBar.showBiometricDialog(bundle, receiver, type, requireConfirmation, userId,
- opPackageName);
+ mBar.showAuthenticationDialog(bundle, receiver, biometricModality,
+ requireConfirmation, userId, opPackageName);
} catch (RemoteException ex) {
}
}
@@ -644,22 +644,22 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void onBiometricError(String error) {
+ public void onBiometricError(int errorCode, String error) {
enforceBiometricDialog();
if (mBar != null) {
try {
- mBar.onBiometricError(error);
+ mBar.onBiometricError(errorCode, error);
} catch (RemoteException ex) {
}
}
}
@Override
- public void hideBiometricDialog() {
+ public void hideAuthenticationDialog() {
enforceBiometricDialog();
if (mBar != null) {
try {
- mBar.hideBiometricDialog();
+ mBar.hideAuthenticationDialog();
} catch (RemoteException ex) {
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c3ff28530b7f..b118cdfeb84d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11902,10 +11902,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
try {
int uid = packageManager.getPackageUidAsUser(packageName,
user.getIdentifier());
-
- // TODO: Prevent noting the app-op
- granted = PermissionChecker.checkPermission(mContext, permission, -1,
- uid, packageName);
+ if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
+ PermissionChecker.PID_UNKNOWN, uid, packageName)
+ != PermissionChecker.PERMISSION_GRANTED) {
+ granted = PackageManager.PERMISSION_DENIED;
+ } else {
+ granted = PackageManager.PERMISSION_GRANTED;
+ }
} catch (NameNotFoundException e) {
throw new RemoteException(
"Cannot check if " + permission + "is a runtime permission", e,
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 1685b04cf2ca..8b3c85e8a7f3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -82,6 +82,7 @@ import android.os.Build;
import android.os.IBinder;
import android.os.PowerManagerInternal;
import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -103,6 +104,7 @@ import java.util.ArrayList;
* Build/Install/Run:
* atest MockingOomAdjusterTests
*/
+@Presubmit
public class MockingOomAdjusterTests {
private static final int MOCKAPP_PID = 12345;
private static final int MOCKAPP_UID = 12345;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 73dcb98abc8c..43f251a9281f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -46,6 +46,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -140,6 +141,8 @@ public class AbstractAccessibilityServiceConnectionTest {
private final List<AccessibilityWindowInfo> mA11yWindowInfosOnSecondDisplay = new ArrayList<>();
private Callable[] mFindA11yNodesFunctions;
private Callable<Boolean> mPerformA11yAction;
+ private ArrayList<Integer> mDisplayList = new ArrayList<>(Arrays.asList(
+ Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID));
// To mock package-private class.
@Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -184,6 +187,7 @@ public class AbstractAccessibilityServiceConnectionTest {
addA11yWindowInfo(mA11yWindowInfos, PIP_WINDOWID, true, Display.DEFAULT_DISPLAY);
addA11yWindowInfo(mA11yWindowInfosOnSecondDisplay, WINDOWID_ONSECONDDISPLAY, false,
SECONDARY_DISPLAY_ID);
+ when(mMockA11yWindowManager.getDisplayListLocked()).thenReturn(mDisplayList);
when(mMockA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY))
.thenReturn(mA11yWindowInfos);
when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID))
@@ -285,7 +289,14 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void getWindows() {
- assertThat(mServiceConnection.getWindows(), is(mA11yWindowInfos));
+ final AccessibilityWindowInfo.WindowListSparseArray allWindows =
+ mServiceConnection.getWindows();
+
+ assertEquals(2, allWindows.size());
+ assertThat(allWindows.get(Display.DEFAULT_DISPLAY), is(mA11yWindowInfos));
+ assertEquals(2, allWindows.get(Display.DEFAULT_DISPLAY).size());
+ assertThat(allWindows.get(SECONDARY_DISPLAY_ID), is(mA11yWindowInfosOnSecondDisplay));
+ assertEquals(1, allWindows.get(SECONDARY_DISPLAY_ID).size());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index b7b5a4eaacfa..b5e5deb9ff59 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -67,6 +67,7 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -104,6 +105,9 @@ public class AccessibilityWindowManagerTest {
// List of callback, mapping from displayId -> callback.
private final SparseArray<WindowsForAccessibilityCallback> mCallbackOfWindows =
new SparseArray<>();
+ // List of display ID.
+ private final ArrayList<Integer> mExpectedDisplayList = new ArrayList<>(Arrays.asList(
+ Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID));
private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
@@ -692,6 +696,15 @@ public class AccessibilityWindowManagerTest {
assertTrue(windowId >= 0);
}
+ @Test
+ public void getDisplayList() throws RemoteException {
+ // Starts tracking window of second display.
+ startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+
+ final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked();
+ assertTrue(displayList.equals(mExpectedDisplayList));
+ }
+
private void startTrackingPerDisplay(int displayId) throws RemoteException {
ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>();
// Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index ccf3a908364a..4aeeb0af1bf8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -39,6 +39,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
@@ -55,6 +56,9 @@ import android.os.Handler;
import android.os.IBinder;
import android.security.KeyStore;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
import com.android.internal.R;
import com.android.internal.statusbar.IStatusBarService;
@@ -66,9 +70,6 @@ import org.mockito.MockitoAnnotations;
import java.util.List;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
@SmallTest
public class BiometricServiceTest {
@@ -82,6 +83,7 @@ public class BiometricServiceTest {
private static final String ERROR_CANCELED = "error_canceled";
private static final String ERROR_UNABLE_TO_PROCESS = "error_unable_to_process";
private static final String ERROR_USER_CANCELED = "error_user_canceled";
+ private static final String ERROR_LOCKOUT = "error_lockout";
private static final String FINGERPRINT_ACQUIRED_SENSOR_DIRTY = "sensor_dirty";
@@ -179,7 +181,8 @@ public class BiometricServiceTest {
mBiometricService = new BiometricService(mContext, new MockInjector());
mBiometricService.onStart();
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
verify(mReceiver1).onError(
eq(BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT), eq(ERROR_HW_UNAVAILABLE));
@@ -193,7 +196,8 @@ public class BiometricServiceTest {
mBiometricService = new BiometricService(mContext, new MockInjector());
mBiometricService.onStart();
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
verify(mReceiver1).onError(
eq(BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS), any());
@@ -208,7 +212,8 @@ public class BiometricServiceTest {
mBiometricService = new BiometricService(mContext, new MockInjector());
mBiometricService.onStart();
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
verify(mReceiver1).onError(
eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(ERROR_HW_UNAVAILABLE));
@@ -226,7 +231,8 @@ public class BiometricServiceTest {
// Disabled in user settings receives onError
when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(false);
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
verify(mReceiver1).onError(
eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(ERROR_HW_UNAVAILABLE));
@@ -236,7 +242,8 @@ public class BiometricServiceTest {
when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
.thenReturn(true);
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
verify(mReceiver1, never()).onError(anyInt(), any(String.class));
verify(mBiometricService.mFaceService).prepareForAuthentication(
@@ -255,7 +262,8 @@ public class BiometricServiceTest {
resetReceiver();
when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
.thenReturn(false);
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
verify(mBiometricService.mFaceService).prepareForAuthentication(
eq(false) /* requireConfirmation */,
@@ -277,7 +285,8 @@ public class BiometricServiceTest {
mBiometricService.onStart();
// Start testing the happy path
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
// Creates a pending auth session with the correct initial states
@@ -311,7 +320,7 @@ public class BiometricServiceTest {
.startPreparedClient(cookieCaptor.getValue());
// StatusBar showBiometricDialog invoked
- verify(mBiometricService.mStatusBarService).showBiometricDialog(
+ verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
eq(mBiometricService.mCurrentAuthSession.mBundle),
any(IBiometricServiceReceiverInternal.class),
eq(BiometricAuthenticator.TYPE_FINGERPRINT),
@@ -333,7 +342,7 @@ public class BiometricServiceTest {
// SystemUI sends callback with dismissed reason
mBiometricService.mInternalReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
waitForIdle();
// HAT sent to keystore
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
@@ -344,10 +353,32 @@ public class BiometricServiceTest {
}
@Test
+ public void testAuthenticate_noBiometrics_credentialAllowed() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+ true /* requireConfirmation */, true /* allowDeviceCredential */);
+ waitForIdle();
+
+ assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mState);
+ assertEquals(Authenticator.TYPE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mBundle
+ .getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+ verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+ eq(mBiometricService.mCurrentAuthSession.mBundle),
+ any(IBiometricServiceReceiverInternal.class),
+ eq(0 /* biometricModality */),
+ anyBoolean() /* requireConfirmation */,
+ anyInt() /* userId */,
+ eq(TEST_PACKAGE_NAME));
+ }
+
+ @Test
public void testAuthenticate_happyPathWithConfirmation() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- true /* requireConfirmation */);
+ true /* requireConfirmation */, false /* allowDeviceCredential */);
// Test authentication succeeded goes to PENDING_CONFIRMATION and that the HAT is not
// sent to KeyStore yet
@@ -362,7 +393,7 @@ public class BiometricServiceTest {
// SystemUI sends confirm, HAT is sent to keystore and client is notified.
mBiometricService.mInternalReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
waitForIdle();
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
verify(mReceiver1).onAuthenticationSucceeded();
@@ -373,7 +404,7 @@ public class BiometricServiceTest {
throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onAuthenticationFailed();
waitForIdle();
@@ -390,7 +421,7 @@ public class BiometricServiceTest {
throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onAuthenticationFailed();
waitForIdle();
@@ -406,13 +437,14 @@ public class BiometricServiceTest {
public void testErrorCanceled_whenAuthenticating_notifiesSystemUIAndClient() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
// Create a new pending auth session but don't start it yet. HAL contract is that previous
// one must get ERROR_CANCELED. Simulate that here by creating the pending auth session,
// sending ERROR_CANCELED to the current auth session, and then having the second one
// onReadyForAuthentication.
- invokeAuthenticate(mBiometricService.mImpl, mReceiver2, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver2, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
assertEquals(mBiometricService.mCurrentAuthSession.mState,
@@ -430,7 +462,7 @@ public class BiometricServiceTest {
verify(mReceiver2, never()).onError(anyInt(), any(String.class));
// SystemUI dialog closed
- verify(mBiometricService.mStatusBarService).hideBiometricDialog();
+ verify(mBiometricService.mStatusBarService).hideAuthenticationDialog();
// After SystemUI notifies that the animation has completed
mBiometricService.mInternalReceiver
@@ -446,7 +478,7 @@ public class BiometricServiceTest {
public void testErrorHalTimeout_whenAuthenticating_entersPausedState() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
@@ -479,7 +511,7 @@ public class BiometricServiceTest {
resetStatusBar();
startPendingAuthSession(mBiometricService);
waitForIdle();
- verify(mBiometricService.mStatusBarService, never()).showBiometricDialog(
+ verify(mBiometricService.mStatusBarService, never()).showAuthenticationDialog(
any(Bundle.class),
any(IBiometricServiceReceiverInternal.class),
anyInt(),
@@ -492,7 +524,7 @@ public class BiometricServiceTest {
public void testErrorFromHal_whenPaused_notifiesSystemUIAndClient() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireCOnfirmation */);
+ false /* requireCOnfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
@@ -509,7 +541,7 @@ public class BiometricServiceTest {
eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
eq(ERROR_CANCELED));
// Dialog is hidden immediately
- verify(mBiometricService.mStatusBarService).hideBiometricDialog();
+ verify(mBiometricService.mStatusBarService).hideAuthenticationDialog();
// Auth session is over
assertNull(mBiometricService.mCurrentAuthSession);
}
@@ -522,7 +554,7 @@ public class BiometricServiceTest {
// session is done.
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
@@ -533,9 +565,10 @@ public class BiometricServiceTest {
// Sends error to SystemUI and does not notify client yet
assertEquals(mBiometricService.mCurrentAuthSession.mState,
BiometricService.STATE_ERROR_PENDING_SYSUI);
- verify(mBiometricService.mStatusBarService)
- .onBiometricError(eq(ERROR_UNABLE_TO_PROCESS));
- verify(mBiometricService.mStatusBarService, never()).hideBiometricDialog();
+ verify(mBiometricService.mStatusBarService).onBiometricError(
+ eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS),
+ eq(ERROR_UNABLE_TO_PROCESS));
+ verify(mBiometricService.mStatusBarService, never()).hideAuthenticationDialog();
verify(mReceiver1, never()).onError(anyInt(), anyString());
// SystemUI animation completed, client is notified, auth session is over
@@ -549,11 +582,177 @@ public class BiometricServiceTest {
}
@Test
+ public void testErrorFromHal_whilePreparingAuthentication_credentialAllowed() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, true /* allowDeviceCredential */);
+ waitForIdle();
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForPendingSession(mBiometricService.mPendingAuthSession),
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
+ ERROR_LOCKOUT);
+ waitForIdle();
+
+ // Pending auth session becomes current auth session, since device credential should
+ // be shown now.
+ assertNull(mBiometricService.mPendingAuthSession);
+ assertNotNull(mBiometricService.mCurrentAuthSession);
+ assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mState);
+ assertEquals(Authenticator.TYPE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mBundle.getInt(
+ BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+ verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+ eq(mBiometricService.mCurrentAuthSession.mBundle),
+ any(IBiometricServiceReceiverInternal.class),
+ eq(0 /* biometricModality */),
+ anyBoolean() /* requireConfirmation */,
+ anyInt() /* userId */,
+ eq(TEST_PACKAGE_NAME));
+ }
+
+ @Test
+ public void testErrorFromHal_whilePreparingAuthentication_credentialNotAllowed()
+ throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
+ waitForIdle();
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForPendingSession(mBiometricService.mPendingAuthSession),
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
+ ERROR_LOCKOUT);
+ waitForIdle();
+
+ // Error is sent to client
+ assertNull(mBiometricService.mPendingAuthSession);
+ assertNull(mBiometricService.mCurrentAuthSession);
+ }
+
+ @Test
+ public void testCombineAuthenticatorBundle_keyAllowDeviceCredentialAlwaysRemoved() {
+ Bundle bundle;
+ int authenticators;
+
+ // In:
+ // KEY_ALLOW_DEVICE_CREDENTIAL = true
+ // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
+ // Out:
+ // KEY_ALLOW_DEVICE_CREDENTIAL = null
+ // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
+ bundle = new Bundle();
+ bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, true);
+ authenticators = Authenticator.TYPE_CREDENTIAL | Authenticator.TYPE_BIOMETRIC;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ Utils.combineAuthenticatorBundles(bundle);
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(authenticators, bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+
+ // In:
+ // KEY_ALLOW_DEVICE_CREDENTIAL = true
+ // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC
+ // Out:
+ // KEY_ALLOW_DEVICE_CREDENTIAL = null
+ // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
+ bundle = new Bundle();
+ bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, true);
+ authenticators = Authenticator.TYPE_BIOMETRIC;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ Utils.combineAuthenticatorBundles(bundle);
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(authenticators, bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+
+ // In:
+ // KEY_ALLOW_DEVICE_CREDENTIAL = null
+ // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
+ // Out:
+ // KEY_ALLOW_DEVICE_CREDENTIAL = null
+ // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
+ bundle = new Bundle();
+ authenticators = Authenticator.TYPE_BIOMETRIC | Authenticator.TYPE_CREDENTIAL;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ Utils.combineAuthenticatorBundles(bundle);
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(authenticators, bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+ }
+
+ @Test
+ public void testErrorFromHal_whileShowingDeviceCredential_doesntNotifySystemUI()
+ throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, true /* allowDeviceCredential */);
+
+ mBiometricService.mInternalReceiver.onDeviceCredentialPressed();
+ waitForIdle();
+
+ assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mState);
+ verify(mReceiver1, never()).onError(anyInt(), anyString());
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ ERROR_CANCELED);
+ waitForIdle();
+
+ assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mState);
+ verify(mReceiver1, never()).onError(anyInt(), anyString());
+ }
+
+ @Test
+ public void testLockout_whileAuthenticating_credentialAllowed() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, true /* allowDeviceCredential */);
+
+ assertEquals(BiometricService.STATE_AUTH_STARTED,
+ mBiometricService.mCurrentAuthSession.mState);
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
+ ERROR_LOCKOUT);
+ waitForIdle();
+
+ assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mState);
+ verify(mBiometricService.mStatusBarService).onBiometricError(
+ eq(BiometricConstants.BIOMETRIC_ERROR_LOCKOUT),
+ eq(ERROR_LOCKOUT));
+ }
+
+ @Test
+ public void testLockout_whenAuthenticating_credentialNotAllowed() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
+
+ assertEquals(BiometricService.STATE_AUTH_STARTED,
+ mBiometricService.mCurrentAuthSession.mState);
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS,
+ ERROR_UNABLE_TO_PROCESS);
+ waitForIdle();
+
+ assertEquals(BiometricService.STATE_ERROR_PENDING_SYSUI,
+ mBiometricService.mCurrentAuthSession.mState);
+ verify(mBiometricService.mStatusBarService).onBiometricError(
+ eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS),
+ eq(ERROR_UNABLE_TO_PROCESS));
+ }
+
+ @Test
public void testDismissedReasonUserCancel_whileAuthenticating_cancelsHalAuthentication()
throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver
.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
@@ -575,7 +774,7 @@ public class BiometricServiceTest {
public void testDismissedReasonNegative_whilePaused_doesntInvokeHalCancel() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
@@ -598,7 +797,7 @@ public class BiometricServiceTest {
public void testDismissedReasonUserCancel_whilePaused_doesntInvokeHalCancel() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
@@ -621,7 +820,7 @@ public class BiometricServiceTest {
public void testDismissedReasonUserCancel_whenPendingConfirmation() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- true /* requireConfirmation */);
+ true /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
true /* requireConfirmation */,
@@ -648,7 +847,7 @@ public class BiometricServiceTest {
public void testAcquire_whenAuthenticating_sentToSystemUI() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onAcquired(
FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
@@ -698,9 +897,10 @@ public class BiometricServiceTest {
}
private void invokeAuthenticateAndStart(IBiometricService.Stub service,
- IBiometricServiceReceiver receiver, boolean requireConfirmation) throws Exception {
+ IBiometricServiceReceiver receiver, boolean requireConfirmation,
+ boolean allowDeviceCredential) throws Exception {
// Request auth, creates a pending session
- invokeAuthenticate(service, receiver, requireConfirmation);
+ invokeAuthenticate(service, receiver, requireConfirmation, allowDeviceCredential);
waitForIdle();
startPendingAuthSession(mBiometricService);
@@ -720,20 +920,25 @@ public class BiometricServiceTest {
}
private static void invokeAuthenticate(IBiometricService.Stub service,
- IBiometricServiceReceiver receiver, boolean requireConfirmation) throws Exception {
+ IBiometricServiceReceiver receiver, boolean requireConfirmation,
+ boolean allowDeviceCredential) throws Exception {
service.authenticate(
new Binder() /* token */,
0 /* sessionId */,
0 /* userId */,
receiver,
TEST_PACKAGE_NAME /* packageName */,
- createTestBiometricPromptBundle(requireConfirmation),
- null /* IBiometricConfirmDeviceCredentialCallback */);
+ createTestBiometricPromptBundle(requireConfirmation, allowDeviceCredential));
}
- private static Bundle createTestBiometricPromptBundle(boolean requireConfirmation) {
+ private static Bundle createTestBiometricPromptBundle(boolean requireConfirmation,
+ boolean allowDeviceCredential) {
final Bundle bundle = new Bundle();
bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, requireConfirmation);
+
+ if (allowDeviceCredential) {
+ bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, true);
+ }
return bundle;
}
@@ -742,6 +947,11 @@ public class BiometricServiceTest {
return session.mModalitiesMatched.values().iterator().next();
}
+ private static int getCookieForPendingSession(BiometricService.AuthSession session) {
+ assertEquals(session.mModalitiesWaiting.values().size(), 1);
+ return session.mModalitiesWaiting.values().iterator().next();
+ }
+
private static void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 11bd29d8a163..ad3e040aca72 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -28,6 +28,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -114,6 +115,7 @@ public class ArcTerminationActionFromAvrTest {
}
@Test
+ @Ignore("b/120845532")
public void testSendMessage_notSuccess() {
mSendCecCommandSuccess = false;
mShouldDispatchReportArcTerminated = false;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index a89198ae3708..0a1899be8c1f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -232,6 +232,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ @Ignore("b/120845532")
public void handleGiveSystemAudioModeStatus_originalOff() throws Exception {
HdmiCecMessage expectedMessage =
HdmiCecMessageBuilder.buildReportSystemAudioMode(
@@ -301,6 +302,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ @Ignore("b/120845532")
public void handleSetSystemAudioMode_setOn_orignalOff() throws Exception {
mMusicMute = true;
HdmiCecMessage messageSet =
@@ -328,6 +330,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ @Ignore("b/120845532")
public void handleSystemAudioModeRequest_turnOffByTv() throws Exception {
assertThat(mMusicMute).isFalse();
// Check if feature correctly turned off
@@ -497,6 +500,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ @Ignore("b/120845532")
public void handleRequestArcTerminate_arcIsNotOn() throws Exception {
assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
HdmiCecMessage message =
@@ -671,6 +675,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ @Ignore("b/120845532")
public void handleReportPhysicalAddress_differentPath_addDevice() {
assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index b8799c3f16f7..0062a1751802 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -99,6 +99,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ @Ignore("b/120845532")
public void handleSetSystemAudioModeOn_audioSystemBroadcast() {
mHdmiControlService.setSystemAudioActivated(false);
assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isFalse();
@@ -110,6 +111,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ @Ignore("b/120845532")
public void handleSetSystemAudioModeOff_audioSystemToPlayback() {
mHdmiCecLocalDevicePlayback.mService.setSystemAudioActivated(true);
assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isTrue();
@@ -123,6 +125,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ @Ignore("b/120845532")
public void handleSystemAudioModeStatusOn_DirectltToLocalDeviceFromAudioSystem() {
mHdmiControlService.setSystemAudioActivated(false);
assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isFalse();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
new file mode 100644
index 000000000000..c6cf9b116a1d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import static android.os.SystemClock.sleep;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.assertEquals;
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.Looper;
+import android.os.SystemProperties;
+import android.os.test.TestLooper;
+import android.util.Slog;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import java.util.ArrayList;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link HdmiControlServiceBinderAPITest} class.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class HdmiControlServiceBinderAPITest {
+
+ private class HdmiCecLocalDeviceMyDevice extends HdmiCecLocalDevice {
+
+ private boolean mCanGoToStandby;
+ private boolean mIsStandby;
+ private boolean mIsDisabled;
+
+ protected HdmiCecLocalDeviceMyDevice(HdmiControlService service, int deviceType) {
+ super(service, deviceType);
+ }
+
+ @Override
+ protected void onAddressAllocated(int logicalAddress, int reason) {
+ }
+
+ @Override
+ protected int getPreferredAddress() {
+ return 0;
+ }
+
+ @Override
+ protected void setPreferredAddress(int addr) {
+ }
+
+ @Override
+ protected boolean canGoToStandby() {
+ return mCanGoToStandby;
+ }
+
+ @Override
+ protected void disableDevice(
+ boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
+ mIsDisabled = true;
+ originalCallback.onCleared(this);
+ }
+
+ @Override
+ protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ mIsStandby = true;
+ }
+
+ protected boolean isStandby() {
+ return mIsStandby;
+ }
+
+ protected boolean isDisabled() {
+ return mIsDisabled;
+ }
+
+ protected void setCanGoToStandby(boolean canGoToStandby) {
+ mCanGoToStandby = canGoToStandby;
+ }
+ }
+
+ private static final String TAG = "HdmiControlServiceBinderAPITest";
+ private HdmiControlService mHdmiControlService;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDevicePlayback mPlaybackDevice;
+ private FakeNativeWrapper mNativeWrapper;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private HdmiPortInfo[] mHdmiPortInfo;
+ private int mResult;
+ private int mPowerStatus;
+
+ @Before
+ public void SetUp() {
+ mHdmiControlService =
+ new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ @Override
+ void sendCecCommand(HdmiCecMessage command) {
+ switch (command.getOpcode()) {
+ case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ Constants.ADDR_TV,
+ Constants.ADDR_PLAYBACK_1,
+ HdmiControlManager.POWER_STATUS_ON);
+ handleCecCommand(message);
+ break;
+ default:
+ return;
+ }
+ }
+
+ @Override
+ boolean isPowerStandby() {
+ return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
+ }
+ };
+ mMyLooper = mTestLooper.getLooper();
+
+ mPlaybackDevice = new HdmiCecLocalDevicePlayback(mHdmiControlService) {
+ @Override
+ void setIsActiveSource(boolean on) {
+ mIsActiveSource = on;
+ }
+
+ @Override
+ protected void wakeUpIfActiveSource() {}
+
+ @Override
+ protected void setPreferredAddress(int addr) {}
+
+ @Override
+ protected int getPreferredAddress() {
+ return Constants.ADDR_PLAYBACK_1;
+ }
+ };
+ mPlaybackDevice.init();
+
+ mHdmiControlService.setIoLooper(mMyLooper);
+
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+
+ mLocalDevices.add(mPlaybackDevice);
+ mHdmiPortInfo = new HdmiPortInfo[1];
+ mHdmiPortInfo[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
+ mNativeWrapper.setPortInfo(mHdmiPortInfo);
+ mHdmiControlService.initPortInfo();
+ mResult = -1;
+ mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
+
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void oneTouchPlay_addressNotAllocated() {
+ assertThat(mHdmiControlService.isAddressAllocated()).isFalse();
+ mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ mResult = result;
+ }
+ });
+ assertEquals(mResult, -1);
+ assertThat(mPlaybackDevice.mIsActiveSource).isFalse();
+
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiControlService.isAddressAllocated()).isTrue();
+ assertEquals(mResult, HdmiControlManager.RESULT_SUCCESS);
+ assertThat(mPlaybackDevice.mIsActiveSource).isTrue();
+ }
+
+ @Test
+ public void oneTouchPlay_addressAllocated() {
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiControlService.isAddressAllocated()).isTrue();
+ mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ mResult = result;
+ }
+ });
+ assertEquals(mResult, HdmiControlManager.RESULT_SUCCESS);
+ assertThat(mPlaybackDevice.mIsActiveSource).isTrue();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index 440a49ab81fc..6dcff3574faf 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -221,6 +221,7 @@ public class SystemAudioInitiationActionFromAvrTest {
}
@Test
+ @Ignore("b/120845532")
public void testTvSupport() {
resetTestVariables();
mShouldDispatchActiveSource = true;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 806c71a7a9b8..6d5b994a63bb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -132,6 +132,7 @@ public class UserManagerServiceUserInfoTest {
user.profileBadge = 2;
user.partial = true;
user.guestToRemove = true;
+ user.preCreated = true;
return user;
}
@@ -147,5 +148,6 @@ public class UserManagerServiceUserInfoTest {
assertEquals("profile badge not preseved", one.profileBadge, two.profileBadge);
assertEquals("partial not preseved", one.partial, two.partial);
assertEquals("guestToRemove not preseved", one.guestToRemove, two.guestToRemove);
+ assertEquals("preCreated not preseved", one.preCreated, two.preCreated);
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 59d07357ff56..34eb3f16bf2a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -97,6 +97,8 @@ import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
@@ -159,7 +161,7 @@ public class UsageStatsService extends SystemService implements
int mUsageSource;
/** Manages the standby state of apps. */
- AppStandbyController mAppStandby;
+ AppStandbyInternal mAppStandby;
/** Manages app time limit observers */
AppTimeLimitController mAppTimeLimit;
@@ -208,7 +210,9 @@ public class UsageStatsService extends SystemService implements
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mHandler = new H(BackgroundThread.get().getLooper());
- mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
+ mAppStandby = AppStandbyInternal.newAppStandbyController(
+ UsageStatsService.class.getClassLoader(), getContext(),
+ BackgroundThread.get().getLooper());
mAppTimeLimit = new AppTimeLimitController(
new AppTimeLimitController.TimeLimitCallbackListener() {
@@ -1010,7 +1014,7 @@ public class UsageStatsService extends SystemService implements
pw.println("Flushed stats to disk");
return;
} else if ("is-app-standby-enabled".equals(arg)) {
- pw.println(mAppStandby.mAppIdleEnabled);
+ pw.println(mAppStandby.isAppIdleEnabled());
return;
} else if ("apptimelimit".equals(arg)) {
if (i + 1 >= args.length) {
diff --git a/telephony/java/android/telephony/CellBroadcastService.java b/telephony/java/android/telephony/CellBroadcastService.java
new file mode 100644
index 000000000000..d5e447e6c73d
--- /dev/null
+++ b/telephony/java/android/telephony/CellBroadcastService.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 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.telephony;
+
+import android.annotation.CallSuper;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * A service which exposes the cell broadcast handling module to the system.
+ * <p>
+ * To extend this class, you must declare the service in your manifest file to require the
+ * {@link android.Manifest.permission#BIND_CELL_BROADCAST_SERVICE} permission and include an intent
+ * filter with the {@link #CELL_BROADCAST_SERVICE_INTERFACE}.
+ * Implementations of this service should run in the phone process and with its UID.
+ * <p>
+ * For example:
+ * <pre>{@code
+ * <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:sharedUserId="android.uid.phone">
+ * <service android:name=".MyCellBroadcastService"
+ * android:label="@string/service_name"
+ * android:process="com.android.phone"
+ * android:exported="true"
+ * android:permission="android.permission.BIND_CELL_BROADCAST_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.telephony.CellBroadcastService" />
+ * </intent-filter>
+ * </service>
+ * </manifest>
+ * }</pre>
+ * @hide
+ */
+@SystemApi
+public abstract class CellBroadcastService extends Service {
+
+ public static final String CELL_BROADCAST_SERVICE_INTERFACE =
+ "android.telephony.CellBroadcastService";
+
+ private final ICellBroadcastService.Stub mStubWrapper;
+
+ public CellBroadcastService() {
+ mStubWrapper = new ICellBroadcastServiceWrapper();
+ }
+
+ /**
+ * Handle a GSM cell broadcast SMS message forwarded from the system.
+ * @param slotIndex the index of the slot which received the message
+ * @param message the SMS PDU
+ */
+ public abstract void onGsmCellBroadcastSms(int slotIndex, byte[] message);
+
+ /**
+ * Handle a CDMA cell broadcast SMS message forwarded from the system.
+ * @param slotIndex the index of the slot which received the message
+ * @param message the SMS PDU
+ */
+ public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] message);
+
+ /**
+ * If overriding this method, call through to the super method for any unknown actions.
+ * {@inheritDoc}
+ */
+ @Override
+ @CallSuper
+ public IBinder onBind(Intent intent) {
+ return mStubWrapper;
+ }
+
+ /**
+ * A wrapper around ICellBroadcastService that forwards calls to implementations of
+ * {@link CellBroadcastService}.
+ * @hide
+ */
+ public class ICellBroadcastServiceWrapper extends ICellBroadcastService.Stub {
+ /**
+ * Handle a GSM cell broadcast SMS.
+ * @param slotIndex the index of the slot which received the broadcast
+ * @param message the SMS message PDU
+ */
+ @Override
+ public void handleGsmCellBroadcastSms(int slotIndex, byte[] message) {
+ CellBroadcastService.this.onGsmCellBroadcastSms(slotIndex, message);
+ }
+
+ /**
+ * Handle a CDMA cell broadcast SMS.
+ * @param slotIndex the index of the slot which received the broadcast
+ * @param message the SMS message PDU
+ */
+ @Override
+ public void handleCdmaCellBroadcastSms(int slotIndex, byte[] message) {
+ CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, message);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/ICellBroadcastService.aidl b/telephony/java/android/telephony/ICellBroadcastService.aidl
new file mode 100644
index 000000000000..eff64a2e35ba
--- /dev/null
+++ b/telephony/java/android/telephony/ICellBroadcastService.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2019, 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.telephony;
+
+/**
+ * Service bound to by the system to allow custom handling of cell broadcast messages.
+ * <p>
+ * @see android.telephony.CellBroadcastService
+ * @hide
+ */
+interface ICellBroadcastService {
+
+ /** @see android.telephony.CellBroadcastService#onGsmCellBroadcastSms */
+ oneway void handleGsmCellBroadcastSms(int slotId, in byte[] message);
+
+ /** @see android.telephony.CellBroadcastService#onCdmaCellBroadcastSms */
+ oneway void handleCdmaCellBroadcastSms(int slotId, in byte[] message);
+}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index c2028f900508..1ba0a41024ed 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -374,7 +374,7 @@ public class PhoneStateListener {
@SystemApi
@TestApi
@RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
- public static final int LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER = 0x10000000;
+ public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 0x10000000;
/**
* Listen for the emergency number placed from an outgoing SMS.
@@ -387,7 +387,7 @@ public class PhoneStateListener {
@SystemApi
@TestApi
@RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
- public static final int LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER = 0x20000000;
+ public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 0x20000000;
/*
* Subscription used to listen to the phone state changes
@@ -878,6 +878,7 @@ public class PhoneStateListener {
* @hide
*/
@SystemApi
+ @TestApi
public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
// default implementation empty
}
@@ -889,6 +890,7 @@ public class PhoneStateListener {
* @hide
*/
@SystemApi
+ @TestApi
public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
// default implementation empty
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
index 6eea118787a7..c65c45fa015b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
@@ -461,7 +461,11 @@ public class GsmSmsCbMessage {
}
}
- static final class GeoFencingTriggerMessage {
+ /**
+ * Part of a GSM SMS cell broadcast message which may trigger geo-fencing logic.
+ * @hide
+ */
+ public static final class GeoFencingTriggerMessage {
/**
* Indicate the list of active alerts share their warning area coordinates which means the
* broadcast area is the union of the broadcast areas of the active alerts in this list.
@@ -476,6 +480,11 @@ public class GsmSmsCbMessage {
this.cbIdentifiers = cbIdentifiers;
}
+ /**
+ * Whether the trigger message indicates that the broadcast areas are shared between all
+ * active alerts.
+ * @return true if broadcast areas are to be shared
+ */
boolean shouldShareBroadcastArea() {
return type == TYPE_ACTIVE_ALERT_SHARE_WAC;
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index 6bbff4b91ee7..cbe521182667 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -74,22 +74,22 @@ public class SmsCbHeader {
/**
* Length of SMS-CB header
*/
- static final int PDU_HEADER_LENGTH = 6;
+ public static final int PDU_HEADER_LENGTH = 6;
/**
* GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1
*/
- static final int FORMAT_GSM = 1;
+ public static final int FORMAT_GSM = 1;
/**
* UMTS pdu format, as defined in 3gpp TS 23.041, section 9.4.2
*/
- static final int FORMAT_UMTS = 2;
+ public static final int FORMAT_UMTS = 2;
/**
- * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1.3
+ * ETWS pdu format, as defined in 3gpp TS 23.041, section 9.4.1.3
*/
- static final int FORMAT_ETWS_PRIMARY = 3;
+ public static final int FORMAT_ETWS_PRIMARY = 3;
/**
* Message type value as defined in 3gpp TS 25.324, section 11.1.
@@ -230,43 +230,43 @@ public class SmsCbHeader {
}
@UnsupportedAppUsage
- int getGeographicalScope() {
+ public int getGeographicalScope() {
return mGeographicalScope;
}
@UnsupportedAppUsage
- int getSerialNumber() {
+ public int getSerialNumber() {
return mSerialNumber;
}
@UnsupportedAppUsage
- int getServiceCategory() {
+ public int getServiceCategory() {
return mMessageIdentifier;
}
- int getDataCodingScheme() {
+ public int getDataCodingScheme() {
return mDataCodingScheme;
}
- DataCodingScheme getDataCodingSchemeStructedData() {
+ public DataCodingScheme getDataCodingSchemeStructedData() {
return mDataCodingSchemeStructedData;
}
@UnsupportedAppUsage
- int getPageIndex() {
+ public int getPageIndex() {
return mPageIndex;
}
@UnsupportedAppUsage
- int getNumberOfPages() {
+ public int getNumberOfPages() {
return mNrOfPages;
}
- SmsCbEtwsInfo getEtwsInfo() {
+ public SmsCbEtwsInfo getEtwsInfo() {
return mEtwsInfo;
}
- SmsCbCmasInfo getCmasInfo() {
+ public SmsCbCmasInfo getCmasInfo() {
return mCmasInfo;
}
@@ -274,7 +274,7 @@ public class SmsCbHeader {
* Return whether this broadcast is an emergency (PWS) message type.
* @return true if this message is emergency type; false otherwise
*/
- boolean isEmergencyMessage() {
+ public boolean isEmergencyMessage() {
return mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER
&& mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_PWS_LAST_IDENTIFIER;
}
@@ -292,7 +292,7 @@ public class SmsCbHeader {
* Return whether this broadcast is an ETWS primary notification.
* @return true if this message is an ETWS primary notification; false otherwise
*/
- boolean isEtwsPrimaryNotification() {
+ public boolean isEtwsPrimaryNotification() {
return mFormat == FORMAT_ETWS_PRIMARY;
}
@@ -300,7 +300,7 @@ public class SmsCbHeader {
* Return whether this broadcast is in UMTS format.
* @return true if this message is in UMTS format; false otherwise
*/
- boolean isUmtsFormat() {
+ public boolean isUmtsFormat() {
return mFormat == FORMAT_UMTS;
}
diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt
index c91eca94a9c0..039f7b2fc627 100755
--- a/tools/codegen/src/com/android/codegen/Main.kt
+++ b/tools/codegen/src/com/android/codegen/Main.kt
@@ -95,7 +95,13 @@ In addition, for any field mMyField(or myField) of type FieldType you can define
you can use with final fields.
Version: $CODEGEN_VERSION
-Questions? Feedback? Contact: eugenesusla@
+
+Questions? Feedback?
+Contact: eugenesusla@
+Bug/feature request: http://go/codegen-bug
+
+Slides: http://go/android-codegen
+In-depth example: http://go/SampleDataClass
"""
fun main(args: Array<String>) {