summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/appsearch/framework/api/current.txt8
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java6
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java6
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java6
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java42
-rw-r--r--apex/appsearch/synced_jetpack_changeid.txt2
-rw-r--r--apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java7
-rw-r--r--cmds/idmap2/Android.bp15
-rw-r--r--cmds/idmap2/AndroidTest.xml26
-rw-r--r--core/api/current.txt44
-rw-r--r--core/api/system-current.txt16
-rw-r--r--core/java/android/accessibilityservice/AccessibilityGestureEvent.java8
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java4
-rw-r--r--core/java/android/app/Notification.java543
-rw-r--r--core/java/android/app/smartspace/SmartspaceTargetEvent.java9
-rw-r--r--core/java/android/app/usage/IUsageStatsManager.aidl2
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java4
-rw-r--r--core/java/android/hardware/face/FaceAuthenticationFrame.aidl21
-rw-r--r--core/java/android/hardware/face/FaceDataFrame.java16
-rw-r--r--core/java/android/hardware/face/FaceEnrollFrame.aidl21
-rw-r--r--core/java/android/hardware/face/FaceEnrollStage.java6
-rw-r--r--core/java/android/hardware/face/FaceManager.java83
-rw-r--r--core/java/android/hardware/face/IFaceServiceReceiver.aidl4
-rw-r--r--core/java/android/net/vcn/VcnManager.java17
-rw-r--r--core/java/android/os/BatteryUsageStats.java45
-rw-r--r--core/java/android/os/BatteryUsageStatsQuery.java33
-rw-r--r--core/java/android/os/Debug.java20
-rw-r--r--core/java/android/os/Process.java11
-rw-r--r--core/java/android/os/UidBatteryConsumer.java34
-rw-r--r--core/java/android/os/UserBatteryConsumer.java115
-rw-r--r--core/java/android/provider/FontsContract.java3
-rw-r--r--core/java/android/view/IWindowSession.aidl8
-rw-r--r--core/java/android/view/View.java6
-rw-r--r--core/java/android/view/ViewRootImpl.java13
-rw-r--r--core/java/android/view/WindowlessWindowManager.java4
-rw-r--r--core/java/android/view/autofill/AutofillManager.java33
-rw-r--r--core/java/android/widget/RemoteViews.java109
-rw-r--r--core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java3
-rw-r--r--core/java/com/android/internal/os/BatteryUsageStatsProvider.java26
-rw-r--r--core/java/com/android/internal/os/BluetoothPowerCalculator.java5
-rw-r--r--core/java/com/android/internal/os/GnssPowerCalculator.java3
-rw-r--r--core/java/com/android/internal/os/IdlePowerCalculator.java3
-rw-r--r--core/java/com/android/internal/os/MemoryPowerCalculator.java3
-rw-r--r--core/java/com/android/internal/os/MobileRadioPowerCalculator.java3
-rw-r--r--core/java/com/android/internal/os/PhonePowerCalculator.java3
-rw-r--r--core/java/com/android/internal/os/PowerCalculator.java11
-rw-r--r--core/java/com/android/internal/os/ScreenPowerCalculator.java3
-rw-r--r--core/java/com/android/internal/os/SystemServicePowerCalculator.java5
-rw-r--r--core/java/com/android/internal/os/UserPowerCalculator.java32
-rw-r--r--core/java/com/android/internal/widget/CallLayout.java137
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java137
-rw-r--r--core/java/com/android/internal/widget/EmphasizedNotificationButton.java63
-rw-r--r--core/java/com/android/internal/widget/NotificationActionListLayout.java47
-rw-r--r--core/java/com/android/internal/widget/PeopleHelper.java179
-rw-r--r--core/jni/android_os_Debug.cpp38
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--core/res/res/drawable/btn_notification_emphasized.xml2
-rw-r--r--core/res/res/drawable/ic_call_answer.xml34
-rw-r--r--core/res/res/drawable/ic_call_decline.xml36
-rw-r--r--core/res/res/layout/notification_material_action_emphasized.xml1
-rw-r--r--core/res/res/layout/notification_template_conversation_header.xml166
-rw-r--r--core/res/res/layout/notification_template_conversation_icon_container.xml97
-rw-r--r--core/res/res/layout/notification_template_material_call.xml92
-rw-r--r--core/res/res/layout/notification_template_material_conversation.xml228
-rw-r--r--core/res/res/layout/notification_top_line_views.xml12
-rw-r--r--core/res/res/values/attrs.xml10
-rw-r--r--core/res/res/values/colors.xml5
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/dimens.xml11
-rw-r--r--core/res/res/values/ids.xml4
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--core/res/res/values/strings.xml12
-rw-r--r--core/res/res/values/symbols.xml20
-rw-r--r--core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java12
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java1
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java22
-rw-r--r--core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java145
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java18
-rw-r--r--keystore/java/android/security/KeyStoreSecurityLevel.java2
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreProvider.java12
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java3
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKey.java10
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java43
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java235
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java21
-rw-r--r--libs/hwui/DeviceInfo.h3
-rw-r--r--libs/hwui/Properties.cpp5
-rw-r--r--libs/hwui/Properties.h4
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp4
-rw-r--r--libs/hwui/jni/fonts/Font.cpp2
-rw-r--r--libs/hwui/jni/fonts/FontFamily.cpp4
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp49
-rw-r--r--libs/hwui/renderthread/CanvasContext.h7
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp5
-rw-r--r--libs/hwui/renderthread/RenderProxy.h17
-rw-r--r--libs/hwui/tests/common/TestScene.h1
-rw-r--r--libs/hwui/tests/macrobench/TestSceneRunner.cpp5
-rw-r--r--libs/hwui/tests/macrobench/main.cpp13
-rw-r--r--media/java/android/media/AudioDeviceInfo.java2
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java62
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java31
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/TextAnimator.kt23
-rw-r--r--packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java47
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java222
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java13
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java8
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java17
-rw-r--r--services/core/java/com/android/server/EntropyMixer.java51
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java29
-rw-r--r--services/core/java/com/android/server/am/AppProfiler.java23
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java41
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java16
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java32
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java16
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java32
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java31
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java12
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java84
-rw-r--r--services/core/java/com/android/server/location/eventlog/LocalEventLog.java130
-rw-r--r--services/core/java/com/android/server/location/eventlog/LocationEventLog.java (renamed from services/core/java/com/android/server/location/injector/LocationEventLog.java)240
-rw-r--r--services/core/java/com/android/server/location/injector/Injector.java3
-rw-r--r--services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java2
-rw-r--r--services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java1
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java40
-rw-r--r--services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java6
-rw-r--r--services/core/java/com/android/server/pm/InstantAppRegistry.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java8
-rw-r--r--services/core/java/com/android/server/pm/Settings.java1
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java89
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java13
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java65
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/Session.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java16
-rw-r--r--services/core/jni/Android.bp9
-rw-r--r--services/core/jni/com_android_server_am_CachedAppOptimizer.cpp9
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt666
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt3
-rw-r--r--services/tests/mockingservicestests/Android.bp9
-rw-r--r--services/tests/mockingservicestests/jni/Android.bp33
-rw-r--r--services/tests/mockingservicestests/jni/onload.cpp45
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java15
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/EntropyMixerTest.java2
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java10
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java33
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java16
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java78
-rw-r--r--telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl19
-rw-r--r--telephony/java/android/telephony/ims/ImsRegistrationAttributes.java252
-rw-r--r--telephony/java/android/telephony/ims/RegistrationManager.java123
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl5
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java85
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl14
-rw-r--r--tests/ApkVerityTest/Android.bp5
-rw-r--r--tests/ApkVerityTest/OWNERS3
-rw-r--r--tests/ApkVerityTest/block_device_writer/Android.bp6
-rw-r--r--tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java100
-rw-r--r--tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java41
-rw-r--r--tests/UpdatableSystemFontTest/Android.bp8
-rw-r--r--tests/UpdatableSystemFontTest/AndroidTest.xml1
-rw-r--r--tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java77
-rw-r--r--tests/vcn/java/android/net/vcn/VcnManagerTest.java6
204 files changed, 5347 insertions, 1776 deletions
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index a3b80139ced8..905000ac7c3d 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -208,8 +208,8 @@ package android.app.appsearch {
ctor public GetByUriRequest.Builder();
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.lang.String...);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
- method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.lang.String...);
- method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.GetByUriRequest build();
method @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String);
}
@@ -243,8 +243,8 @@ package android.app.appsearch {
public static final class RemoveByUriRequest.Builder {
ctor public RemoveByUriRequest.Builder();
- method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.lang.String...);
- method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.RemoveByUriRequest build();
method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
index 0fcf0613dd38..656608d82ad4 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -122,14 +122,14 @@ public final class GetByUriRequest {
/** Adds one or more URIs to the request. */
@NonNull
- public Builder addUri(@NonNull String... uris) {
+ public Builder addUris(@NonNull String... uris) {
Preconditions.checkNotNull(uris);
- return addUri(Arrays.asList(uris));
+ return addUris(Arrays.asList(uris));
}
/** Adds one or more URIs to the request. */
@NonNull
- public Builder addUri(@NonNull Collection<String> uris) {
+ public Builder addUris(@NonNull Collection<String> uris) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(uris);
mUris.addAll(uris);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
index 2104198d371c..198eee85be53 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -73,14 +73,14 @@ public final class RemoveByUriRequest {
/** Adds one or more URIs to the request. */
@NonNull
- public Builder addUri(@NonNull String... uris) {
+ public Builder addUris(@NonNull String... uris) {
Preconditions.checkNotNull(uris);
- return addUri(Arrays.asList(uris));
+ return addUris(Arrays.asList(uris));
}
/** Adds one or more URIs to the request. */
@NonNull
- public Builder addUri(@NonNull Collection<String> uris) {
+ public Builder addUris(@NonNull Collection<String> uris) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(uris);
mUris.addAll(uris);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
index 0328d5453ab8..4869aa38b5fd 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -163,7 +163,7 @@ public class SetSchemaResponse {
/** Adds deletedTypes to the list of deleted schema types. */
@NonNull
- public Builder addDeletedType(@NonNull Collection<String> deletedTypes) {
+ public Builder addDeletedTypes(@NonNull Collection<String> deletedTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mDeletedTypes.addAll(Preconditions.checkNotNull(deletedTypes));
return this;
@@ -171,7 +171,7 @@ public class SetSchemaResponse {
/** Adds incompatibleTypes to the list of incompatible schema types. */
@NonNull
- public Builder addIncompatibleType(@NonNull Collection<String> incompatibleTypes) {
+ public Builder addIncompatibleTypes(@NonNull Collection<String> incompatibleTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mIncompatibleTypes.addAll(Preconditions.checkNotNull(incompatibleTypes));
return this;
@@ -179,7 +179,7 @@ public class SetSchemaResponse {
/** Adds migratedTypes to the list of migrated schema types. */
@NonNull
- public Builder addMigratedType(@NonNull Collection<String> migratedTypes) {
+ public Builder addMigratedTypes(@NonNull Collection<String> migratedTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mMigratedTypes.addAll(Preconditions.checkNotNull(migratedTypes));
return this;
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
index 1b4d28401ea0..14dd472c9b9c 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
@@ -147,35 +147,41 @@ public final class BundleUtil {
if (bundle == null) {
return 0;
}
- int[] hashCodes = new int[bundle.size()];
- int i = 0;
+ int[] hashCodes = new int[bundle.size() + 1];
+ int hashCodeIdx = 0;
// Bundle inherit its hashCode() from Object.java, which only relative to their memory
// address. Bundle doesn't have an order, so we should iterate all keys and combine
// their value's hashcode into an array. And use the hashcode of the array to be
// the hashcode of the bundle.
- for (String key : bundle.keySet()) {
- Object value = bundle.get(key);
+ // Because bundle.keySet() doesn't guarantee any particular order, we need to sort the keys
+ // in case the iteration order varies from run to run.
+ String[] keys = bundle.keySet().toArray(new String[0]);
+ Arrays.sort(keys);
+ // Hash the keys so we can detect key-only differences
+ hashCodes[hashCodeIdx++] = Arrays.hashCode(keys);
+ for (int keyIdx = 0; keyIdx < keys.length; keyIdx++) {
+ Object value = bundle.get(keys[keyIdx]);
if (value instanceof Bundle) {
- hashCodes[i++] = deepHashCode((Bundle) value);
+ hashCodes[hashCodeIdx++] = deepHashCode((Bundle) value);
} else if (value instanceof int[]) {
- hashCodes[i++] = Arrays.hashCode((int[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((int[]) value);
} else if (value instanceof byte[]) {
- hashCodes[i++] = Arrays.hashCode((byte[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((byte[]) value);
} else if (value instanceof char[]) {
- hashCodes[i++] = Arrays.hashCode((char[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((char[]) value);
} else if (value instanceof long[]) {
- hashCodes[i++] = Arrays.hashCode((long[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((long[]) value);
} else if (value instanceof float[]) {
- hashCodes[i++] = Arrays.hashCode((float[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((float[]) value);
} else if (value instanceof short[]) {
- hashCodes[i++] = Arrays.hashCode((short[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((short[]) value);
} else if (value instanceof double[]) {
- hashCodes[i++] = Arrays.hashCode((double[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((double[]) value);
} else if (value instanceof boolean[]) {
- hashCodes[i++] = Arrays.hashCode((boolean[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((boolean[]) value);
} else if (value instanceof String[]) {
// Optimization to avoid Object[] handler creating an inner array for common cases
- hashCodes[i++] = Arrays.hashCode((String[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((String[]) value);
} else if (value instanceof Object[]) {
Object[] array = (Object[]) value;
int[] innerHashCodes = new int[array.length];
@@ -186,7 +192,7 @@ public final class BundleUtil {
innerHashCodes[j] = array[j].hashCode();
}
}
- hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes);
} else if (value instanceof ArrayList) {
ArrayList<?> list = (ArrayList<?>) value;
int[] innerHashCodes = new int[list.size()];
@@ -198,7 +204,7 @@ public final class BundleUtil {
innerHashCodes[j] = item.hashCode();
}
}
- hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes);
} else if (value instanceof SparseArray) {
SparseArray<?> array = (SparseArray<?>) value;
int[] innerHashCodes = new int[array.size() * 2];
@@ -211,9 +217,9 @@ public final class BundleUtil {
innerHashCodes[j * 2 + 1] = item.hashCode();
}
}
- hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes);
} else {
- hashCodes[i++] = value.hashCode();
+ hashCodes[hashCodeIdx++] = value.hashCode();
}
}
return Arrays.hashCode(hashCodes);
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 12699b7815fb..6ba557224582 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Ibe06fb9c574c8718191f833bb042fa10c300e4e2
+I2bf8bd9db1b71b7da4ab50dd7480e4529678413a
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
index 20fb90986f41..44d5180c3a36 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
@@ -59,7 +59,7 @@ public class AppSearchTestUtils {
session.getByUri(
new GetByUriRequest.Builder()
.setNamespace(namespace)
- .addUri(uris)
+ .addUris(uris)
.build()));
assertThat(result.getSuccesses()).hasSize(uris.length);
assertThat(result.getFailures()).isEmpty();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index bfc153f5f2f7..82ee5d8c1c7c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1990,8 +1990,11 @@ public class JobSchedulerService extends com.android.server.SystemService
}
final boolean shouldForceBatchJob;
- // Restricted jobs must always be batched
- if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) {
+ if (job.shouldTreatAsExpeditedJob()) {
+ // Never batch expedited jobs, even for RESTRICTED apps.
+ shouldForceBatchJob = false;
+ } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) {
+ // Restricted jobs must always be batched
shouldForceBatchJob = true;
} else if (job.getNumFailures() > 0) {
shouldForceBatchJob = false;
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index e21a6b288fb3..50f400122fe1 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -132,9 +132,6 @@ cc_test {
"tests/XmlParserTests.cpp",
"tests/ZipFileTests.cpp",
],
- required: [
- "idmap2",
- ],
static_libs: ["libgmock"],
target: {
android: {
@@ -163,9 +160,19 @@ cc_test {
shared_libs: [
"libz",
],
+ data: [
+ ":libz",
+ ":idmap2",
+ ],
},
},
- data: ["tests/data/**/*.apk"],
+ data: [
+ "tests/data/**/*.apk",
+ ],
+ compile_multilib: "first",
+ test_options: {
+ unit_test: true,
+ },
}
cc_binary {
diff --git a/cmds/idmap2/AndroidTest.xml b/cmds/idmap2/AndroidTest.xml
deleted file mode 100644
index 5147f4e6cb4c..000000000000
--- a/cmds/idmap2/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration description="Config for idmap2_tests">
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="idmap2_tests->/data/local/tmp/idmap2_tests" />
- </target_preparer>
- <option name="test-suite-tag" value="idmap2_tests" />
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="idmap2_tests" />
- </test>
-</configuration>
diff --git a/core/api/current.txt b/core/api/current.txt
index 838dd55e34d5..77b5032cf9e8 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -435,6 +435,7 @@ package android {
field public static final int clickable = 16842981; // 0x10100e5
field public static final int clipChildren = 16842986; // 0x10100ea
field public static final int clipOrientation = 16843274; // 0x101020a
+ field public static final int clipToOutline = 16844328; // 0x1010628
field public static final int clipToPadding = 16842987; // 0x10100eb
field public static final int closeIcon = 16843905; // 0x1010481
field @Deprecated public static final int codes = 16843330; // 0x1010242
@@ -2936,12 +2937,12 @@ package android.accessibilityservice {
field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
- field public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; // 0x2b
field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c
field public static final int GESTURE_2_FINGER_SWIPE_UP = 25; // 0x19
field public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; // 0x15
+ field public static final int GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD = 43; // 0x2b
field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29
field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
@@ -5554,15 +5555,19 @@ package android.app {
field public static final int DEFAULT_LIGHTS = 4; // 0x4
field public static final int DEFAULT_SOUND = 1; // 0x1
field public static final int DEFAULT_VIBRATE = 2; // 0x2
+ field public static final String EXTRA_ANSWER_INTENT = "android.answerIntent";
field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
field public static final String EXTRA_BIG_TEXT = "android.bigText";
+ field public static final String EXTRA_CALL_PERSON = "android.callPerson";
field public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
field public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
field public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
field public static final String EXTRA_COLORIZED = "android.colorized";
field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+ field public static final String EXTRA_DECLINE_INTENT = "android.declineIntent";
+ field public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
field public static final String EXTRA_INFO_TEXT = "android.infoText";
field public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
@@ -5594,6 +5599,8 @@ package android.app {
field public static final String EXTRA_TEXT_LINES = "android.textLines";
field public static final String EXTRA_TITLE = "android.title";
field public static final String EXTRA_TITLE_BIG = "android.title.big";
+ field public static final String EXTRA_VERIFICATION_ICON = "android.verificationIcon";
+ field public static final String EXTRA_VERIFICATION_TEXT = "android.verificationText";
field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
field public static final int FLAG_BUBBLE = 4096; // 0x1000
field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
@@ -5842,6 +5849,14 @@ package android.app {
method @NonNull public android.app.Notification.Builder setWhen(long);
}
+ public static class Notification.CallStyle extends android.app.Notification.Style {
+ method @NonNull public static android.app.Notification.CallStyle forIncomingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent);
+ method @NonNull public static android.app.Notification.CallStyle forOngoingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent);
+ method @NonNull public static android.app.Notification.CallStyle forScreeningCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent);
+ method @NonNull public android.app.Notification.CallStyle setVerificationIcon(@Nullable android.graphics.drawable.Icon);
+ method @NonNull public android.app.Notification.CallStyle setVerificationText(@Nullable CharSequence);
+ }
+
public static final class Notification.CarExtender implements android.app.Notification.Extender {
ctor public Notification.CarExtender();
ctor public Notification.CarExtender(android.app.Notification);
@@ -40338,6 +40353,7 @@ package android.telephony {
field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
+ field public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL = "store_sim_pin_for_unattended_reboot_bool";
field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
field public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = "support_add_conference_participants_bool";
@@ -42063,7 +42079,7 @@ package android.telephony {
method @Deprecated public int getPhoneCount();
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
- method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState();
method @Nullable public android.telephony.SignalStrength getSignalStrength();
method public int getSimCarrierId();
method @Nullable public CharSequence getSimCarrierIdName();
@@ -42922,6 +42938,16 @@ package android.telephony.ims {
field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
}
+ public final class ImsRegistrationAttributes implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getAttributeFlags();
+ method @NonNull public java.util.Set<java.lang.String> getFeatureTags();
+ method public int getTransportType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1; // 0x1
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsRegistrationAttributes> CREATOR;
+ }
+
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
}
@@ -42931,7 +42957,6 @@ package android.telephony.ims {
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
- field public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1; // 0x1
field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0
field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2
field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1
@@ -42940,9 +42965,9 @@ package android.telephony.ims {
public static class RegistrationManager.RegistrationCallback {
ctor public RegistrationManager.RegistrationCallback();
method @Deprecated public void onRegistered(int);
- method public void onRegistered(int, int);
+ method public void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes);
method @Deprecated public void onRegistering(int);
- method public void onRegistering(int, int);
+ method public void onRegistering(@NonNull android.telephony.ims.ImsRegistrationAttributes);
method public void onTechnologyChangeFailed(int, @NonNull android.telephony.ims.ImsReasonInfo);
method public void onUnregistered(@NonNull android.telephony.ims.ImsReasonInfo);
}
@@ -50687,6 +50712,7 @@ package android.view.autofill {
method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
+ field public static final String EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET = "android.view.autofill.extra.AUTHENTICATION_RESULT_EPHEMERAL_DATASET";
field public static final String EXTRA_CLIENT_STATE = "android.view.autofill.extra.CLIENT_STATE";
}
@@ -54634,6 +54660,8 @@ package android.widget {
method public void setTextViewText(@IdRes int, CharSequence);
method public void setTextViewTextSize(@IdRes int, int, float);
method public void setUri(@IdRes int, String, android.net.Uri);
+ method public void setViewOutlinePreferredRadius(@IdRes int, float, int);
+ method public void setViewOutlinePreferredRadiusDimen(@IdRes int, @DimenRes int);
method public void setViewPadding(@IdRes int, @Px int, @Px int, @Px int, @Px int);
method public void setViewVisibility(@IdRes int, int);
method public void showNext(@IdRes int);
@@ -54658,6 +54686,12 @@ package android.widget {
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public static @interface RemoteViews.RemoteView {
}
+ public static final class RemoteViews.RemoteViewOutlineProvider extends android.view.ViewOutlineProvider {
+ ctor public RemoteViews.RemoteViewOutlineProvider(float);
+ method public void getOutline(@NonNull android.view.View, @NonNull android.graphics.Outline);
+ method public float getRadius();
+ }
+
public abstract class RemoteViewsService extends android.app.Service {
ctor public RemoteViewsService();
method public android.os.IBinder onBind(android.content.Intent);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5bb3e0517032..74ccbb14f816 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -11869,6 +11869,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean matchesCurrentSimOperator(@NonNull String, int, @Nullable String);
method public boolean needsOtaServiceProvisioning();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
+ method @RequiresPermission(android.Manifest.permission.REBOOT) public int prepareForUnattendedReboot();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
@@ -11993,6 +11994,9 @@ package android.telephony {
field public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2; // 0x2
field public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3; // 0x3
field public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1; // 0x1
+ field public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2; // 0x2
+ field public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1; // 0x1
+ field public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0; // 0x0
field public static final int RADIO_POWER_OFF = 0; // 0x0
field public static final int RADIO_POWER_ON = 1; // 0x1
field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
@@ -12974,6 +12978,16 @@ package android.telephony.ims {
field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
}
+ public final class ImsRegistrationAttributes implements android.os.Parcelable {
+ method public int getRegistrationTechnology();
+ }
+
+ public static final class ImsRegistrationAttributes.Builder {
+ ctor public ImsRegistrationAttributes.Builder(int);
+ method @NonNull public android.telephony.ims.ImsRegistrationAttributes build();
+ method @NonNull public android.telephony.ims.ImsRegistrationAttributes.Builder setFeatureTags(@NonNull java.util.Set<java.lang.String>);
+ }
+
public class ImsService extends android.app.Service {
ctor public ImsService();
method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
@@ -13757,7 +13771,9 @@ package android.telephony.ims.stub {
ctor public ImsRegistrationImplBase();
method public final void onDeregistered(android.telephony.ims.ImsReasonInfo);
method public final void onRegistered(int);
+ method public final void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes);
method public final void onRegistering(int);
+ method public final void onRegistering(@NonNull android.telephony.ims.ImsRegistrationAttributes);
method public final void onSubscriberAssociatedUriChanged(android.net.Uri[]);
method public final void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
method public void triggerFullNetworkRegistration(@IntRange(from=100, to=699) int, @Nullable String);
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 4b2d74160006..768ec3851991 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -20,12 +20,12 @@ package android.accessibilityservice;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
-import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
@@ -97,10 +97,10 @@ public final class AccessibilityGestureEvent implements Parcelable {
GESTURE_UNKNOWN,
GESTURE_TOUCH_EXPLORATION,
GESTURE_2_FINGER_SINGLE_TAP,
- GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD,
GESTURE_2_FINGER_DOUBLE_TAP,
GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD,
GESTURE_2_FINGER_TRIPLE_TAP,
+ GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD,
GESTURE_3_FINGER_SINGLE_TAP,
GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD,
GESTURE_3_FINGER_DOUBLE_TAP,
@@ -232,8 +232,8 @@ public final class AccessibilityGestureEvent implements Parcelable {
case GESTURE_PASSTHROUGH: return "GESTURE_PASSTHROUGH";
case GESTURE_TOUCH_EXPLORATION: return "GESTURE_TOUCH_EXPLORATION";
case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP";
- case GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD:
- return "GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD";
+ case GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD:
+ return "GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD";
case GESTURE_2_FINGER_DOUBLE_TAP: return "GESTURE_2_FINGER_DOUBLE_TAP";
case GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD:
return "GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD";
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 1fa7fa2b744f..dab4a5dc316c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -445,8 +445,8 @@ public abstract class AccessibilityService extends Service {
/** The user has performed a three-finger double tap and hold gesture on the touch screen. */
public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41;
- /** The user has performed a two-finger single-tap and hold gesture on the touch screen. */
- public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43;
+ /** The user has performed a two-finger triple-tap and hold gesture on the touch screen. */
+ public static final int GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD = 43;
/** The user has performed a three-finger single-tap and hold gesture on the touch screen. */
public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 49f508d83f91..c242fd466c41 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -22,7 +22,10 @@ import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.ColorInt;
+import android.annotation.ColorRes;
import android.annotation.DimenRes;
import android.annotation.Dimension;
import android.annotation.DrawableRes;
@@ -33,6 +36,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringRes;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -403,6 +407,7 @@ public class Notification implements Parcelable
STANDARD_LAYOUTS.add(R.layout.notification_template_material_conversation);
STANDARD_LAYOUTS.add(R.layout.notification_template_material_media);
STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_call);
STANDARD_LAYOUTS.add(R.layout.notification_template_header);
}
@@ -649,7 +654,7 @@ public class Notification implements Parcelable
private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList(
BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
- MessagingStyle.class);
+ MessagingStyle.class, CallStyle.class);
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {FLAG_SHOW_LIGHTS, FLAG_ONGOING_EVENT,
@@ -1318,6 +1323,53 @@ public class Notification implements Parcelable
public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
/**
+ * {@link #extras} key: the type of call represented by the
+ * {@link android.app.Notification.CallStyle} notification. This extra is an int.
+ * @hide
+ */
+ public static final String EXTRA_CALL_TYPE = "android.callType";
+
+ /**
+ * {@link #extras} key: the person to be displayed as calling for the
+ * {@link android.app.Notification.CallStyle} notification. This extra is a {@link Person}.
+ */
+ public static final String EXTRA_CALL_PERSON = "android.callPerson";
+
+ /**
+ * {@link #extras} key: the icon to be displayed as a verification status of the caller on a
+ * {@link android.app.Notification.CallStyle} notification. This extra is an {@link Icon}.
+ */
+ public static final String EXTRA_VERIFICATION_ICON = "android.verificationIcon";
+
+ /**
+ * {@link #extras} key: the text to be displayed as a verification status of the caller on a
+ * {@link android.app.Notification.CallStyle} notification. This extra is a
+ * {@link CharSequence}.
+ */
+ public static final String EXTRA_VERIFICATION_TEXT = "android.verificationText";
+
+ /**
+ * {@link #extras} key: the intent to be sent when the users answers a
+ * {@link android.app.Notification.CallStyle} notification. This extra is a
+ * {@link PendingIntent}.
+ */
+ public static final String EXTRA_ANSWER_INTENT = "android.answerIntent";
+
+ /**
+ * {@link #extras} key: the intent to be sent when the users declines a
+ * {@link android.app.Notification.CallStyle} notification. This extra is a
+ * {@link PendingIntent}.
+ */
+ public static final String EXTRA_DECLINE_INTENT = "android.declineIntent";
+
+ /**
+ * {@link #extras} key: the intent to be sent when the users hangs up a
+ * {@link android.app.Notification.CallStyle} notification. This extra is a
+ * {@link PendingIntent}.
+ */
+ public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
+
+ /**
* {@link #extras} key: whether the notification should be colorized as
* supplied to {@link Builder#setColorized(boolean)}.
*/
@@ -5876,11 +5928,11 @@ public class Notification implements Parcelable
return summary;
}
- private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
+ private RemoteViews generateActionButton(Action action, boolean emphasizedMode,
StandardTemplateParams p) {
final boolean tombstone = (action.actionIntent == null);
RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
- emphazisedMode ? getEmphasizedActionLayoutResource()
+ emphasizedMode ? getEmphasizedActionLayoutResource()
: tombstone ? getActionTombstoneLayoutResource()
: getActionLayoutResource());
if (!tombstone) {
@@ -5890,43 +5942,42 @@ public class Notification implements Parcelable
if (action.mRemoteInputs != null) {
button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
}
- if (emphazisedMode) {
+ if (emphasizedMode) {
// change the background bgColor
CharSequence title = action.title;
- ColorStateList[] outResultColor = null;
+ ColorStateList[] outResultColor = new ColorStateList[1];
int background = resolveBackgroundColor(p);
if (isLegacy()) {
title = ContrastColorUtil.clearColorSpans(title);
} else {
- outResultColor = new ColorStateList[1];
title = ensureColorSpanContrast(title, background, outResultColor);
}
button.setTextViewText(R.id.action0, processTextSpans(title));
- setTextViewColorPrimary(button, R.id.action0, p);
- int rippleColor;
- boolean hasColorOverride = outResultColor != null && outResultColor[0] != null;
+ int textColor = getPrimaryTextColor(p);
+ boolean hasColorOverride = outResultColor[0] != null;
if (hasColorOverride) {
// There's a span spanning the full text, let's take it and use it as the
// background color
background = outResultColor[0].getDefaultColor();
- int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
+ textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
background, mInNightMode);
- button.setTextColor(R.id.action0, textColor);
- rippleColor = textColor;
} else if (getRawColor(p) != COLOR_DEFAULT && !isColorized(p)
&& mTintActionButtons && !mInNightMode) {
- rippleColor = resolveContrastColor(p);
- button.setTextColor(R.id.action0, rippleColor);
- } else {
- rippleColor = getPrimaryTextColor(p);
+ textColor = resolveContrastColor(p);
}
+ button.setTextColor(R.id.action0, textColor);
// We only want about 20% alpha for the ripple
- rippleColor = (rippleColor & 0x00ffffff) | 0x33000000;
+ final int rippleColor = (textColor & 0x00ffffff) | 0x33000000;
button.setColorStateList(R.id.action0, "setRippleColor",
ColorStateList.valueOf(rippleColor));
button.setColorStateList(R.id.action0, "setButtonBackground",
ColorStateList.valueOf(background));
button.setBoolean(R.id.action0, "setHasStroke", !hasColorOverride);
+ if (p.mAllowActionIcons) {
+ button.setImageViewIcon(R.id.action0, action.getIcon());
+ boolean priority = action.getExtras().getBoolean(CallStyle.KEY_ACTION_PRIORITY);
+ button.setBoolean(R.id.action0, "setWrapModePriority", priority);
+ }
} else {
button.setTextViewText(R.id.action0, processTextSpans(
processLegacyText(action.title)));
@@ -5936,8 +5987,12 @@ public class Notification implements Parcelable
button.setTextColor(R.id.action0, resolveContrastColor(p));
}
}
- button.setIntTag(R.id.action0, R.id.notification_action_index_tag,
- mActions.indexOf(action));
+ // CallStyle notifications add action buttons which don't actually exist in mActions,
+ // so we have to omit the index in that case.
+ int actionIndex = mActions.indexOf(action);
+ if (actionIndex != -1) {
+ button.setIntTag(R.id.action0, R.id.notification_action_index_tag, actionIndex);
+ }
return button;
}
@@ -6371,6 +6426,10 @@ public class Notification implements Parcelable
return R.layout.notification_template_material_conversation;
}
+ private int getCallLayoutResource() {
+ return R.layout.notification_template_material_call;
+ }
+
private int getActionLayoutResource() {
return R.layout.notification_material_action;
}
@@ -8039,6 +8098,10 @@ public class Notification implements Parcelable
: mBuilder.getMessagingLayoutResource(),
p,
bindResult);
+ if (isConversationLayout) {
+ mBuilder.setTextViewColorPrimary(contentView, R.id.conversation_text, p);
+ mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
+ }
addExtras(mBuilder.mN.extras);
if (!isConversationLayout) {
@@ -8925,6 +8988,441 @@ public class Notification implements Parcelable
}
}
+
+
+ /**
+ * Helper class for generating large-format notifications that include a large image attachment.
+ *
+ * Here's how you'd set the <code>CallStyle</code> on a notification:
+ * <pre class="prettyprint">
+ * Notification notif = new Notification.Builder(mContext)
+ * .setSmallIcon(R.drawable.new_post)
+ * .setStyle(Notification.CallStyle.forIncomingCall(caller, declineIntent, answerIntent))
+ * .build();
+ * </pre>
+ */
+ public static class CallStyle extends Style {
+ private static final int CALL_TYPE_INCOMING = 1;
+ private static final int CALL_TYPE_ONGOING = 2;
+ private static final int CALL_TYPE_SCREENING = 3;
+
+ /**
+ * This is a key used privately on the action.extras to give spacing priority
+ * to the required call actions
+ */
+ private static final String KEY_ACTION_PRIORITY = "key_action_priority";
+
+ private int mCallType;
+ private Person mPerson;
+ private PendingIntent mAnswerIntent;
+ private PendingIntent mDeclineIntent;
+ private PendingIntent mHangUpIntent;
+ private Icon mVerificationIcon;
+ private CharSequence mVerificationText;
+
+ CallStyle() {
+ }
+
+ /**
+ * Create a CallStyle for an incoming call.
+ * This notification will have a decline and an answer action, will allow a single
+ * custom {@link Builder#addAction(Action) action}, and will have a default
+ * {@link Builder#setContentText(CharSequence) content text} for an incoming call.
+ *
+ * @param person The person displayed as the caller.
+ * The person also needs to have a non-empty name associated with it.
+ * @param declineIntent The intent to be sent when the user taps the decline action
+ * @param answerIntent The intent to be sent when the user taps the answer action
+ */
+ @NonNull
+ public static CallStyle forIncomingCall(@NonNull Person person,
+ @NonNull PendingIntent declineIntent, @NonNull PendingIntent answerIntent) {
+ return new CallStyle(CALL_TYPE_INCOMING, person,
+ null /* hangUpIntent */,
+ requireNonNull(declineIntent, "declineIntent is required"),
+ requireNonNull(answerIntent, "answerIntent is required")
+ );
+ }
+
+ /**
+ * Create a CallStyle for an ongoing call.
+ * This notification will have a hang up action, will allow up to two
+ * custom {@link Builder#addAction(Action) actions}, and will have a default
+ * {@link Builder#setContentText(CharSequence) content text} for an ongoing call.
+ *
+ * @param person The person displayed as being on the other end of the call.
+ * The person also needs to have a non-empty name associated with it.
+ * @param hangUpIntent The intent to be sent when the user taps the hang up action
+ */
+ @NonNull
+ public static CallStyle forOngoingCall(@NonNull Person person,
+ @NonNull PendingIntent hangUpIntent) {
+ return new CallStyle(CALL_TYPE_ONGOING, person,
+ requireNonNull(hangUpIntent, "hangUpIntent is required"),
+ null /* declineIntent */,
+ null /* answerIntent */
+ );
+ }
+
+ /**
+ * Create a CallStyle for a call that is being screened.
+ * This notification will have a hang up and an answer action, will allow a single
+ * custom {@link Builder#addAction(Action) action}, and will have a default
+ * {@link Builder#setContentText(CharSequence) content text} for a call that is being
+ * screened.
+ *
+ * @param person The person displayed as the caller.
+ * The person also needs to have a non-empty name associated with it.
+ * @param hangUpIntent The intent to be sent when the user taps the hang up action
+ * @param answerIntent The intent to be sent when the user taps the answer action
+ */
+ @NonNull
+ public static CallStyle forScreeningCall(@NonNull Person person,
+ @NonNull PendingIntent hangUpIntent, @NonNull PendingIntent answerIntent) {
+ return new CallStyle(CALL_TYPE_SCREENING, person,
+ requireNonNull(hangUpIntent, "hangUpIntent is required"),
+ null /* declineIntent */,
+ requireNonNull(answerIntent, "answerIntent is required")
+ );
+ }
+
+ /**
+ * @param person The person displayed for the incoming call.
+ * The user also needs to have a non-empty name associated with it.
+ * @param hangUpIntent The intent to be sent when the user taps the hang up action
+ * @param declineIntent The intent to be sent when the user taps the decline action
+ * @param answerIntent The intent to be sent when the user taps the answer action
+ */
+ private CallStyle(int callType, @NonNull Person person,
+ @Nullable PendingIntent hangUpIntent, @Nullable PendingIntent declineIntent,
+ @Nullable PendingIntent answerIntent) {
+ if (person == null || TextUtils.isEmpty(person.getName())) {
+ throw new IllegalArgumentException("person must have a non-empty a name");
+ }
+ mCallType = callType;
+ mPerson = person;
+ mAnswerIntent = answerIntent;
+ mDeclineIntent = declineIntent;
+ mHangUpIntent = hangUpIntent;
+ }
+
+ /**
+ * Optional icon to be displayed with {@link #setVerificationText(CharSequence) text}
+ * as a verification status of the caller.
+ */
+ @NonNull
+ public CallStyle setVerificationIcon(@Nullable Icon verificationIcon) {
+ mVerificationIcon = verificationIcon;
+ return this;
+ }
+
+ /**
+ * Optional text to be displayed with an {@link #setVerificationIcon(Icon) icon}
+ * as a verification status of the caller.
+ */
+ @NonNull
+ public CallStyle setVerificationText(@Nullable CharSequence verificationText) {
+ mVerificationText = safeCharSequence(verificationText);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean displayCustomViewInline() {
+ // This is a lie; True is returned to make sure that the custom view is not used
+ // instead of the template, but it will not actually be included.
+ return true;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void purgeResources() {
+ super.purgeResources();
+ if (mVerificationIcon != null) {
+ mVerificationIcon.convertToAshmem();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void reduceImageSizes(Context context) {
+ super.reduceImageSizes(context);
+ if (mVerificationIcon != null) {
+ int rightIconSize = context.getResources().getDimensionPixelSize(
+ ActivityManager.isLowRamDeviceStatic()
+ ? R.dimen.notification_right_icon_size_low_ram
+ : R.dimen.notification_right_icon_size);
+ mVerificationIcon.scaleDownIfNecessary(rightIconSize, rightIconSize);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public RemoteViews makeContentView(boolean increasedHeight) {
+ return makeCallLayout();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
+ return makeCallLayout();
+ }
+
+ /**
+ * @hide
+ */
+ public RemoteViews makeBigContentView() {
+ return makeCallLayout();
+ }
+
+ @NonNull
+ private Action makeNegativeAction() {
+ if (mDeclineIntent == null) {
+ return makeAction(R.drawable.ic_call_decline,
+ R.string.call_notification_hang_up_action,
+ R.color.call_notification_decline_color, mHangUpIntent);
+ } else {
+ return makeAction(R.drawable.ic_call_decline,
+ R.string.call_notification_decline_action,
+ R.color.call_notification_decline_color, mDeclineIntent);
+ }
+ }
+
+ @Nullable
+ private Action makeAnswerAction() {
+ return mAnswerIntent == null ? null : makeAction(R.drawable.ic_call_answer,
+ R.string.call_notification_answer_action,
+ R.color.call_notification_answer_color, mAnswerIntent);
+ }
+
+ @NonNull
+ private Action makeAction(@DrawableRes int icon, @StringRes int title,
+ @ColorRes int colorRes, PendingIntent intent) {
+ Action action = new Action.Builder(Icon.createWithResource("", icon),
+ new SpannableStringBuilder().append(mBuilder.mContext.getString(title),
+ new ForegroundColorSpan(mBuilder.mContext.getColor(colorRes)),
+ SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE),
+ intent).build();
+ action.getExtras().putBoolean(KEY_ACTION_PRIORITY, true);
+ return action;
+ }
+
+ private ArrayList<Action> makeActionsList() {
+ final Action negativeAction = makeNegativeAction();
+ final Action answerAction = makeAnswerAction();
+
+ ArrayList<Action> actions = new ArrayList<>(MAX_ACTION_BUTTONS);
+ final Action lastAction;
+ if (answerAction == null) {
+ // If there's no answer action, put the hang up / decline action at the end
+ lastAction = negativeAction;
+ } else {
+ // Otherwise put the answer action at the end, and put the decline action at start.
+ actions.add(negativeAction);
+ lastAction = answerAction;
+ }
+ // For consistency with the standard actions bar, contextual actions are ignored.
+ for (Action action : Builder.filterOutContextualActions(mBuilder.mActions)) {
+ if (actions.size() >= MAX_ACTION_BUTTONS - 1) {
+ break;
+ }
+ actions.add(action);
+ }
+ actions.add(lastAction);
+ return actions;
+ }
+
+ private RemoteViews makeCallLayout() {
+ Bundle extras = mBuilder.mN.extras;
+ CharSequence text = mBuilder.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
+ if (text == null) {
+ text = getDefaultText();
+ }
+
+ // Bind standard template
+ StandardTemplateParams p = mBuilder.mParams.reset()
+ .viewType(StandardTemplateParams.VIEW_TYPE_BIG)
+ .allowActionIcons(true)
+ .hideLargeIcon(true)
+ .text(text)
+ .summaryText(mBuilder.processLegacyText(mVerificationText));
+ // TODO(b/179178086): hide the snooze button
+ RemoteViews contentView = mBuilder.applyStandardTemplate(
+ mBuilder.getCallLayoutResource(), p, null /* result */);
+
+ // Bind actions.
+ mBuilder.resetStandardTemplateWithActions(contentView);
+ bindCallActions(contentView, p);
+
+ // Bind some extra conversation-specific header fields.
+ mBuilder.setTextViewColorPrimary(contentView, R.id.conversation_text, p);
+ mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
+ contentView.setViewVisibility(R.id.app_name_divider, View.VISIBLE);
+ bindCallerVerification(contentView, p);
+
+ // Bind some custom CallLayout properties
+ contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
+ mBuilder.isColorized(p)
+ ? mBuilder.getPrimaryTextColor(p)
+ : mBuilder.resolveContrastColor(p));
+ contentView.setInt(R.id.status_bar_latest_event_content,
+ "setNotificationBackgroundColor", mBuilder.resolveBackgroundColor(p));
+ contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
+ mBuilder.mN.mLargeIcon);
+ contentView.setBundle(R.id.status_bar_latest_event_content, "setData",
+ mBuilder.mN.extras);
+
+ return contentView;
+ }
+
+ private void bindCallActions(RemoteViews view, StandardTemplateParams p) {
+ view.setViewVisibility(R.id.actions_container, View.VISIBLE);
+ view.setViewVisibility(R.id.actions, View.VISIBLE);
+ view.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+ RemoteViews.MARGIN_BOTTOM, 0);
+
+ // Clear view padding to allow buttons to start on the left edge.
+ // This must be done before 'setEmphasizedMode' which sets top/bottom margins.
+ view.setViewPadding(R.id.actions, 0, 0, 0, 0);
+ // Add an optional indent that will make buttons start at the correct column when
+ // there is enough space to do so (and fall back to the left edge if not).
+ view.setInt(R.id.actions, "setCollapsibleIndentDimen",
+ R.dimen.call_notification_collapsible_indent);
+
+ // Emphasize so that buttons have borders or colored backgrounds
+ boolean emphasizedMode = true;
+ view.setBoolean(R.id.actions, "setEmphasizedMode", emphasizedMode);
+ // Use "wrap_content" (unlike normal emphasized mode) and allow prioritizing the
+ // required actions (Answer, Decline, and Hang Up).
+ view.setBoolean(R.id.actions, "setPrioritizedWrapMode", true);
+
+ // Create the buttons for the generated actions list.
+ int i = 0;
+ for (Action action : makeActionsList()) {
+ final RemoteViews button = mBuilder.generateActionButton(action, emphasizedMode, p);
+ if (i > 0) {
+ // Clear start margin from non-first buttons to reduce the gap between buttons.
+ // (8dp remaining gap is from all buttons' standard 4dp inset).
+ button.setViewLayoutMarginDimen(R.id.action0, RemoteViews.MARGIN_START, 0);
+ }
+ view.addView(R.id.actions, button);
+ ++i;
+ }
+ }
+
+ private void bindCallerVerification(RemoteViews contentView, StandardTemplateParams p) {
+ if (mVerificationIcon != null) {
+ contentView.setImageViewIcon(R.id.verification_icon, mVerificationIcon);
+ contentView.setDrawableTint(R.id.verification_icon, false /* targetBackground */,
+ mBuilder.getSecondaryTextColor(p), PorterDuff.Mode.SRC_ATOP);
+ contentView.setViewVisibility(R.id.verification_icon, View.VISIBLE);
+ } else {
+ contentView.setViewVisibility(R.id.verification_icon, View.GONE);
+ }
+ if (!TextUtils.isEmpty(mVerificationText)) {
+ contentView.setTextViewText(R.id.verification_text, mVerificationText);
+ mBuilder.setTextViewColorSecondary(contentView, R.id.verification_text, p);
+ contentView.setViewVisibility(R.id.verification_text, View.VISIBLE);
+ } else {
+ contentView.setViewVisibility(R.id.verification_text, View.GONE);
+ }
+ }
+
+ @Nullable
+ private String getDefaultText() {
+ switch (mCallType) {
+ case CALL_TYPE_INCOMING:
+ return mBuilder.mContext.getString(R.string.call_notification_incoming_text);
+ case CALL_TYPE_ONGOING:
+ return mBuilder.mContext.getString(R.string.call_notification_ongoing_text);
+ case CALL_TYPE_SCREENING:
+ return mBuilder.mContext.getString(R.string.call_notification_screening_text);
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public void addExtras(Bundle extras) {
+ super.addExtras(extras);
+ extras.putInt(EXTRA_CALL_TYPE, mCallType);
+ extras.putParcelable(EXTRA_CALL_PERSON, mPerson);
+ if (mVerificationIcon != null) {
+ extras.putParcelable(EXTRA_VERIFICATION_ICON, mVerificationIcon);
+ }
+ if (mVerificationText != null) {
+ extras.putCharSequence(EXTRA_VERIFICATION_TEXT, mVerificationText);
+ }
+ if (mAnswerIntent != null) {
+ extras.putParcelable(EXTRA_ANSWER_INTENT, mAnswerIntent);
+ }
+ if (mDeclineIntent != null) {
+ extras.putParcelable(EXTRA_DECLINE_INTENT, mDeclineIntent);
+ }
+ if (mHangUpIntent != null) {
+ extras.putParcelable(EXTRA_HANG_UP_INTENT, mHangUpIntent);
+ }
+ fixTitleAndTextExtras(extras);
+ }
+
+ private void fixTitleAndTextExtras(Bundle extras) {
+ CharSequence sender = mPerson != null ? mPerson.getName() : null;
+ if (sender != null) {
+ extras.putCharSequence(EXTRA_TITLE, sender);
+ }
+ if (extras.getCharSequence(EXTRA_TEXT) == null) {
+ extras.putCharSequence(EXTRA_TEXT, getDefaultText());
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void restoreFromExtras(Bundle extras) {
+ super.restoreFromExtras(extras);
+ mCallType = extras.getInt(EXTRA_CALL_TYPE);
+ mPerson = extras.getParcelable(EXTRA_CALL_PERSON);
+ mVerificationIcon = extras.getParcelable(EXTRA_VERIFICATION_ICON);
+ mVerificationText = extras.getCharSequence(EXTRA_VERIFICATION_TEXT);
+ mAnswerIntent = extras.getParcelable(EXTRA_ANSWER_INTENT);
+ mDeclineIntent = extras.getParcelable(EXTRA_DECLINE_INTENT);
+ mHangUpIntent = extras.getParcelable(EXTRA_HANG_UP_INTENT);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean hasSummaryInHeader() {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean areNotificationsVisiblyDifferent(Style other) {
+ if (other == null || getClass() != other.getClass()) {
+ return true;
+ }
+ CallStyle otherS = (CallStyle) other;
+ return !Objects.equals(mCallType, otherS.mCallType)
+ || !Objects.equals(mPerson, otherS.mPerson)
+ || !Objects.equals(mVerificationText, otherS.mVerificationText);
+ }
+ }
+
/**
* Notification style for custom views that are decorated by the system
*
@@ -11376,6 +11874,7 @@ public class Notification implements Parcelable
boolean mHideActions;
boolean mHideProgress;
boolean mPromotePicture;
+ boolean mAllowActionIcons;
CharSequence title;
CharSequence text;
CharSequence headerTextSecondary;
@@ -11392,6 +11891,7 @@ public class Notification implements Parcelable
mHideActions = false;
mHideProgress = false;
mPromotePicture = false;
+ mAllowActionIcons = false;
title = null;
text = null;
summaryText = null;
@@ -11431,6 +11931,11 @@ public class Notification implements Parcelable
return this;
}
+ final StandardTemplateParams allowActionIcons(boolean allowActionIcons) {
+ this.mAllowActionIcons = allowActionIcons;
+ return this;
+ }
+
final StandardTemplateParams promotePicture(boolean promotePicture) {
this.mPromotePicture = promotePicture;
return this;
diff --git a/core/java/android/app/smartspace/SmartspaceTargetEvent.java b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
index 1e0653d67ace..920b9fe6a34f 100644
--- a/core/java/android/app/smartspace/SmartspaceTargetEvent.java
+++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
@@ -138,6 +138,15 @@ public final class SmartspaceTargetEvent implements Parcelable {
dest.writeInt(mEventType);
}
+ @Override
+ public String toString() {
+ return "SmartspaceTargetEvent{"
+ + "mSmartspaceTarget=" + mSmartspaceTarget
+ + ", mSmartspaceActionId='" + mSmartspaceActionId + '\''
+ + ", mEventType=" + mEventType
+ + '}';
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 2c1e951b6128..30ea5c476191 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -30,7 +30,7 @@ import java.util.Map;
interface IUsageStatsManager {
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
ParceledListSlice queryUsageStats(int bucketType, long beginTime, long endTime,
- String callingPackage);
+ String callingPackage, int userId);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
String callingPackage);
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index f74d16ee9238..31781ec79203 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -23,6 +23,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
import android.app.Activity;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
@@ -437,11 +438,12 @@ public final class UsageStatsManager {
* @see #INTERVAL_YEARLY
* @see #INTERVAL_BEST
*/
+ @UserHandleAware
public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
try {
@SuppressWarnings("unchecked")
ParceledListSlice<UsageStats> slice = mService.queryUsageStats(intervalType, beginTime,
- endTime, mContext.getOpPackageName());
+ endTime, mContext.getOpPackageName(), mContext.getUserId());
if (slice != null) {
return slice.getList();
}
diff --git a/core/java/android/hardware/face/FaceAuthenticationFrame.aidl b/core/java/android/hardware/face/FaceAuthenticationFrame.aidl
new file mode 100644
index 000000000000..4dc41f149328
--- /dev/null
+++ b/core/java/android/hardware/face/FaceAuthenticationFrame.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.hardware.face;
+
+/**
+ * @hide
+ */
+parcelable FaceAuthenticationFrame;
diff --git a/core/java/android/hardware/face/FaceDataFrame.java b/core/java/android/hardware/face/FaceDataFrame.java
index 3a0e09b70b50..092359c99173 100644
--- a/core/java/android/hardware/face/FaceDataFrame.java
+++ b/core/java/android/hardware/face/FaceDataFrame.java
@@ -63,6 +63,22 @@ public final class FaceDataFrame implements Parcelable {
}
/**
+ * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}.
+ *
+ * @param acquiredInfo An integer corresponding to a known acquired message.
+ * @param vendorCode An integer representing a custom vendor-specific message. Ignored unless
+ * {@code acquiredInfo} is {@code FACE_ACQUIRED_VENDOR}.
+ */
+ public FaceDataFrame(int acquiredInfo, int vendorCode) {
+ mAcquiredInfo = acquiredInfo;
+ mVendorCode = vendorCode;
+ mPan = 0f;
+ mTilt = 0f;
+ mDistance = 0f;
+ mIsCancellable = false;
+ }
+
+ /**
* @return An integer corresponding to a known acquired message.
*
* @see android.hardware.biometrics.BiometricFaceConstants
diff --git a/core/java/android/hardware/face/FaceEnrollFrame.aidl b/core/java/android/hardware/face/FaceEnrollFrame.aidl
new file mode 100644
index 000000000000..b8546812beeb
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollFrame.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.hardware.face;
+
+/**
+ * @hide
+ */
+parcelable FaceEnrollFrame;
diff --git a/core/java/android/hardware/face/FaceEnrollStage.java b/core/java/android/hardware/face/FaceEnrollStage.java
index 03dba551aae2..de717fbe46b4 100644
--- a/core/java/android/hardware/face/FaceEnrollStage.java
+++ b/core/java/android/hardware/face/FaceEnrollStage.java
@@ -28,6 +28,7 @@ import java.lang.annotation.RetentionPolicy;
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
+ FaceEnrollStage.UNKNOWN,
FaceEnrollStage.FIRST_FRAME_RECEIVED,
FaceEnrollStage.WAITING_FOR_CENTERING,
FaceEnrollStage.HOLD_STILL_IN_CENTER,
@@ -37,6 +38,11 @@ import java.lang.annotation.RetentionPolicy;
})
public @interface FaceEnrollStage {
/**
+ * The current enrollment stage is not known.
+ */
+ int UNKNOWN = -1;
+
+ /**
* Enrollment has just begun. No action is needed from the user yet.
*/
int FIRST_FRAME_RECEIVED = 0;
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 588bc01d7d42..f3da6a9d4a03 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -71,17 +71,19 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
private static final int MSG_FACE_DETECTED = 109;
private static final int MSG_CHALLENGE_INTERRUPTED = 110;
private static final int MSG_CHALLENGE_INTERRUPT_FINISHED = 111;
+ private static final int MSG_AUTHENTICATION_FRAME = 112;
+ private static final int MSG_ENROLLMENT_FRAME = 113;
private final IFaceService mService;
private final Context mContext;
private IBinder mToken = new Binder();
- private AuthenticationCallback mAuthenticationCallback;
- private FaceDetectionCallback mFaceDetectionCallback;
- private EnrollmentCallback mEnrollmentCallback;
- private RemovalCallback mRemovalCallback;
- private SetFeatureCallback mSetFeatureCallback;
- private GetFeatureCallback mGetFeatureCallback;
- private GenerateChallengeCallback mGenerateChallengeCallback;
+ @Nullable private AuthenticationCallback mAuthenticationCallback;
+ @Nullable private FaceDetectionCallback mFaceDetectionCallback;
+ @Nullable private EnrollmentCallback mEnrollmentCallback;
+ @Nullable private RemovalCallback mRemovalCallback;
+ @Nullable private SetFeatureCallback mSetFeatureCallback;
+ @Nullable private GetFeatureCallback mGetFeatureCallback;
+ @Nullable private GenerateChallengeCallback mGenerateChallengeCallback;
private CryptoObject mCryptoObject;
private Face mRemovalFace;
private Handler mHandler;
@@ -154,6 +156,16 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
public void onChallengeInterruptFinished(int sensorId) {
mHandler.obtainMessage(MSG_CHALLENGE_INTERRUPT_FINISHED, sensorId).sendToTarget();
}
+
+ @Override
+ public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_FRAME, frame).sendToTarget();
+ }
+
+ @Override
+ public void onEnrollmentFrame(FaceEnrollFrame frame) {
+ mHandler.obtainMessage(MSG_ENROLLMENT_FRAME, frame).sendToTarget();
+ }
};
/**
@@ -1248,6 +1260,12 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
case MSG_CHALLENGE_INTERRUPT_FINISHED:
sendChallengeInterruptFinished((int) msg.obj /* sensorId */);
break;
+ case MSG_AUTHENTICATION_FRAME:
+ sendAuthenticationFrame((FaceAuthenticationFrame) msg.obj /* frame */);
+ break;
+ case MSG_ENROLLMENT_FRAME:
+ sendEnrollmentFrame((FaceEnrollFrame) msg.obj /* frame */);
+ break;
default:
Slog.w(TAG, "Unknown message: " + msg.what);
}
@@ -1349,15 +1367,52 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
private void sendAcquiredResult(int acquireInfo, int vendorCode) {
if (mAuthenticationCallback != null) {
+ final FaceAuthenticationFrame frame = new FaceAuthenticationFrame(
+ new FaceDataFrame(acquireInfo, vendorCode));
+ sendAuthenticationFrame(frame);
+ } else if (mEnrollmentCallback != null) {
+ final FaceEnrollFrame frame = new FaceEnrollFrame(
+ null /* cell */,
+ FaceEnrollStage.UNKNOWN,
+ new FaceDataFrame(acquireInfo, vendorCode));
+ sendEnrollmentFrame(frame);
+ }
+ }
+
+ private void sendAuthenticationFrame(@Nullable FaceAuthenticationFrame frame) {
+ if (frame == null) {
+ Slog.w(TAG, "Received null authentication frame");
+ } else if (mAuthenticationCallback != null) {
+ // TODO(b/178414967): Send additional frame data to callback
+ final int acquireInfo = frame.getData().getAcquiredInfo();
+ final int vendorCode = frame.getData().getVendorCode();
+ final int helpCode = getHelpCode(acquireInfo, vendorCode);
+ final String helpMessage = getAcquiredString(mContext, acquireInfo, vendorCode);
mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
+
+ // Ensure that only non-null help messages are sent.
+ if (helpMessage != null) {
+ mAuthenticationCallback.onAuthenticationHelp(helpCode, helpMessage);
+ }
}
- final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
- final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR
- ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo;
- if (mEnrollmentCallback != null) {
- mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
- } else if (mAuthenticationCallback != null && msg != null) {
- mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
+ }
+
+ private void sendEnrollmentFrame(@Nullable FaceEnrollFrame frame) {
+ if (frame == null) {
+ Slog.w(TAG, "Received null enrollment frame");
+ } else if (mEnrollmentCallback != null) {
+ // TODO(b/178414967): Send additional frame data to callback
+ final int acquireInfo = frame.getData().getAcquiredInfo();
+ final int vendorCode = frame.getData().getVendorCode();
+ final int helpCode = getHelpCode(acquireInfo, vendorCode);
+ final String helpMessage = getAcquiredString(mContext, acquireInfo, vendorCode);
+ mEnrollmentCallback.onEnrollmentHelp(helpCode, helpMessage);
}
}
+
+ private static int getHelpCode(int acquireInfo, int vendorCode) {
+ return acquireInfo == FACE_ACQUIRED_VENDOR
+ ? vendorCode + FACE_ACQUIRED_VENDOR_BASE
+ : acquireInfo;
+ }
}
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index bd4d3a0b7017..2ef1430a2f99 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -16,6 +16,8 @@
package android.hardware.face;
import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceEnrollFrame;
/**
* Communication channel from the FaceService back to FaceAuthenticationManager.
@@ -34,4 +36,6 @@ oneway interface IFaceServiceReceiver {
void onChallengeGenerated(int sensorId, long challenge);
void onChallengeInterrupted(int sensorId);
void onChallengeInterruptFinished(int sensorId);
+ void onAuthenticationFrame(in FaceAuthenticationFrame frame);
+ void onEnrollmentFrame(in FaceEnrollFrame frame);
}
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index fa090f59a8b9..1a38338c26aa 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -28,8 +28,10 @@ import android.os.RemoteException;
import android.os.ServiceSpecificException;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
import java.io.IOException;
+import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -67,8 +69,7 @@ import java.util.concurrent.Executor;
public class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
- @VisibleForTesting
- public static final Map<
+ private static final Map<
VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
@@ -88,6 +89,18 @@ public class VcnManager {
mService = requireNonNull(service, "missing service");
}
+ /**
+ * Get all currently registered VcnUnderlyingNetworkPolicyListeners for testing purposes.
+ *
+ * @hide
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ public static Map<VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+ getAllPolicyListeners() {
+ return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS);
+ }
+
// TODO: Make setVcnConfig(), clearVcnConfig() Public API
/**
* Sets the VCN configuration for a given subscription group.
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index af8e8de85d9f..305815ff9e51 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -33,20 +33,33 @@ public final class BatteryUsageStats implements Parcelable {
private final int mDischargePercentage;
private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers;
private final ArrayList<SystemBatteryConsumer> mSystemBatteryConsumers;
+ private final ArrayList<UserBatteryConsumer> mUserBatteryConsumers;
private BatteryUsageStats(@NonNull Builder builder) {
mConsumedPower = builder.mConsumedPower;
mDischargePercentage = builder.mDischargePercentage;
+
int uidBatteryConsumerCount = builder.mUidBatteryConsumerBuilders.size();
mUidBatteryConsumers = new ArrayList<>(uidBatteryConsumerCount);
for (int i = 0; i < uidBatteryConsumerCount; i++) {
- mUidBatteryConsumers.add(builder.mUidBatteryConsumerBuilders.valueAt(i).build());
+ UidBatteryConsumer.Builder uidBatteryConsumerBuilder =
+ builder.mUidBatteryConsumerBuilders.valueAt(i);
+ if (!uidBatteryConsumerBuilder.isExcludedFromBatteryUsageStats()) {
+ mUidBatteryConsumers.add(uidBatteryConsumerBuilder.build());
+ }
}
+
int systemBatteryConsumerCount = builder.mSystemBatteryConsumerBuilders.size();
mSystemBatteryConsumers = new ArrayList<>(systemBatteryConsumerCount);
for (int i = 0; i < systemBatteryConsumerCount; i++) {
mSystemBatteryConsumers.add(builder.mSystemBatteryConsumerBuilders.valueAt(i).build());
}
+
+ int userBatteryConsumerCount = builder.mUserBatteryConsumerBuilders.size();
+ mUserBatteryConsumers = new ArrayList<>(userBatteryConsumerCount);
+ for (int i = 0; i < userBatteryConsumerCount; i++) {
+ mUserBatteryConsumers.add(builder.mUserBatteryConsumerBuilders.valueAt(i).build());
+ }
}
/**
@@ -75,6 +88,11 @@ public final class BatteryUsageStats implements Parcelable {
return mSystemBatteryConsumers;
}
+ @NonNull
+ public List<UserBatteryConsumer> getUserBatteryConsumers() {
+ return mUserBatteryConsumers;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -85,6 +103,8 @@ public final class BatteryUsageStats implements Parcelable {
source.readParcelableList(mUidBatteryConsumers, getClass().getClassLoader());
mSystemBatteryConsumers = new ArrayList<>();
source.readParcelableList(mSystemBatteryConsumers, getClass().getClassLoader());
+ mUserBatteryConsumers = new ArrayList<>();
+ source.readParcelableList(mUserBatteryConsumers, getClass().getClassLoader());
mConsumedPower = source.readDouble();
mDischargePercentage = source.readInt();
}
@@ -93,6 +113,7 @@ public final class BatteryUsageStats implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelableList(mUidBatteryConsumers, flags);
dest.writeParcelableList(mSystemBatteryConsumers, flags);
+ dest.writeParcelableList(mUserBatteryConsumers, flags);
dest.writeDouble(mConsumedPower);
dest.writeInt(mDischargePercentage);
}
@@ -120,6 +141,8 @@ public final class BatteryUsageStats implements Parcelable {
new SparseArray<>();
private final SparseArray<SystemBatteryConsumer.Builder> mSystemBatteryConsumerBuilders =
new SparseArray<>();
+ private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders =
+ new SparseArray<>();
public Builder(int customPowerComponentCount, int customTimeComponentCount) {
mCustomPowerComponentCount = customPowerComponentCount;
@@ -137,7 +160,6 @@ public final class BatteryUsageStats implements Parcelable {
/**
* Sets the battery discharge amount since BatteryStats reset as percentage of the full
* charge.
- *
*/
@SuppressLint("PercentageInt") // See b/174188159
@NonNull
@@ -173,8 +195,8 @@ public final class BatteryUsageStats implements Parcelable {
}
/**
- * Creates or returns a exiting UidBatteryConsumer, which represents battery attribution
- * data for an individual UID.
+ * Creates or returns a exiting SystemBatteryConsumer, which represents battery attribution
+ * data for a specific drain type.
*/
@NonNull
public SystemBatteryConsumer.Builder getOrCreateSystemBatteryConsumerBuilder(
@@ -188,6 +210,21 @@ public final class BatteryUsageStats implements Parcelable {
return builder;
}
+ /**
+ * Creates or returns a exiting UserBatteryConsumer, which represents battery attribution
+ * data for an individual {@link UserHandle}.
+ */
+ @NonNull
+ public UserBatteryConsumer.Builder getOrCreateUserBatteryConsumerBuilder(int userId) {
+ UserBatteryConsumer.Builder builder = mUserBatteryConsumerBuilders.get(userId);
+ if (builder == null) {
+ builder = new UserBatteryConsumer.Builder(mCustomPowerComponentCount,
+ mCustomTimeComponentCount, userId);
+ mUserBatteryConsumerBuilders.put(userId, builder);
+ }
+ return builder;
+ }
+
@NonNull
public SparseArray<UidBatteryConsumer.Builder> getUidBatteryConsumerBuilders() {
return mUidBatteryConsumerBuilders;
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 48e7389e5ff2..5b5fe1d15a82 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -18,6 +18,7 @@ package android.os;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.util.IntArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -53,9 +54,13 @@ public final class BatteryUsageStatsQuery implements Parcelable {
public static final int FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL = 1;
private final int mFlags;
+ @NonNull
+ private final int[] mUserIds;
private BatteryUsageStatsQuery(@NonNull Builder builder) {
mFlags = builder.mFlags;
+ mUserIds = builder.mUserIds != null ? builder.mUserIds.toArray()
+ : new int[]{UserHandle.USER_ALL};
}
@BatteryUsageStatsFlags
@@ -63,13 +68,28 @@ public final class BatteryUsageStatsQuery implements Parcelable {
return mFlags;
}
+ /**
+ * Returns an array of users for which the attribution is requested. It may
+ * contain {@link UserHandle#USER_ALL} to indicate that the attribution
+ * should be performed for all users. Battery consumed by users <b>not</b> included
+ * in this array will be returned in the aggregated form as {@link UserBatteryConsumer}'s.
+ */
+ @NonNull
+ public int[] getUserIds() {
+ return mUserIds;
+ }
+
private BatteryUsageStatsQuery(Parcel in) {
mFlags = in.readInt();
+ mUserIds = new int[in.readInt()];
+ in.readIntArray(mUserIds);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mFlags);
+ dest.writeInt(mUserIds.length);
+ dest.writeIntArray(mUserIds);
}
@Override
@@ -96,6 +116,7 @@ public final class BatteryUsageStatsQuery implements Parcelable {
*/
public static final class Builder {
private int mFlags;
+ private IntArray mUserIds;
/**
* Builds a read-only BatteryUsageStatsQuery object.
@@ -105,6 +126,18 @@ public final class BatteryUsageStatsQuery implements Parcelable {
}
/**
+ * Add a user whose battery stats should be included in the battery usage stats.
+ * {@link UserHandle#USER_ALL} will be used by default if no users are added explicitly.
+ */
+ public Builder addUser(@NonNull UserHandle userHandle) {
+ if (mUserIds == null) {
+ mUserIds = new IntArray(1);
+ }
+ mUserIds.add(userHandle.getIdentifier());
+ return this;
+ }
+
+ /**
* Sets flags to modify the behavior of {@link BatteryStatsManager#getBatteryUsageStats}.
*/
public Builder setFlags(@BatteryUsageStatsFlags int flags) {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 9584bc74d6f2..6a76da2cc13d 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2557,6 +2557,14 @@ public final class Debug
public static native long getZramFreeKb();
/**
+ * Return total memory size in kilobytes for exported DMA-BUFs or -1 if
+ * the DMA-BUF sysfs stats at /sys/kernel/dmabuf/buffers could not be read.
+ *
+ * @hide
+ */
+ public static native long getDmabufTotalExportedKb();
+
+ /**
* Return memory size in kilobytes allocated for ION heaps or -1 if
* /sys/kernel/ion/total_heaps_kb could not be read.
*
@@ -2565,6 +2573,14 @@ public final class Debug
public static native long getIonHeapsSizeKb();
/**
+ * Return memory size in kilobytes allocated for DMA-BUF heap pools or -1 if
+ * /sys/kernel/dma_heap/total_pools_kb could not be read.
+ *
+ * @hide
+ */
+ public static native long getDmabufHeapPoolsSizeKb();
+
+ /**
* Return memory size in kilobytes allocated for ION pools or -1 if
* /sys/kernel/ion/total_pools_kb could not be read.
*
@@ -2573,13 +2589,13 @@ public final class Debug
public static native long getIonPoolsSizeKb();
/**
- * Return ION memory mapped by processes in kB.
+ * Return DMA-BUF memory mapped by processes in kB.
* Notes:
* * Warning: Might impact performance as it reads /proc/<pid>/maps files for each process.
*
* @hide
*/
- public static native long getIonMappedSizeKb();
+ public static native long getDmabufMappedSizeKb();
/**
* Return memory size in kilobytes used by GPU.
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 39e3e146f45b..8068c872c4bb 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -1442,7 +1442,7 @@ public class Process {
*
* @hide
*/
- public static boolean hasFileLocks(int pid) throws IOException {
+ public static boolean hasFileLocks(int pid) throws Exception {
BufferedReader br = null;
try {
@@ -1454,8 +1454,13 @@ public class Process {
for (int i = 0; i < 5 && st.hasMoreTokens(); i++) {
String str = st.nextToken();
- if (i == 4 && Integer.parseInt(str) == pid) {
- return true;
+ try {
+ if (i == 4 && Integer.parseInt(str) == pid) {
+ return true;
+ }
+ } catch (NumberFormatException nfe) {
+ throw new Exception("Exception parsing /proc/locks at \" "
+ + line + " \", token #" + i);
}
}
}
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 3ffaa9e4b1f0..d1ca6a560c3f 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -96,6 +96,7 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
private final int mUid;
private String mPackageWithHighestDrain;
private boolean mSystemComponent;
+ private boolean mExcludeFromBatteryUsageStats;
public Builder(int customPowerComponentCount, int customTimeComponentCount,
BatteryStats.Uid batteryStatsUid) {
@@ -113,14 +114,6 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
}
/**
- * Creates a read-only object out of the Builder values.
- */
- @NonNull
- public UidBatteryConsumer build() {
- return new UidBatteryConsumer(this);
- }
-
- /**
* Sets the name of the package owned by this UID that consumed the highest amount
* of power since BatteryStats reset.
*/
@@ -131,12 +124,27 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
}
/**
- * Marks the UidBatteryConsumer as part of the system. For example,
- * the UidBatteryConsumer with the UID {@link Process#BLUETOOTH_UID} is considered
- * as a system component.
+ * Marks the UidBatteryConsumer for exclusion from the result set.
*/
- public void setSystemComponent(boolean systemComponent) {
- mSystemComponent = systemComponent;
+ public Builder excludeFromBatteryUsageStats() {
+ mExcludeFromBatteryUsageStats = true;
+ return this;
+ }
+
+ /**
+ * Returns true if this UidBatteryConsumer must be excluded from the
+ * BatteryUsageStats.
+ */
+ public boolean isExcludedFromBatteryUsageStats() {
+ return mExcludeFromBatteryUsageStats;
+ }
+
+ /**
+ * Creates a read-only object out of the Builder values.
+ */
+ @NonNull
+ public UidBatteryConsumer build() {
+ return new UidBatteryConsumer(this);
}
}
}
diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java
new file mode 100644
index 000000000000..94e567f368b0
--- /dev/null
+++ b/core/java/android/os/UserBatteryConsumer.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains power consumption data attributed to a {@link UserHandle}.
+ *
+ * {@hide}
+ */
+public class UserBatteryConsumer extends BatteryConsumer implements Parcelable {
+ private final int mUserId;
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ private UserBatteryConsumer(@NonNull UserBatteryConsumer.Builder builder) {
+ super(builder.mPowerComponentsBuilder.build());
+ mUserId = builder.mUserId;
+ }
+
+ private UserBatteryConsumer(Parcel in) {
+ super(new PowerComponents(in));
+ mUserId = in.readInt();
+ }
+
+ /**
+ * Writes the contents into a Parcel.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mUserId);
+ }
+
+ public static final Creator<UserBatteryConsumer> CREATOR =
+ new Creator<UserBatteryConsumer>() {
+ @Override
+ public UserBatteryConsumer createFromParcel(Parcel in) {
+ return new UserBatteryConsumer(in);
+ }
+
+ @Override
+ public UserBatteryConsumer[] newArray(int size) {
+ return new UserBatteryConsumer[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Builder for UserBatteryConsumer.
+ */
+ public static final class Builder extends BaseBuilder<Builder> {
+ private final int mUserId;
+ private List<UidBatteryConsumer.Builder> mUidBatteryConsumers;
+
+ Builder(int customPowerComponentCount, int customTimeComponentCount, int userId) {
+ super(customPowerComponentCount, customTimeComponentCount);
+ mUserId = userId;
+ }
+
+ /**
+ * Add a UidBatteryConsumer to this UserBatteryConsumer.
+ * <p>
+ * Calculated power and duration components of the added UID battery consumers
+ * are aggregated at the time the UserBatteryConsumer is built by the {@link #build()}
+ * method.
+ * </p>
+ */
+ public void addUidBatteryConsumer(UidBatteryConsumer.Builder uidBatteryConsumerBuilder) {
+ if (mUidBatteryConsumers == null) {
+ mUidBatteryConsumers = new ArrayList<>();
+ }
+ mUidBatteryConsumers.add(uidBatteryConsumerBuilder);
+ }
+
+ /**
+ * Creates a read-only object out of the Builder values.
+ */
+ @NonNull
+ public UserBatteryConsumer build() {
+ if (mUidBatteryConsumers != null) {
+ for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) {
+ UidBatteryConsumer.Builder uidBatteryConsumer = mUidBatteryConsumers.get(i);
+ mPowerComponentsBuilder.addPowerAndDuration(
+ uidBatteryConsumer.mPowerComponentsBuilder);
+ }
+ }
+ return new UserBatteryConsumer(this);
+ }
+ }
+}
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index faa90d93719f..86b20c424869 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -350,6 +350,9 @@ public class FontsContract {
return cachedTypeface;
}
+ Log.w(TAG, "Platform version of downloadable fonts is deprecated. Please use"
+ + " androidx version instead.");
+
synchronized (sLock) {
// It is possible that Font is loaded during the thread sleep time
// re-check the cache to avoid re-loading the font
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 990b7bdfa987..dad932c2ee19 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -123,14 +123,6 @@ interface IWindowSession {
boolean outOfMemory(IWindow window);
/**
- * Give the window manager a hint of the part of the window that is
- * completely transparent, allowing it to work with the surface flinger
- * to optimize compositing of this part of the window.
- */
- @UnsupportedAppUsage
- oneway void setTransparentRegion(IWindow window, in Region region);
-
- /**
* Tell the window manager about the content and visible insets of the
* given window, which can be used to adjust the <var>outContentInsets</var>
* and <var>outVisibleInsets</var> values returned by
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e1ccc51c71e1..1273b491d0e1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -740,6 +740,7 @@ import java.util.function.Predicate;
* @attr ref android.R.styleable#View_alpha
* @attr ref android.R.styleable#View_background
* @attr ref android.R.styleable#View_clickable
+ * @attr ref android.R.styleable#View_clipToOutline
* @attr ref android.R.styleable#View_contentDescription
* @attr ref android.R.styleable#View_drawingCacheQuality
* @attr ref android.R.styleable#View_duplicateParentState
@@ -5968,6 +5969,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
case R.styleable.View_scrollCaptureHint:
setScrollCaptureHint((a.getInt(attr, SCROLL_CAPTURE_HINT_AUTO)));
break;
+ case R.styleable.View_clipToOutline:
+ setClipToOutline(a.getBoolean(attr, false));
+ break;
}
}
@@ -17921,6 +17925,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @see #setOutlineProvider(ViewOutlineProvider)
* @see #getClipToOutline()
+ *
+ * @attr ref android.R.styleable#View_clipToOutline
*/
@RemotableViewMethod
public void setClipToOutline(boolean clipToOutline) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index acc77b541293..4716141ee8d3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3064,11 +3064,14 @@ public final class ViewRootImpl implements ViewParent,
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
- // reconfigure window manager
- try {
- mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
- } catch (RemoteException e) {
- }
+ // TODO: Ideally we would do this in prepareSurfaces,
+ // but prepareSurfaces is currently working under
+ // the assumption that we paused the render thread
+ // via the WM relayout code path. We probably eventually
+ // want to synchronize transparent region hint changes
+ // with draws.
+ mTransaction.setTransparentRegionHint(getSurfaceControl(),
+ mTransparentRegion).apply();
}
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 3aedda1a6bd3..39d3c01dd409 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -314,10 +314,6 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
- public void setTransparentRegion(android.view.IWindow window, android.graphics.Region region) {
- }
-
- @Override
public void setInsets(android.view.IWindow window, int touchableInsets,
android.graphics.Rect contentInsets, android.graphics.Rect visibleInsets,
android.graphics.Region touchableRegion) {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 794181e388cf..decbf8c0c59e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -200,6 +200,34 @@ public final class AutofillManager {
"android.view.autofill.extra.AUTHENTICATION_RESULT";
/**
+ * Intent extra: The optional boolean extra field provided by the
+ * {@link android.service.autofill.AutofillService} accompanying the {@link
+ * android.service.autofill.Dataset} result of an authentication operation.
+ *
+ * <p> Before {@link android.os.Build.VERSION_CODES#R}, if the authentication result is a
+ * {@link android.service.autofill.Dataset}, it'll be used to autofill the fields, and also
+ * replace the existing dataset in the cached {@link android.service.autofill.FillResponse}.
+ * That means if the user clears the field values, the autofill suggestion will show up again
+ * with the new authenticated Dataset.
+ *
+ * <p> In {@link android.os.Build.VERSION_CODES#R}, we added an exception to this behavior
+ * that if the Dataset being authenticated is a pinned dataset (see
+ * {@link android.service.autofill.InlinePresentation#isPinned()}), the old Dataset will not be
+ * replaced.
+ *
+ * <p> In {@link android.os.Build.VERSION_CODES#S}, we added this boolean extra field to
+ * allow the {@link android.service.autofill.AutofillService} to explicitly specify whether
+ * the returned authenticated Dataset is ephemeral. An ephemeral Dataset will be used to
+ * autofill once and then thrown away. Therefore, when the boolean extra is set to true, the
+ * returned Dataset will not replace the old dataset from the existing
+ * {@link android.service.autofill.FillResponse}. When it's set to false, it will. When it's not
+ * set, the old dataset will be replaced, unless it is a pinned inline suggestion, which is
+ * consistent with the behavior in {@link android.os.Build.VERSION_CODES#R}.
+ */
+ public static final String EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET =
+ "android.view.autofill.extra.AUTHENTICATION_RESULT_EPHEMERAL_DATASET";
+
+ /**
* Intent extra: The optional extras provided by the
* {@link android.service.autofill.AutofillService}.
*
@@ -1755,6 +1783,11 @@ public final class AutofillManager {
if (newClientState != null) {
responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
}
+ if (data.getExtras().containsKey(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
+ responseData.putBoolean(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET,
+ data.getBooleanExtra(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET,
+ false));
+ }
try {
mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
mContext.getUserId());
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index dfef7ca825a1..6cb4b81827b9 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -47,6 +47,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
+import android.graphics.Outline;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -79,6 +80,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewManager;
+import android.view.ViewOutlineProvider;
import android.view.ViewParent;
import android.view.ViewStub;
import android.widget.AdapterView.OnItemClickListener;
@@ -194,6 +196,7 @@ public class RemoteViews implements Parcelable, Filter {
private static final int COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG = 25;
private static final int SET_COMPOUND_BUTTON_CHECKED_TAG = 26;
private static final int SET_RADIO_GROUP_CHECKED = 27;
+ private static final int SET_VIEW_OUTLINE_RADIUS_TAG = 28;
/** @hide **/
@IntDef(prefix = "MARGIN_", value = {
@@ -2642,6 +2645,88 @@ public class RemoteViews implements Parcelable, Filter {
}
}
+ private static class SetViewOutlinePreferredRadiusAction extends Action {
+
+ private final boolean mIsDimen;
+ private final int mValue;
+
+ SetViewOutlinePreferredRadiusAction(@IdRes int viewId, @DimenRes int dimenResId) {
+ this.viewId = viewId;
+ this.mIsDimen = true;
+ this.mValue = dimenResId;
+ }
+
+ SetViewOutlinePreferredRadiusAction(
+ @IdRes int viewId, float radius, @ComplexDimensionUnit int units) {
+ this.viewId = viewId;
+ this.mIsDimen = false;
+ this.mValue = TypedValue.createComplexDimension(radius, units);
+
+ }
+
+ SetViewOutlinePreferredRadiusAction(Parcel in) {
+ viewId = in.readInt();
+ mIsDimen = in.readBoolean();
+ mValue = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(viewId);
+ dest.writeBoolean(mIsDimen);
+ dest.writeInt(mValue);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+ throws ActionException {
+ final View target = root.findViewById(viewId);
+ if (target == null) return;
+
+ float radius;
+ if (mIsDimen) {
+ radius = mValue == 0 ? 0 : target.getResources().getDimension(mValue);
+ } else {
+ radius = TypedValue.complexToDimensionPixelSize(mValue,
+ target.getResources().getDisplayMetrics());
+ }
+ target.setOutlineProvider(new RemoteViewOutlineProvider(radius));
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_VIEW_OUTLINE_RADIUS_TAG;
+ }
+ }
+
+ /**
+ * OutlineProvider for a view with a radius set by
+ * {@link #setViewOutlinePreferredRadius(int, float, int)}.
+ */
+ public static final class RemoteViewOutlineProvider extends ViewOutlineProvider {
+
+ private final float mRadius;
+
+ public RemoteViewOutlineProvider(float radius) {
+ mRadius = radius;
+ }
+
+ /** Returns the corner radius used when providing the view outline. */
+ public float getRadius() {
+ return mRadius;
+ }
+
+ @Override
+ public void getOutline(@NonNull View view, @NonNull Outline outline) {
+ outline.setRoundRect(
+ 0 /*left*/,
+ 0 /* top */,
+ view.getWidth() /* right */,
+ view.getHeight() /* bottom */,
+ mRadius);
+ }
+ }
+
/**
* Create a new RemoteViews object that will display the views contained
* in the specified layout file.
@@ -2860,6 +2945,8 @@ public class RemoteViews implements Parcelable, Filter {
return new SetCompoundButtonCheckedAction(parcel);
case SET_RADIO_GROUP_CHECKED:
return new SetRadioGroupCheckedAction(parcel);
+ case SET_VIEW_OUTLINE_RADIUS_TAG:
+ return new SetViewOutlinePreferredRadiusAction(parcel);
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -3595,6 +3682,28 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Sets an OutlineProvider on the view whose corner radius is a dimension calculated using
+ * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}. This outline may change shape
+ * during system transitions.
+ *
+ * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0.
+ * Setting margins in pixels will behave poorly when the RemoteViews object is used on a
+ * display with a different density.
+ */
+ public void setViewOutlinePreferredRadius(
+ @IdRes int viewId, float radius, @ComplexDimensionUnit int units) {
+ addAction(new SetViewOutlinePreferredRadiusAction(viewId, radius, units));
+ }
+
+ /**
+ * Sets an OutlineProvider on the view whose corner radius is a dimension resource with
+ * {@code resId}. This outline may change shape during system transitions.
+ */
+ public void setViewOutlinePreferredRadiusDimen(@IdRes int viewId, @DimenRes int resId) {
+ addAction(new SetViewOutlinePreferredRadiusAction(viewId, resId));
+ }
+
+ /**
* Call a method taking one boolean on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view on which to call the method.
diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
index 8fe17fb1565c..16b6f3a155f8 100644
--- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
+++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
@@ -43,8 +43,7 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator {
*/
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final long durationMs = calculateDuration(batteryStats, rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
final double powerMah = mPowerEstimator.calculatePower(durationMs);
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index e76e34f840ba..094724c00508 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -24,7 +24,6 @@ import android.os.BatteryUsageStatsQuery;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.UserManager;
import android.util.SparseArray;
import java.util.ArrayList;
@@ -89,26 +88,27 @@ public class BatteryUsageStatsProvider {
final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(mContext,
false /* collectBatteryBroadcast */);
batteryStatsHelper.create((Bundle) null);
- final UserManager userManager = mContext.getSystemService(UserManager.class);
- final List<UserHandle> asUsers = userManager.getUserProfiles();
- final int n = asUsers.size();
- SparseArray<UserHandle> users = new SparseArray<>(n);
- for (int i = 0; i < n; ++i) {
- UserHandle userHandle = asUsers.get(i);
- users.put(userHandle.getIdentifier(), userHandle);
+ final List<UserHandle> users = new ArrayList<>();
+ for (int i = 0; i < queries.size(); i++) {
+ BatteryUsageStatsQuery query = queries.get(i);
+ for (int userId : query.getUserIds()) {
+ UserHandle userHandle = UserHandle.of(userId);
+ if (!users.contains(userHandle)) {
+ users.add(userHandle);
+ }
+ }
}
-
batteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, users);
ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size());
for (int i = 0; i < queries.size(); i++) {
- results.add(getBatteryUsageStats(queries.get(i), batteryStatsHelper, users));
+ results.add(getBatteryUsageStats(queries.get(i), batteryStatsHelper));
}
return results;
}
private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
- BatteryStatsHelper batteryStatsHelper, SparseArray<UserHandle> users) {
+ BatteryStatsHelper batteryStatsHelper) {
// TODO(b/174186358): read extra power component number from configuration
final int customPowerComponentCount = 0;
final int customTimeComponentCount = 0;
@@ -128,8 +128,8 @@ public class BatteryUsageStatsProvider {
final List<PowerCalculator> powerCalculators = getPowerCalculators();
for (PowerCalculator powerCalculator : powerCalculators) {
- powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs, query,
- users);
+ powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs,
+ query);
}
return batteryUsageStatsBuilder.build();
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index 4c3b950ff715..7d42de4486a4 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -50,8 +50,7 @@ public class BluetoothPowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
if (!mHasBluetoothPowerController || !batteryStats.hasBluetoothActivityReporting()) {
return;
}
@@ -68,7 +67,7 @@ public class BluetoothPowerCalculator extends PowerCalculator {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
calculateApp(app, total);
if (app.getUid() == Process.BLUETOOTH_UID) {
- app.setSystemComponent(true);
+ app.excludeFromBatteryUsageStats();
systemBatteryConsumerBuilder.addUidBatteryConsumer(app);
}
}
diff --git a/core/java/com/android/internal/os/GnssPowerCalculator.java b/core/java/com/android/internal/os/GnssPowerCalculator.java
index 9ea934af642a..df25cdaee17f 100644
--- a/core/java/com/android/internal/os/GnssPowerCalculator.java
+++ b/core/java/com/android/internal/os/GnssPowerCalculator.java
@@ -45,8 +45,7 @@ public class GnssPowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final double averageGnssPowerMa = getAverageGnssPower(batteryStats, rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
diff --git a/core/java/com/android/internal/os/IdlePowerCalculator.java b/core/java/com/android/internal/os/IdlePowerCalculator.java
index dcc8a15b2f50..4a4991bd3966 100644
--- a/core/java/com/android/internal/os/IdlePowerCalculator.java
+++ b/core/java/com/android/internal/os/IdlePowerCalculator.java
@@ -49,8 +49,7 @@ public class IdlePowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
calculatePowerAndDuration(batteryStats, rawRealtimeUs, rawUptimeUs,
BatteryStats.STATS_SINCE_CHARGED);
if (mPowerMah != 0) {
diff --git a/core/java/com/android/internal/os/MemoryPowerCalculator.java b/core/java/com/android/internal/os/MemoryPowerCalculator.java
index df4605838b28..21dcce9fb64f 100644
--- a/core/java/com/android/internal/os/MemoryPowerCalculator.java
+++ b/core/java/com/android/internal/os/MemoryPowerCalculator.java
@@ -26,8 +26,7 @@ public class MemoryPowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final long durationMs = calculateDuration(batteryStats, rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
final double powerMah = calculatePower(batteryStats, rawRealtimeUs,
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index e3bd64d77e9b..22001d4ffbb7 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -85,8 +85,7 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
PowerAndDuration total = new PowerAndDuration();
diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java
index 6ab8c90d21d9..362ca0761b2c 100644
--- a/core/java/com/android/internal/os/PhonePowerCalculator.java
+++ b/core/java/com/android/internal/os/PhonePowerCalculator.java
@@ -39,8 +39,7 @@ public class PhonePowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final long phoneOnTimeMs = batteryStats.getPhoneOnTime(rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED) / 1000;
final double phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs);
diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java
index 05fcc705e734..13c59dcdbc4e 100644
--- a/core/java/com/android/internal/os/PowerCalculator.java
+++ b/core/java/com/android/internal/os/PowerCalculator.java
@@ -67,17 +67,10 @@ public abstract class PowerCalculator {
* @param batteryStats The recorded battery stats.
* @param rawRealtimeUs The raw system realtime in microseconds.
* @param rawUptimeUs The raw system uptime in microseconds.
- * @param statsType The type of stats. As of {@link android.os.Build.VERSION_CODES#Q}, this
- * can only be {@link BatteryStats#STATS_SINCE_CHARGED}, since
- * {@link BatteryStats#STATS_CURRENT} and
- * {@link BatteryStats#STATS_SINCE_UNPLUGGED} are deprecated.
- * @param asUsers An array of users for which the attribution is requested. It may
- * contain {@link UserHandle#USER_ALL} to indicate that the attribution
- * should be performed for all users.
+ * @param query The query parameters for the calculator.
*/
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index c86c795c965a..bb1222eac11c 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -51,8 +51,7 @@ public class ScreenPowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final long durationMs = computeDuration(batteryStats, rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
final double powerMah = computePower(batteryStats, rawRealtimeUs,
diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
index 55fc1bb607a9..955f6afe579c 100644
--- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -51,10 +51,9 @@ public class SystemServicePowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
calculateSystemServicePower(batteryStats);
- super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query, asUsers);
+ super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query);
}
@Override
diff --git a/core/java/com/android/internal/os/UserPowerCalculator.java b/core/java/com/android/internal/os/UserPowerCalculator.java
index 53f85154330c..8e802869e2fc 100644
--- a/core/java/com/android/internal/os/UserPowerCalculator.java
+++ b/core/java/com/android/internal/os/UserPowerCalculator.java
@@ -17,10 +17,15 @@
package com.android.internal.os;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
import android.os.Process;
+import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.util.SparseArray;
+import com.android.internal.util.ArrayUtils;
+
import java.util.List;
/**
@@ -29,6 +34,33 @@ import java.util.List;
public class UserPowerCalculator extends PowerCalculator {
@Override
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ final int[] userIds = query.getUserIds();
+ if (ArrayUtils.contains(userIds, UserHandle.USER_ALL)) {
+ return;
+ }
+
+ SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+ builder.getUidBatteryConsumerBuilders();
+
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ UidBatteryConsumer.Builder uidBuilder = uidBatteryConsumerBuilders.valueAt(i);
+ final int uid = uidBuilder.getUid();
+ if (UserHandle.getAppId(uid) < Process.FIRST_APPLICATION_UID) {
+ continue;
+ }
+
+ final int userId = UserHandle.getUserId(uid);
+ if (!ArrayUtils.contains(userIds, userId)) {
+ uidBuilder.excludeFromBatteryUsageStats();
+ builder.getOrCreateUserBatteryConsumerBuilder(userId)
+ .addUidBatteryConsumer(uidBuilder);
+ }
+ }
+ }
+
+ @Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java
new file mode 100644
index 000000000000..6cc5a4aacda5
--- /dev/null
+++ b/core/java/com/android/internal/widget/CallLayout.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
+import android.app.Notification;
+import android.app.Person;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
+import android.widget.FrameLayout;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+/**
+ * A custom-built layout for the Notification.CallStyle.
+ */
+@RemoteViews.RemoteView
+public class CallLayout extends FrameLayout {
+ private final PeopleHelper mPeopleHelper = new PeopleHelper();
+
+ private int mLayoutColor;
+ private Icon mLargeIcon;
+ private Person mUser;
+
+ private CachingIconView mConversationIconView;
+ private CachingIconView mIcon;
+ private CachingIconView mConversationIconBadgeBg;
+ private TextView mConversationText;
+
+ public CallLayout(@NonNull Context context) {
+ super(context);
+ }
+
+ public CallLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CallLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public CallLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mPeopleHelper.init(getContext());
+ mConversationText = findViewById(R.id.conversation_text);
+ mConversationIconView = findViewById(R.id.conversation_icon);
+ mIcon = findViewById(R.id.icon);
+ mConversationIconBadgeBg = findViewById(R.id.conversation_icon_badge_bg);
+
+ // When the small icon is gone, hide the rest of the badge
+ mIcon.setOnForceHiddenChangedListener((forceHidden) -> {
+ mPeopleHelper.animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
+ });
+ }
+
+ private void updateCallLayout() {
+ CharSequence callerName = "";
+ String symbol = "";
+ Icon icon = null;
+ if (mUser != null) {
+ icon = mUser.getIcon();
+ callerName = mUser.getName();
+ symbol = mPeopleHelper.findNamePrefix(callerName, "");
+ }
+ if (icon == null) {
+ icon = mLargeIcon;
+ }
+ if (icon == null) {
+ icon = mPeopleHelper.createAvatarSymbol(callerName, symbol, mLayoutColor);
+ }
+ // TODO(b/179178086): crop/clip the icon to a circle?
+ mConversationIconView.setImageIcon(icon);
+ mConversationText.setText(callerName);
+ }
+
+ @RemotableViewMethod
+ public void setLayoutColor(int color) {
+ mLayoutColor = color;
+ }
+
+ /**
+ * @param color the color of the notification background
+ */
+ @RemotableViewMethod
+ public void setNotificationBackgroundColor(int color) {
+ mConversationIconBadgeBg.setImageTintList(ColorStateList.valueOf(color));
+ }
+
+ @RemotableViewMethod
+ public void setLargeIcon(Icon largeIcon) {
+ mLargeIcon = largeIcon;
+ }
+
+ /**
+ * Set the notification extras so that this layout has access
+ */
+ @RemotableViewMethod
+ public void setData(Bundle extras) {
+ setUser(extras.getParcelable(Notification.EXTRA_CALL_PERSON));
+ updateCallLayout();
+ }
+
+ private void setUser(Person user) {
+ mUser = user;
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 40e671ffd27c..1b1e0bfb3a58 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -18,8 +18,6 @@ package com.android.internal.widget;
import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_EXTERNAL;
import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_INLINE;
-import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_IN;
-import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_OUT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -34,10 +32,7 @@ import android.app.Person;
import android.app.RemoteInputHistoryItem;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
@@ -68,14 +63,12 @@ import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.graphics.ColorUtils;
-import com.android.internal.util.ContrastColorUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.function.Consumer;
-import java.util.regex.Pattern;
/**
* A custom-built layout for the Notification.MessagingStyle allows dynamic addition and removal
@@ -85,16 +78,6 @@ import java.util.regex.Pattern;
public class ConversationLayout extends FrameLayout
implements ImageMessageConsumer, IMessagingLayout {
- private static final float COLOR_SHIFT_AMOUNT = 60;
- /**
- * Pattern for filter some ignorable characters.
- * p{Z} for any kind of whitespace or invisible separator.
- * p{C} for any kind of punctuation character.
- */
- private static final Pattern IGNORABLE_CHAR_PATTERN
- = Pattern.compile("[\\p{C}\\p{Z}]");
- private static final Pattern SPECIAL_CHAR_PATTERN
- = Pattern.compile ("[!@#$%&*()_+=|<>?{}\\[\\]~-]");
private static final Consumer<MessagingMessage> REMOVE_MESSAGE
= MessagingMessage::removeMessage;
public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
@@ -106,6 +89,7 @@ public class ConversationLayout extends FrameLayout
public static final int IMPORTANCE_ANIM_GROW_DURATION = 250;
public static final int IMPORTANCE_ANIM_SHRINK_DURATION = 200;
public static final int IMPORTANCE_ANIM_SHRINK_DELAY = 25;
+ private final PeopleHelper mPeopleHelper = new PeopleHelper();
private List<MessagingMessage> mMessages = new ArrayList<>();
private List<MessagingMessage> mHistoricMessages = new ArrayList<>();
private MessagingLinearLayout mMessagingLinearLayout;
@@ -114,9 +98,6 @@ public class ConversationLayout extends FrameLayout
private int mLayoutColor;
private int mSenderTextColor;
private int mMessageTextColor;
- private int mAvatarSize;
- private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private Paint mTextPaint = new Paint();
private Icon mAvatarReplacement;
private boolean mIsOneToOne;
private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
@@ -196,6 +177,7 @@ public class ConversationLayout extends FrameLayout
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mPeopleHelper.init(getContext());
mMessagingLinearLayout = findViewById(R.id.notification_messaging);
mActions = findViewById(R.id.actions);
mImageMessageContainer = findViewById(R.id.conversation_image_message_container);
@@ -205,9 +187,6 @@ public class ConversationLayout extends FrameLayout
int size = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels);
mMessagingClipRect = new Rect(0, 0, size, size);
setMessagingClippingDisabled(false);
- mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size);
- mTextPaint.setTextAlign(Paint.Align.CENTER);
- mTextPaint.setAntiAlias(true);
mConversationIconView = findViewById(R.id.conversation_icon);
mConversationIconContainer = findViewById(R.id.conversation_icon_container);
mIcon = findViewById(R.id.icon);
@@ -250,15 +229,15 @@ public class ConversationLayout extends FrameLayout
});
// When the small icon is gone, hide the rest of the badge
mIcon.setOnForceHiddenChangedListener((forceHidden) -> {
- animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
- animateViewForceHidden(mImportanceRingView, forceHidden);
+ mPeopleHelper.animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
+ mPeopleHelper.animateViewForceHidden(mImportanceRingView, forceHidden);
});
// When the conversation icon is gone, hide the whole badge
mConversationIconView.setOnForceHiddenChangedListener((forceHidden) -> {
- animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
- animateViewForceHidden(mImportanceRingView, forceHidden);
- animateViewForceHidden(mIcon, forceHidden);
+ mPeopleHelper.animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
+ mPeopleHelper.animateViewForceHidden(mImportanceRingView, forceHidden);
+ mPeopleHelper.animateViewForceHidden(mIcon, forceHidden);
});
mConversationText = findViewById(R.id.conversation_text);
mExpandButtonContainer = findViewById(R.id.expand_button_container);
@@ -317,28 +296,6 @@ public class ConversationLayout extends FrameLayout
R.dimen.notification_header_separating_margin);
}
- private void animateViewForceHidden(CachingIconView view, boolean forceHidden) {
- boolean nowForceHidden = view.willBeForceHidden() || view.isForceHidden();
- if (forceHidden == nowForceHidden) {
- // We are either already forceHidden or will be
- return;
- }
- view.animate().cancel();
- view.setWillBeForceHidden(forceHidden);
- view.animate()
- .scaleX(forceHidden ? 0.5f : 1.0f)
- .scaleY(forceHidden ? 0.5f : 1.0f)
- .alpha(forceHidden ? 0.0f : 1.0f)
- .setInterpolator(forceHidden ? ALPHA_OUT : ALPHA_IN)
- .setDuration(160);
- if (view.getVisibility() != VISIBLE) {
- view.setForceHidden(forceHidden);
- } else {
- view.animate().withEndAction(() -> view.setForceHidden(forceHidden));
- }
- view.animate().start();
- }
-
@RemotableViewMethod
public void setAvatarReplacement(Icon icon) {
mAvatarReplacement = icon;
@@ -561,7 +518,8 @@ public class ConversationLayout extends FrameLayout
if (mConversationIcon == null) {
Icon avatarIcon = messagingGroup.getAvatarIcon();
if (avatarIcon == null) {
- avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor);
+ avatarIcon = mPeopleHelper.createAvatarSymbol(conversationText, "",
+ mLayoutColor);
}
mConversationIcon = avatarIcon;
}
@@ -674,11 +632,11 @@ public class ConversationLayout extends FrameLayout
}
}
if (lastIcon == null) {
- lastIcon = createAvatarSymbol(" ", "", mLayoutColor);
+ lastIcon = mPeopleHelper.createAvatarSymbol(" ", "", mLayoutColor);
}
bottomView.setImageIcon(lastIcon);
if (secondLastIcon == null) {
- secondLastIcon = createAvatarSymbol("", "", mLayoutColor);
+ secondLastIcon = mPeopleHelper.createAvatarSymbol("", "", mLayoutColor);
}
topView.setImageIcon(secondLastIcon);
}
@@ -838,8 +796,10 @@ public class ConversationLayout extends FrameLayout
}
private void updateTitleAndNamesDisplay() {
+ // Map of unique names to their prefix
ArrayMap<CharSequence, String> uniqueNames = new ArrayMap<>();
- ArrayMap<Character, CharSequence> uniqueCharacters = new ArrayMap<>();
+ // Map of single-character string prefix to the only name which uses it, or null if multiple
+ ArrayMap<String, CharSequence> uniqueCharacters = new ArrayMap<>();
for (int i = 0; i < mGroups.size(); i++) {
MessagingGroup group = mGroups.get(i);
CharSequence senderName = group.getSenderName();
@@ -847,22 +807,22 @@ public class ConversationLayout extends FrameLayout
continue;
}
if (!uniqueNames.containsKey(senderName)) {
- // Only use visible characters to get uniqueNames
- String pureSenderName = IGNORABLE_CHAR_PATTERN
- .matcher(senderName).replaceAll("" /* replacement */);
- char c = pureSenderName.charAt(0);
- if (uniqueCharacters.containsKey(c)) {
+ String charPrefix = mPeopleHelper.findNamePrefix(senderName, null);
+ if (charPrefix == null) {
+ continue;
+ }
+ if (uniqueCharacters.containsKey(charPrefix)) {
// this character was already used, lets make it more unique. We first need to
// resolve the existing character if it exists
- CharSequence existingName = uniqueCharacters.get(c);
+ CharSequence existingName = uniqueCharacters.get(charPrefix);
if (existingName != null) {
- uniqueNames.put(existingName, findNameSplit((String) existingName));
- uniqueCharacters.put(c, null);
+ uniqueNames.put(existingName, mPeopleHelper.findNameSplit(existingName));
+ uniqueCharacters.put(charPrefix, null);
}
- uniqueNames.put(senderName, findNameSplit((String) senderName));
+ uniqueNames.put(senderName, mPeopleHelper.findNameSplit(senderName));
} else {
- uniqueNames.put(senderName, Character.toString(c));
- uniqueCharacters.put(c, pureSenderName);
+ uniqueNames.put(senderName, charPrefix);
+ uniqueCharacters.put(charPrefix, senderName);
}
}
}
@@ -898,8 +858,8 @@ public class ConversationLayout extends FrameLayout
} else {
Icon cachedIcon = cachedAvatars.get(senderName);
if (cachedIcon == null) {
- cachedIcon = createAvatarSymbol(senderName, uniqueNames.get(senderName),
- mLayoutColor);
+ cachedIcon = mPeopleHelper.createAvatarSymbol(senderName,
+ uniqueNames.get(senderName), mLayoutColor);
cachedAvatars.put(senderName, cachedIcon);
}
group.setCreatedAvatar(cachedIcon, senderName, uniqueNames.get(senderName),
@@ -908,49 +868,6 @@ public class ConversationLayout extends FrameLayout
}
}
- private Icon createAvatarSymbol(CharSequence senderName, String symbol, int layoutColor) {
- if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol) ||
- SPECIAL_CHAR_PATTERN.matcher(symbol).find()) {
- Icon avatarIcon = Icon.createWithResource(getContext(),
- R.drawable.messaging_user);
- avatarIcon.setTint(findColor(senderName, layoutColor));
- return avatarIcon;
- } else {
- Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- float radius = mAvatarSize / 2.0f;
- int color = findColor(senderName, layoutColor);
- mPaint.setColor(color);
- canvas.drawCircle(radius, radius, radius, mPaint);
- boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f;
- mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE);
- mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f);
- int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
- canvas.drawText(symbol, radius, yPos, mTextPaint);
- return Icon.createWithBitmap(bitmap);
- }
- }
-
- private int findColor(CharSequence senderName, int layoutColor) {
- double luminance = ContrastColorUtil.calculateLuminance(layoutColor);
- float shift = Math.abs(senderName.hashCode()) % 5 / 4.0f - 0.5f;
-
- // we need to offset the range if the luminance is too close to the borders
- shift += Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - luminance, 0);
- shift -= Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - (1.0f - luminance), 0);
- return ContrastColorUtil.getShiftedColor(layoutColor,
- (int) (shift * COLOR_SHIFT_AMOUNT));
- }
-
- private String findNameSplit(String existingName) {
- String[] split = existingName.split(" ");
- if (split.length > 1) {
- return Character.toString(split[0].charAt(0))
- + Character.toString(split[1].charAt(0));
- }
- return existingName.substring(0, 1);
- }
-
@RemotableViewMethod
public void setLayoutColor(int color) {
mLayoutColor = color;
diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
index 5213746e5a12..058a9218def4 100644
--- a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
+++ b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
@@ -16,17 +16,24 @@
package com.android.internal.widget;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.BlendMode;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableWrapper;
import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.Icon;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.RemotableViewMethod;
+import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.LinearLayout;
import android.widget.RemoteViews;
+import com.android.internal.R;
+
/**
* A button implementation for the emphasized notification style.
*
@@ -37,6 +44,7 @@ public class EmphasizedNotificationButton extends Button {
private final RippleDrawable mRipple;
private final int mStrokeWidth;
private final int mStrokeColor;
+ private boolean mPriority;
public EmphasizedNotificationButton(Context context) {
this(context, null);
@@ -80,4 +88,57 @@ public class EmphasizedNotificationButton extends Button {
inner.setStroke(hasStroke ? mStrokeWidth : 0, mStrokeColor);
invalidate();
}
+
+ /**
+ * Sets an image icon which will have its size constrained and will be set to the same color as
+ * the text. Must be called after {@link #setTextColor(int)} for the latter to work.
+ */
+ @RemotableViewMethod(asyncImpl = "setImageIconAsync")
+ public void setImageIcon(@Nullable Icon icon) {
+ final Drawable drawable = icon == null ? null : icon.loadDrawable(mContext);
+ setImageDrawable(drawable);
+ }
+
+ /**
+ * @hide
+ */
+ @RemotableViewMethod
+ public Runnable setImageIconAsync(@Nullable Icon icon) {
+ final Drawable drawable = icon == null ? null : icon.loadDrawable(mContext);
+ return () -> setImageDrawable(drawable);
+ }
+
+ private void setImageDrawable(Drawable drawable) {
+ if (drawable != null) {
+ drawable.mutate();
+ drawable.setTintList(getTextColors());
+ drawable.setTintBlendMode(BlendMode.SRC_IN);
+ int iconSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.notification_actions_icon_drawable_size);
+ drawable.setBounds(0, 0, iconSize, iconSize);
+ }
+ setCompoundDrawablesRelative(drawable, null, null, null);
+ }
+
+ /**
+ * Changes the LayoutParams.width to WRAP_CONTENT, with the argument representing if this view
+ * is a priority over its peers (which affects weight).
+ */
+ @RemotableViewMethod
+ public void setWrapModePriority(boolean priority) {
+ mPriority = priority;
+ ViewGroup.LayoutParams layoutParams = getLayoutParams();
+ layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ if (layoutParams instanceof LinearLayout.LayoutParams) {
+ ((LinearLayout.LayoutParams) layoutParams).weight = 0;
+ }
+ setLayoutParams(layoutParams);
+ }
+
+ /**
+ * Sizing this button is a priority compared with its peers.
+ */
+ public boolean isPriority() {
+ return mPriority;
+ }
}
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index c7ea781b2793..8e6497b204c7 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -16,6 +16,7 @@
package com.android.internal.widget;
+import android.annotation.DimenRes;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.RippleDrawable;
@@ -41,13 +42,16 @@ public class NotificationActionListLayout extends LinearLayout {
private final int mGravity;
private int mTotalWidth = 0;
+ private int mExtraStartPadding = 0;
private ArrayList<Pair<Integer, TextView>> mMeasureOrderTextViews = new ArrayList<>();
private ArrayList<View> mMeasureOrderOther = new ArrayList<>();
private boolean mEmphasizedMode;
+ private boolean mPrioritizedWrapMode;
private int mDefaultPaddingBottom;
private int mDefaultPaddingTop;
private int mEmphasizedHeight;
private int mRegularHeight;
+ @DimenRes private int mCollapsibleIndentDimen;
public NotificationActionListLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -68,7 +72,7 @@ public class NotificationActionListLayout extends LinearLayout {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mEmphasizedMode) {
+ if (mEmphasizedMode && !mPrioritizedWrapMode) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
@@ -151,7 +155,15 @@ public class NotificationActionListLayout extends LinearLayout {
measuredChildren++;
}
- mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft;
+ int collapsibleIndent = mCollapsibleIndentDimen == 0 ? 0
+ : getResources().getDimensionPixelOffset(mCollapsibleIndentDimen);
+ if (innerWidth - usedWidth > collapsibleIndent) {
+ mExtraStartPadding = collapsibleIndent;
+ } else {
+ mExtraStartPadding = 0;
+ }
+
+ mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft + mExtraStartPadding;
setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec),
resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
@@ -163,7 +175,11 @@ public class NotificationActionListLayout extends LinearLayout {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View c = getChildAt(i);
- if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
+ if (c instanceof EmphasizedNotificationButton
+ && ((EmphasizedNotificationButton) c).isPriority()) {
+ // add with 0 length to ensure that this view is measured before others.
+ mMeasureOrderTextViews.add(Pair.create(0, (TextView) c));
+ } else if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
mMeasureOrderTextViews.add(Pair.create(((TextView) c).getText().length(),
(TextView)c));
} else {
@@ -197,7 +213,7 @@ public class NotificationActionListLayout extends LinearLayout {
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (mEmphasizedMode) {
+ if (mEmphasizedMode && !mPrioritizedWrapMode) {
super.onLayout(changed, left, top, right, bottom);
return;
}
@@ -214,6 +230,9 @@ public class NotificationActionListLayout extends LinearLayout {
int absoluteGravity = Gravity.getAbsoluteGravity(Gravity.START, getLayoutDirection());
if (absoluteGravity == Gravity.RIGHT) {
childLeft += right - left - mTotalWidth;
+ } else {
+ // Put the extra start padding (if any) on the left when LTR
+ childLeft += mExtraStartPadding;
}
}
@@ -274,6 +293,26 @@ public class NotificationActionListLayout extends LinearLayout {
}
/**
+ * When used with emphasizedMode, changes the button sizing behavior to prioritize certain
+ * buttons (which are system generated) to not scrunch, and leave the remaining space for
+ * custom actions.
+ */
+ @RemotableViewMethod
+ public void setPrioritizedWrapMode(boolean prioritizedWrapMode) {
+ mPrioritizedWrapMode = prioritizedWrapMode;
+ }
+
+ /**
+ * When buttons are in wrap mode, this is a padding that will be applied at the start of the
+ * layout of the actions, but only when those actions would fit with the entire padding
+ * visible. Otherwise, this padding will be omitted entirely.
+ */
+ @RemotableViewMethod
+ public void setCollapsibleIndentDimen(@DimenRes int collapsibleIndentDimen) {
+ mCollapsibleIndentDimen = collapsibleIndentDimen;
+ }
+
+ /**
* Set whether the list is in a mode where some actions are emphasized. This will trigger an
* equal measuring where all actions are full height and change a few parameters like
* the padding.
diff --git a/core/java/com/android/internal/widget/PeopleHelper.java b/core/java/com/android/internal/widget/PeopleHelper.java
new file mode 100644
index 000000000000..77f4c8f6bede
--- /dev/null
+++ b/core/java/com/android/internal/widget/PeopleHelper.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_IN;
+import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_OUT;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.android.internal.R;
+import com.android.internal.graphics.ColorUtils;
+import com.android.internal.util.ContrastColorUtil;
+
+import java.util.regex.Pattern;
+
+/**
+ * This class provides some methods used by both the {@link ConversationLayout} and
+ * {@link CallLayout} which both use the visual design originally created for conversations in R.
+ */
+public class PeopleHelper {
+
+ private static final float COLOR_SHIFT_AMOUNT = 60;
+ /**
+ * Pattern for filter some ignorable characters.
+ * p{Z} for any kind of whitespace or invisible separator.
+ * p{C} for any kind of punctuation character.
+ */
+ private static final Pattern IGNORABLE_CHAR_PATTERN = Pattern.compile("[\\p{C}\\p{Z}]");
+ private static final Pattern SPECIAL_CHAR_PATTERN =
+ Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]");
+
+ private Context mContext;
+ private int mAvatarSize;
+ private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private Paint mTextPaint = new Paint();
+
+ /**
+ * Call this when the view is inflated to provide a context and initialize the helper
+ */
+ public void init(Context context) {
+ mContext = context;
+ mAvatarSize = context.getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size);
+ mTextPaint.setTextAlign(Paint.Align.CENTER);
+ mTextPaint.setAntiAlias(true);
+ }
+
+ /**
+ * A utility for animating CachingIconViews away when hidden.
+ */
+ public void animateViewForceHidden(CachingIconView view, boolean forceHidden) {
+ boolean nowForceHidden = view.willBeForceHidden() || view.isForceHidden();
+ if (forceHidden == nowForceHidden) {
+ // We are either already forceHidden or will be
+ return;
+ }
+ view.animate().cancel();
+ view.setWillBeForceHidden(forceHidden);
+ view.animate()
+ .scaleX(forceHidden ? 0.5f : 1.0f)
+ .scaleY(forceHidden ? 0.5f : 1.0f)
+ .alpha(forceHidden ? 0.0f : 1.0f)
+ .setInterpolator(forceHidden ? ALPHA_OUT : ALPHA_IN)
+ .setDuration(160);
+ if (view.getVisibility() != View.VISIBLE) {
+ view.setForceHidden(forceHidden);
+ } else {
+ view.animate().withEndAction(() -> view.setForceHidden(forceHidden));
+ }
+ view.animate().start();
+ }
+
+ /**
+ * This creates an avatar symbol for the given person or group
+ *
+ * @param name the name of the person or group
+ * @param symbol a pre-chosen symbol for the person or group. See
+ * {@link #findNamePrefix(CharSequence, String)} or
+ * {@link #findNameSplit(CharSequence)}
+ * @param layoutColor the background color of the layout
+ */
+ @NonNull
+ public Icon createAvatarSymbol(@NonNull CharSequence name, @NonNull String symbol,
+ @ColorInt int layoutColor) {
+ if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol)
+ || SPECIAL_CHAR_PATTERN.matcher(symbol).find()) {
+ Icon avatarIcon = Icon.createWithResource(mContext, R.drawable.messaging_user);
+ avatarIcon.setTint(findColor(name, layoutColor));
+ return avatarIcon;
+ } else {
+ Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ float radius = mAvatarSize / 2.0f;
+ int color = findColor(name, layoutColor);
+ mPaint.setColor(color);
+ canvas.drawCircle(radius, radius, radius, mPaint);
+ boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f;
+ mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE);
+ mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f);
+ int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
+ canvas.drawText(symbol, radius, yPos, mTextPaint);
+ return Icon.createWithBitmap(bitmap);
+ }
+ }
+
+ private int findColor(@NonNull CharSequence senderName, int layoutColor) {
+ double luminance = ContrastColorUtil.calculateLuminance(layoutColor);
+ float shift = Math.abs(senderName.hashCode()) % 5 / 4.0f - 0.5f;
+
+ // we need to offset the range if the luminance is too close to the borders
+ shift += Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - luminance, 0);
+ shift -= Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - (1.0f - luminance), 0);
+ return ContrastColorUtil.getShiftedColor(layoutColor,
+ (int) (shift * COLOR_SHIFT_AMOUNT));
+ }
+
+ /**
+ * Get the name with whitespace and punctuation characters removed
+ */
+ private String getPureName(@NonNull CharSequence name) {
+ return IGNORABLE_CHAR_PATTERN.matcher(name).replaceAll("" /* replacement */);
+ }
+
+ /**
+ * Gets a single character string prefix name for the person or group
+ *
+ * @param name the name of the person or group
+ * @param fallback the string to return if the name has no usable characters
+ */
+ public String findNamePrefix(@NonNull CharSequence name, String fallback) {
+ String pureName = getPureName(name);
+ if (pureName.isEmpty()) {
+ return fallback;
+ }
+ try {
+ return new String(Character.toChars(pureName.codePointAt(0)));
+ } catch (RuntimeException ignore) {
+ return fallback;
+ }
+ }
+
+ /**
+ * Find a 1 or 2 character prefix name for the person or group
+ */
+ public String findNameSplit(@NonNull CharSequence name) {
+ String nameString = name instanceof String ? ((String) name) : name.toString();
+ String[] split = nameString.trim().split("[ ]+");
+ if (split.length > 1) {
+ String first = findNamePrefix(split[0], null);
+ String second = findNamePrefix(split[1], null);
+ if (first != null && second != null) {
+ return first + second;
+ }
+ }
+ return findNamePrefix(name, "");
+ }
+}
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index bb51c57c74b2..c64174b93c0d 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -43,6 +43,7 @@
#include <nativehelper/JNIPlatformHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include "jni.h"
+#include <dmabufinfo/dmabuf_sysfs_stats.h>
#include <dmabufinfo/dmabufinfo.h>
#include <meminfo/procmeminfo.h>
#include <meminfo/sysmeminfo.h>
@@ -805,6 +806,16 @@ static jlong android_os_Debug_getIonHeapsSizeKb(JNIEnv* env, jobject clazz) {
return heapsSizeKb;
}
+static jlong android_os_Debug_getDmabufTotalExportedKb(JNIEnv* env, jobject clazz) {
+ jlong dmabufTotalSizeKb = -1;
+ uint64_t size;
+
+ if (dmabufinfo::GetDmabufTotalExportedKb(&size)) {
+ dmabufTotalSizeKb = size;
+ }
+ return dmabufTotalSizeKb;
+}
+
static jlong android_os_Debug_getIonPoolsSizeKb(JNIEnv* env, jobject clazz) {
jlong poolsSizeKb = -1;
uint64_t size;
@@ -816,8 +827,19 @@ static jlong android_os_Debug_getIonPoolsSizeKb(JNIEnv* env, jobject clazz) {
return poolsSizeKb;
}
-static jlong android_os_Debug_getIonMappedSizeKb(JNIEnv* env, jobject clazz) {
- jlong ionPss = 0;
+static jlong android_os_Debug_getDmabufHeapPoolsSizeKb(JNIEnv* env, jobject clazz) {
+ jlong poolsSizeKb = -1;
+ uint64_t size;
+
+ if (meminfo::ReadDmabufHeapPoolsSizeKb(&size)) {
+ poolsSizeKb = size;
+ }
+
+ return poolsSizeKb;
+}
+
+static jlong android_os_Debug_getDmabufMappedSizeKb(JNIEnv* env, jobject clazz) {
+ jlong dmabufPss = 0;
std::vector<dmabufinfo::DmaBuffer> dmabufs;
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir);
@@ -841,10 +863,10 @@ static jlong android_os_Debug_getIonMappedSizeKb(JNIEnv* env, jobject clazz) {
}
for (const dmabufinfo::DmaBuffer& buf : dmabufs) {
- ionPss += buf.size() / 1024;
+ dmabufPss += buf.size() / 1024;
}
- return ionPss;
+ return dmabufPss;
}
static jlong android_os_Debug_getGpuTotalUsageKb(JNIEnv* env, jobject clazz) {
@@ -922,10 +944,14 @@ static const JNINativeMethod gMethods[] = {
(void*)android_os_Debug_getFreeZramKb },
{ "getIonHeapsSizeKb", "()J",
(void*)android_os_Debug_getIonHeapsSizeKb },
+ { "getDmabufTotalExportedKb", "()J",
+ (void*)android_os_Debug_getDmabufTotalExportedKb },
{ "getIonPoolsSizeKb", "()J",
(void*)android_os_Debug_getIonPoolsSizeKb },
- { "getIonMappedSizeKb", "()J",
- (void*)android_os_Debug_getIonMappedSizeKb },
+ { "getDmabufMappedSizeKb", "()J",
+ (void*)android_os_Debug_getDmabufMappedSizeKb },
+ { "getDmabufHeapPoolsSizeKb", "()J",
+ (void*)android_os_Debug_getDmabufHeapPoolsSizeKb },
{ "getGpuTotalUsageKb", "()J",
(void*)android_os_Debug_getGpuTotalUsageKb },
{ "isVmapStack", "()Z",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cc7251a4b3b0..856657ad8b56 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2698,11 +2698,11 @@
The app can check whether it has this authorization by calling
{@link android.provider.Settings#canDrawOverlays
Settings.canDrawOverlays()}.
- <p>Protection level: signature|appop|installer|recents|appPredictor|pre23|development -->
+ <p>Protection level: signature|appop|installer|appPredictor|pre23|development -->
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
android:label="@string/permlab_systemAlertWindow"
android:description="@string/permdesc_systemAlertWindow"
- android:protectionLevel="signature|appop|installer|recents|appPredictor|pre23|development" />
+ android:protectionLevel="signature|appop|installer|appPredictor|pre23|development" />
<!-- @SystemApi @hide Allows an application to create windows using the type
{@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY},
diff --git a/core/res/res/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml
index 1a574fe39e6e..ad680549aaa1 100644
--- a/core/res/res/drawable/btn_notification_emphasized.xml
+++ b/core/res/res/drawable/btn_notification_emphasized.xml
@@ -23,7 +23,7 @@
<ripple android:color="?attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
- <corners android:radius="?attr/buttonCornerRadius" />
+ <corners android:radius="@dimen/notification_action_button_radius" />
<padding android:left="@dimen/button_padding_horizontal_material"
android:top="@dimen/button_padding_vertical_material"
android:right="@dimen/button_padding_horizontal_material"
diff --git a/core/res/res/drawable/ic_call_answer.xml b/core/res/res/drawable/ic_call_answer.xml
new file mode 100644
index 000000000000..77c0ad1a585f
--- /dev/null
+++ b/core/res/res/drawable/ic_call_answer.xml
@@ -0,0 +1,34 @@
+<!--
+ Copyright (C) 2021 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="24dp"
+ android:height="24dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20"
+ android:tint="?android:attr/colorControlNormal"
+ android:autoMirrored="true">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.0168,3.3333L6.5751,5.575L4.6668,7.4916C3.8751,5.7166 3.6001,4.1916
+ 3.4834,3.3333H6.0168ZM14.4251,13.375L16.6668,13.9416V16.5166C15.8084,16.4 14.2668,16.125
+ 12.4834,15.325L14.4251,13.375ZM6.3418,1.6666H2.5668C2.0918,1.6666 1.7001,2.0666
+ 1.7334,2.5416C2.4834,12.875 11.7668,18.275 17.5168,18.275C17.9668,18.275 18.3334,17.9
+ 18.3334,17.4416V13.6166C18.3334,13.0416 17.9418,12.5416
+ 17.3834,12.4083L14.5918,11.7083C14.2251,11.6166 13.7584,11.6833
+ 13.4084,12.0333L10.9251,14.5166C8.6751,13.1833 6.7918,11.3
+ 5.4668,9.0416L7.9168,6.5916C8.2251,6.2833 8.3501,5.8333
+ 8.2418,5.4083L7.5584,2.6166C7.4168,2.0583 6.9168,1.6666 6.3418,1.6666Z"/>
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_call_decline.xml b/core/res/res/drawable/ic_call_decline.xml
new file mode 100644
index 000000000000..a5ee8f4e6e72
--- /dev/null
+++ b/core/res/res/drawable/ic_call_decline.xml
@@ -0,0 +1,36 @@
+<!--
+ Copyright (C) 2021 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="24dp"
+ android:height="24dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20"
+ android:tint="?android:attr/colorControlNormal"
+ android:autoMirrored="true">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5.2834,9.3083V10.7833L4.1084,11.4916L3.1334,10.5166C3.8084,10.0416
+ 4.5251,9.6333 5.2834,9.3083ZM14.75,9.325C15.4917,9.65 16.2084,10.05
+ 16.875,10.525L15.925,11.475L14.75,10.7666V9.325ZM9.975,6.6666C6.8584,6.6666 3.725,7.7333
+ 1.1751,9.9416C0.8084,10.2583 0.9667,10.7166 1.1501,10.9L3.2917,13.0416C3.4917,13.2333
+ 3.7417,13.3333 4.0001,13.3333C4.175,13.3333 4.35,13.2833
+ 4.5084,13.1916L6.4667,12.0166C6.725,11.8583 6.95,11.5583 6.95,11.1666V8.3833C7.95,8.125
+ 8.975,8 10.0084,8C11.0417,8 12.075,8.1333 13.0834,8.3916V11.1416C13.0834,11.4916
+ 13.2667,11.8166 13.5667,11.9916L15.525,13.1666C15.6834,13.2583 15.8584,13.3083
+ 16.0334,13.3083C16.2917,13.3083 16.5417,13.2083
+ 16.7334,13.0166L18.85,10.9C19.1167,10.6333 19.1167,10.1916 18.825,9.9416C16.3334,7.7833
+ 13.1667,6.6666 9.975,6.6666Z"/>
+</vector>
diff --git a/core/res/res/layout/notification_material_action_emphasized.xml b/core/res/res/layout/notification_material_action_emphasized.xml
index a6b7b380eaa4..cd1f1ab88c96 100644
--- a/core/res/res/layout/notification_material_action_emphasized.xml
+++ b/core/res/res/layout/notification_material_action_emphasized.xml
@@ -22,6 +22,7 @@
android:layout_height="match_parent"
android:layout_marginStart="12dp"
android:layout_weight="1"
+ android:drawablePadding="6dp"
android:gravity="center"
android:textColor="@color/notification_default_color"
android:singleLine="true"
diff --git a/core/res/res/layout/notification_template_conversation_header.xml b/core/res/res/layout/notification_template_conversation_header.xml
new file mode 100644
index 000000000000..b018676e68f0
--- /dev/null
+++ b/core/res/res/layout/notification_template_conversation_header.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/conversation_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="16dp"
+ >
+
+ <TextView
+ android:id="@+id/conversation_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ android:textSize="16sp"
+ android:singleLine="true"
+ android:layout_weight="1"
+ />
+
+ <TextView
+ android:id="@+id/app_name_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:layout_gravity="center"
+ android:paddingTop="1sp"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <!-- App Name -->
+ <com.android.internal.widget.ObservableTextView
+ android:id="@+id/app_name_text"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
+ android:paddingTop="1sp"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <TextView
+ android:id="@+id/time_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:layout_gravity="center"
+ android:paddingTop="1sp"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <DateTimeView
+ android:id="@+id/time"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:paddingTop="1sp"
+ android:showRelative="true"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <ViewStub
+ android:id="@+id/chronometer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:layout="@layout/notification_template_part_chronometer"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/verification_icon"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
+ android:layout_gravity="center"
+ android:layout_marginStart="4dp"
+ android:contentDescription="@string/notification_alerted_content_description"
+ android:paddingTop="2dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_notifications_alerted"
+ android:visibility="gone"
+ />
+
+ <TextView
+ android:id="@+id/verification_text"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:paddingTop="1sp"
+ android:showRelative="true"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <ImageButton
+ android:id="@+id/feedback"
+ android:layout_width="@dimen/notification_feedback_size"
+ android:layout_height="@dimen/notification_feedback_size"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:contentDescription="@string/notification_feedback_indicator"
+ android:paddingTop="2dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_feedback_indicator"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/profile_badge"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
+ android:layout_gravity="center"
+ android:layout_marginStart="4dp"
+ android:paddingTop="2dp"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ android:contentDescription="@string/notification_work_profile_content_description"
+ />
+
+ <ImageView
+ android:id="@+id/alerted_icon"
+ android:layout_width="@dimen/notification_alerted_size"
+ android:layout_height="@dimen/notification_alerted_size"
+ android:layout_gravity="center"
+ android:layout_marginStart="4dp"
+ android:contentDescription="@string/notification_alerted_content_description"
+ android:paddingTop="2dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_notifications_alerted"
+ android:visibility="gone"
+ />
+</LinearLayout>
diff --git a/core/res/res/layout/notification_template_conversation_icon_container.xml b/core/res/res/layout/notification_template_conversation_icon_container.xml
new file mode 100644
index 000000000000..e9ec7ce77deb
--- /dev/null
+++ b/core/res/res/layout/notification_template_conversation_icon_container.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/conversation_icon_container"
+ android:layout_width="@dimen/conversation_content_start"
+ android:layout_height="wrap_content"
+ android:gravity="start|top"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:importantForAccessibility="no"
+ >
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_gravity="top|center_horizontal"
+ >
+
+ <!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right -->
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/conversation_icon"
+ android:layout_width="@dimen/conversation_avatar_size"
+ android:layout_height="@dimen/conversation_avatar_size"
+ android:scaleType="centerCrop"
+ android:importantForAccessibility="no"
+ />
+
+ <ViewStub
+ android:layout="@layout/conversation_face_pile_layout"
+ android:layout_width="@dimen/conversation_avatar_size"
+ android:layout_height="@dimen/conversation_avatar_size"
+ android:id="@+id/conversation_face_pile"
+ />
+
+ <FrameLayout
+ android:id="@+id/conversation_icon_badge"
+ android:layout_width="@dimen/conversation_icon_size_badged"
+ android:layout_height="@dimen/conversation_icon_size_badged"
+ android:layout_marginLeft="@dimen/conversation_badge_side_margin"
+ android:layout_marginTop="@dimen/conversation_badge_side_margin"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ >
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/conversation_icon_badge_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:src="@drawable/conversation_badge_background"
+ android:forceHasOverlappingRendering="false"
+ android:scaleType="center"
+ />
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="4dp"
+ android:layout_gravity="center"
+ android:forceHasOverlappingRendering="false"
+ />
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/conversation_icon_badge_ring"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:src="@drawable/conversation_badge_ring"
+ android:visibility="gone"
+ android:forceHasOverlappingRendering="false"
+ android:clipToPadding="false"
+ android:scaleType="center"
+ />
+ </FrameLayout>
+ </FrameLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_call.xml b/core/res/res/layout/notification_template_material_call.xml
new file mode 100644
index 000000000000..471d874c59f5
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_call.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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.internal.widget.CallLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ android:tag="call"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
+ <include layout="@layout/notification_template_conversation_icon_container" />
+
+ <LinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/notification_action_list_height"
+ android:orientation="vertical"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="top"
+ android:orientation="horizontal"
+ >
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginStart="@dimen/conversation_content_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:orientation="vertical"
+ android:minHeight="68dp"
+ >
+
+ <include
+ layout="@layout/notification_template_conversation_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <include layout="@layout/notification_template_text" />
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_progress_bar_height"
+ android:layout_marginTop="@dimen/notification_progress_margin_top"
+ layout="@layout/notification_template_progress"
+ />
+ </LinearLayout>
+
+ <!-- TODO(b/179178086): remove padding from main column when this is visible -->
+ <com.android.internal.widget.NotificationExpandButton
+ android:id="@+id/expand_button"
+ android:layout_width="@dimen/notification_header_expand_icon_size"
+ android:layout_height="@dimen/notification_header_expand_icon_size"
+ android:layout_gravity="top|end"
+ android:contentDescription="@string/expand_button_content_description_collapsed"
+ android:paddingTop="@dimen/notification_expand_button_padding_top"
+ android:scaleType="center"
+ android:visibility="gone"
+ />
+
+ </LinearLayout>
+
+ <include layout="@layout/notification_material_action_list" />
+
+ </LinearLayout>
+
+</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index f9364d565f3b..f3aa54066c92 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -24,82 +24,7 @@
android:theme="@style/Theme.DeviceDefault.Notification"
>
- <FrameLayout
- android:id="@+id/conversation_icon_container"
- android:layout_width="@dimen/conversation_content_start"
- android:layout_height="wrap_content"
- android:gravity="start|top"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:paddingTop="12dp"
- android:paddingBottom="12dp"
- android:importantForAccessibility="no"
- >
-
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_gravity="top|center_horizontal"
- >
-
- <!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right -->
- <com.android.internal.widget.CachingIconView
- android:id="@+id/conversation_icon"
- android:layout_width="@dimen/conversation_avatar_size"
- android:layout_height="@dimen/conversation_avatar_size"
- android:scaleType="centerCrop"
- android:importantForAccessibility="no"
- />
-
- <ViewStub
- android:layout="@layout/conversation_face_pile_layout"
- android:layout_width="@dimen/conversation_avatar_size"
- android:layout_height="@dimen/conversation_avatar_size"
- android:id="@+id/conversation_face_pile"
- />
-
- <FrameLayout
- android:id="@+id/conversation_icon_badge"
- android:layout_width="@dimen/conversation_icon_size_badged"
- android:layout_height="@dimen/conversation_icon_size_badged"
- android:layout_marginLeft="@dimen/conversation_badge_side_margin"
- android:layout_marginTop="@dimen/conversation_badge_side_margin"
- android:clipChildren="false"
- android:clipToPadding="false"
- >
- <com.android.internal.widget.CachingIconView
- android:id="@+id/conversation_icon_badge_bg"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:src="@drawable/conversation_badge_background"
- android:forceHasOverlappingRendering="false"
- android:scaleType="center"
- />
- <com.android.internal.widget.CachingIconView
- android:id="@+id/icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_margin="4dp"
- android:layout_gravity="center"
- android:forceHasOverlappingRendering="false"
- />
- <com.android.internal.widget.CachingIconView
- android:id="@+id/conversation_icon_badge_ring"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:src="@drawable/conversation_badge_ring"
- android:visibility="gone"
- android:forceHasOverlappingRendering="false"
- android:clipToPadding="false"
- android:scaleType="center"
- />
- </FrameLayout>
- </FrameLayout>
- </FrameLayout>
+ <include layout="@layout/notification_template_conversation_icon_container" />
<!-- Wraps entire "expandable" notification -->
<com.android.internal.widget.RemeasuringLinearLayout
@@ -132,161 +57,14 @@
<!-- Use layout_marginStart instead of paddingStart to work around strange
measurement behavior on lower display densities. -->
- <LinearLayout
- android:id="@+id/conversation_header"
+ <include
+ layout="@layout/notification_template_conversation_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingTop="16dp"
android:layout_marginBottom="2dp"
android:layout_marginStart="@dimen/conversation_content_start"
- >
- <TextView
- android:id="@+id/conversation_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
- android:textSize="16sp"
- android:singleLine="true"
- android:layout_weight="1"
- />
-
- <TextView
- android:id="@+id/app_name_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:layout_gravity="center"
- android:paddingTop="1sp"
- android:singleLine="true"
- android:visibility="gone"
/>
- <!-- App Name -->
- <com.android.internal.widget.ObservableTextView
- android:id="@+id/app_name_text"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
- android:paddingTop="1sp"
- android:singleLine="true"
- android:visibility="gone"
- />
-
- <TextView
- android:id="@+id/time_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:layout_gravity="center"
- android:paddingTop="1sp"
- android:singleLine="true"
- android:visibility="gone"
- />
-
- <DateTimeView
- android:id="@+id/time"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:paddingTop="1sp"
- android:showRelative="true"
- android:singleLine="true"
- android:visibility="gone"
- />
-
- <ImageButton
- android:id="@+id/feedback"
- android:layout_width="@dimen/notification_feedback_size"
- android:layout_height="@dimen/notification_feedback_size"
- android:layout_gravity="center"
- android:layout_marginStart="@dimen/notification_header_separating_margin"
- android:background="?android:selectableItemBackgroundBorderless"
- android:contentDescription="@string/notification_feedback_indicator"
- android:paddingTop="2dp"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_feedback_indicator"
- android:visibility="gone"
- />
-
- <ImageView
- android:id="@+id/profile_badge"
- android:layout_width="@dimen/notification_badge_size"
- android:layout_height="@dimen/notification_badge_size"
- android:layout_gravity="center"
- android:layout_marginStart="4dp"
- android:paddingTop="2dp"
- android:scaleType="fitCenter"
- android:visibility="gone"
- android:contentDescription="@string/notification_work_profile_content_description"
- />
-
- <ImageView
- android:id="@+id/alerted_icon"
- android:layout_width="@dimen/notification_alerted_size"
- android:layout_height="@dimen/notification_alerted_size"
- android:layout_gravity="center"
- android:layout_marginStart="4dp"
- android:contentDescription="@string/notification_alerted_content_description"
- android:paddingTop="2dp"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_notifications_alerted"
- android:visibility="gone"
- />
-
- <LinearLayout
- android:id="@+id/app_ops"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingTop="3dp"
- android:layout_marginStart="2dp"
- android:background="?android:selectableItemBackgroundBorderless"
- android:orientation="horizontal" >
- <ImageView
- android:layout_marginStart="4dp"
- android:id="@+id/camera"
- android:layout_width="?attr/notificationHeaderIconSize"
- android:layout_height="?attr/notificationHeaderIconSize"
- android:src="@drawable/ic_camera"
- android:visibility="gone"
- android:focusable="false"
- android:contentDescription="@string/notification_appops_camera_active"
- />
- <ImageView
- android:id="@+id/mic"
- android:layout_width="?attr/notificationHeaderIconSize"
- android:layout_height="?attr/notificationHeaderIconSize"
- android:src="@drawable/ic_mic"
- android:layout_marginStart="4dp"
- android:visibility="gone"
- android:focusable="false"
- android:contentDescription="@string/notification_appops_microphone_active"
- />
- <ImageView
- android:id="@+id/overlay"
- android:layout_width="?attr/notificationHeaderIconSize"
- android:layout_height="?attr/notificationHeaderIconSize"
- android:src="@drawable/ic_alert_window_layer"
- android:layout_marginStart="4dp"
- android:visibility="gone"
- android:focusable="false"
- android:contentDescription="@string/notification_appops_overlay_active"
- />
- </LinearLayout>
- </LinearLayout>
-
<!-- Messages -->
<com.android.internal.widget.MessagingLinearLayout
android:id="@+id/notification_messaging"
diff --git a/core/res/res/layout/notification_top_line_views.xml b/core/res/res/layout/notification_top_line_views.xml
index 7cda03f8fc4f..7656dd50b2d4 100644
--- a/core/res/res/layout/notification_top_line_views.xml
+++ b/core/res/res/layout/notification_top_line_views.xml
@@ -26,7 +26,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:singleLine="true"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:visibility="?attr/notificationHeaderAppNameVisibility"
/>
@@ -34,7 +34,7 @@
android:id="@+id/header_text_secondary_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:layout_marginStart="@dimen/notification_header_separating_margin"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:text="@string/notification_header_divider_symbol"
@@ -45,7 +45,7 @@
android:id="@+id/header_text_secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:layout_marginStart="@dimen/notification_header_separating_margin"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:visibility="gone"
@@ -56,7 +56,7 @@
android:id="@+id/header_text_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:layout_marginStart="@dimen/notification_header_separating_margin"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:text="@string/notification_header_divider_symbol"
@@ -67,7 +67,7 @@
android:id="@+id/header_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:layout_marginStart="@dimen/notification_header_separating_margin"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:visibility="gone"
@@ -78,7 +78,7 @@
android:id="@+id/time_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:layout_marginStart="@dimen/notification_header_separating_margin"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:text="@string/notification_header_divider_symbol"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e567c3d7d486..586c99d76335 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3251,6 +3251,16 @@
a value of 'true' will not override any 'false' value in its parent chain nor will
it prevent any 'false' in any of its children. -->
<attr name="forceDarkAllowed" format="boolean" />
+
+ <!-- <p>Whether the View's Outline should be used to clip the contents of the View.
+ <p>Only a single non-rectangular clip can be applied on a View at any time. Circular
+ clips from a
+ {@link android.view.ViewAnimationUtils#createCircularReveal(View, int, int, float,
+ float)} circular reveal animation take priority over Outline clipping, and child
+ Outline clipping takes priority over Outline clipping done by a parent.
+ <p>Note that this flag will only be respected if the View's Outline returns true from
+ {@link android.graphics.Outline#canClip()}. -->
+ <attr name="clipToOutline" format="boolean" />
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 20a5d379cbcc..5546621b6ee8 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -153,6 +153,11 @@
<color name="notification_action_list_background_color">@null</color>
+ <!-- The color of the Decline and Hang Up actions on a CallStyle notification -->
+ <color name="call_notification_decline_color">#d93025</color>
+ <!-- The color of the Answer action on a CallStyle notification -->
+ <color name="call_notification_answer_color">#1e8e3e</color>
+
<!-- Keyguard colors -->
<color name="keyguard_avatar_frame_color">#ffffffff</color>
<color name="keyguard_avatar_frame_shadow_color">#80000000</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8c5f454d204d..beae9353a10f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4712,4 +4712,10 @@
<!-- Whether to select voice/data/sms preference without user confirmation -->
<bool name="config_voice_data_sms_auto_fallback">false</bool>
+
+ <!-- Whether to enable the one-handed keyguard on the lock screen for wide-screen devices. -->
+ <bool name="config_enableOneHandedKeyguard">false</bool>
+
+ <!-- Whether to allow the caching of the SIM PIN for verification after unattended reboot -->
+ <bool name="config_allow_pin_storage_for_unattended_reboot">true</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index cb16af5a16b0..3b6e41ffa92b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -236,9 +236,20 @@
value is calculated in ConversationLayout#updateActionListPadding() -->
<dimen name="notification_actions_padding_start">36dp</dimen>
+ <!-- The start padding to optionally use (e.g. if there's extra space) for CallStyle
+ notification actions.
+ this = conversation_content_start (80dp) - button inset (4dp) - action padding (12dp) -->
+ <dimen name="call_notification_collapsible_indent">64dp</dimen>
+
<!-- The size of icons for visual actions in the notification_material_action_list -->
<dimen name="notification_actions_icon_size">48dp</dimen>
+ <!-- The size of icons for visual actions in the notification_material_action_list -->
+ <dimen name="notification_actions_icon_drawable_size">20dp</dimen>
+
+ <!-- The corner radius if the emphasized action buttons in a notification -->
+ <dimen name="notification_action_button_radius">8dp</dimen>
+
<!-- Size of the stroke with for the emphasized notification button style -->
<dimen name="emphasized_button_stroke_width">1dp</dimen>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index a4c7293e48b6..ab4e0f3b608e 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -102,6 +102,10 @@
<item type="id" name="selection_end_handle" />
<item type="id" name="insertion_handle" />
<item type="id" name="floating_toolbar_menu_item_image_button" />
+ <item type="id" name="camera" />
+ <item type="id" name="mic" />
+ <item type="id" name="overlay" />
+ <item type="id" name="app_ops" />
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_ON_SCREEN}. -->
<item type="id" name="accessibilityActionShowOnScreen" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 97ec0f4fa71e..d3f3ebd7c3d9 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3062,6 +3062,7 @@
<!-- @hide @SystemApi -->
<public name="hotwordDetectionService" />
<public name="previewLayout" />
+ <public name="clipToOutline" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9b5f67091a2a..af5e406979ad 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5039,6 +5039,18 @@
<!-- Tempalate for Notification.MessagingStyle to join a conversation name with the name of the sender of a message, to make a notification title [CHAR LIMIT=NONE] -->
<string name="notification_messaging_title_template"><xliff:g id="conversation_title" example="Tasty Treat Team">%1$s</xliff:g>: <xliff:g id="sender_name" example="Adrian Baker">%2$s</xliff:g></string>
+ <!-- Action text to be displayed for the "answer" action of an incoming call [CHAR LIMIT=13] -->
+ <string name="call_notification_answer_action">Answer</string>
+ <!-- Action text to be displayed for the "decline" action of an incoming call [CHAR LIMIT=13] -->
+ <string name="call_notification_decline_action">Decline</string>
+ <!-- Action text to be displayed for the "hang up" action of an ongoing call [CHAR LIMIT=13] -->
+ <string name="call_notification_hang_up_action">Hang Up</string>
+ <!-- Default notification text to be displayed in incoming call notifications [CHAR LIMIT=40] -->
+ <string name="call_notification_incoming_text">Incoming call</string>
+ <!-- Default notification text to be displayed in ongoing call notifications [CHAR LIMIT=40] -->
+ <string name="call_notification_ongoing_text">Ongoing call</string>
+ <!-- Default notification text to be displayed in screening call notifications [CHAR LIMIT=40] -->
+ <string name="call_notification_screening_text">Screening an incoming call</string>
<!-- Label describing the number of selected items [CHAR LIMIT=48] -->
<plurals name="selected_count">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c41b78e4a680..4109d4c9f6f9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2647,6 +2647,7 @@
<java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" />
<java-symbol type="bool" name="config_overrideRemoteViewsActivityTransition" />
<java-symbol type="attr" name="colorProgressBackgroundNormal" />
+ <java-symbol type="bool" name="config_allow_pin_storage_for_unattended_reboot" />
<java-symbol type="layout" name="simple_account_item" />
<java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" />
@@ -3088,8 +3089,26 @@
<!-- TV Remote Service package -->
<java-symbol type="string" name="config_tvRemoteServicePackage" />
+
+ <!-- Notifications: MessagingStyle -->
<java-symbol type="string" name="notification_messaging_title_template" />
+ <!-- Notifications: CallStyle -->
+ <java-symbol type="layout" name="notification_template_material_call" />
+ <java-symbol type="string" name="call_notification_answer_action" />
+ <java-symbol type="string" name="call_notification_decline_action" />
+ <java-symbol type="string" name="call_notification_hang_up_action" />
+ <java-symbol type="string" name="call_notification_incoming_text" />
+ <java-symbol type="string" name="call_notification_ongoing_text" />
+ <java-symbol type="string" name="call_notification_screening_text" />
+ <java-symbol type="color" name="call_notification_decline_color"/>
+ <java-symbol type="color" name="call_notification_answer_color"/>
+ <java-symbol type="dimen" name="call_notification_collapsible_indent"/>
+ <java-symbol type="drawable" name="ic_call_answer" />
+ <java-symbol type="drawable" name="ic_call_decline" />
+ <java-symbol type="id" name="verification_icon" />
+ <java-symbol type="id" name="verification_text" />
+
<!-- Notification handler / dashboard package -->
<java-symbol type="string" name="config_notificationHandlerPackage" />
@@ -3415,6 +3434,7 @@
<java-symbol type="dimen" name="notification_media_image_max_width"/>
<java-symbol type="dimen" name="notification_media_image_max_height"/>
<java-symbol type="dimen" name="notification_right_icon_size"/>
+ <java-symbol type="dimen" name="notification_actions_icon_drawable_size"/>
<java-symbol type="dimen" name="notification_custom_view_max_image_height"/>
<java-symbol type="dimen" name="notification_custom_view_max_image_width"/>
diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
index cfcfcc8cf044..ece37f8054cf 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
@@ -201,6 +201,18 @@ public class BundleUtilTest {
assertThat(BundleUtil.deepHashCode(b1)).isNotEqualTo(BundleUtil.deepHashCode(b2));
}
+ @Test
+ public void testDeepHashCode_differentKeys() {
+ Bundle[] inputs = new Bundle[2];
+ for (int i = 0; i < 2; i++) {
+ Bundle b = new Bundle();
+ b.putString("key" + i, "value");
+ inputs[i] = b;
+ }
+ assertThat(BundleUtil.deepHashCode(inputs[0]))
+ .isNotEqualTo(BundleUtil.deepHashCode(inputs[1]));
+ }
+
private static Bundle createThoroughBundle() {
Bundle toy1 = new Bundle();
toy1.putString("a", "a");
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index 64906bb27ff0..e16d44854516 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -100,7 +100,7 @@ public class ResolverListControllerTest {
final List<UsageStats> slices = new ArrayList<>();
slices.add(packageStats);
ParceledListSlice<UsageStats> stats = new ParceledListSlice<>(slices);
- when(mMockService.queryUsageStats(anyInt(), anyLong(), anyLong(), anyString()))
+ when(mMockService.queryUsageStats(anyInt(), anyLong(), anyLong(), anyString(), anyInt()))
.thenReturn(stats);
Answer<Void> answer = new Answer<Void>() {
@Override
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 143e07a1c8a6..4b9286d4de26 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -65,6 +65,7 @@ import org.junit.runners.Suite;
ScreenPowerCalculatorTest.class,
SensorPowerCalculatorTest.class,
SystemServicePowerCalculatorTest.class,
+ UserPowerCalculatorTest.class,
VideoPowerCalculatorTest.class,
com.android.internal.power.MeasuredEnergyStatsTest.class
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 2c71287fac4a..0c31c5b9818b 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -28,6 +28,7 @@ import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
+import android.os.UserBatteryConsumer;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
@@ -113,15 +114,21 @@ public class BatteryUsageStatsRule implements TestRule {
mMockClocks.uptime = uptimeUs;
}
- void apply(PowerCalculator calculator) {
+ void apply(PowerCalculator... calculators) {
+ apply(BatteryUsageStatsQuery.DEFAULT, calculators);
+ }
+
+ void apply(BatteryUsageStatsQuery query, PowerCalculator... calculators) {
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(0, 0);
SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats();
for (int i = 0; i < uidStats.size(); i++) {
builder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i));
}
- calculator.calculate(builder, mBatteryStats, mMockClocks.realtime, mMockClocks.uptime,
- BatteryUsageStatsQuery.DEFAULT, null);
+ for (PowerCalculator calculator : calculators) {
+ calculator.calculate(builder, mBatteryStats, mMockClocks.realtime, mMockClocks.uptime,
+ query);
+ }
mBatteryUsageStats = builder.build();
}
@@ -144,4 +151,13 @@ public class BatteryUsageStatsRule implements TestRule {
}
return null;
}
+
+ public UserBatteryConsumer getUserBatteryConsumer(int userId) {
+ for (UserBatteryConsumer ubc : mBatteryUsageStats.getUserBatteryConsumers()) {
+ if (ubc.getUserId() == userId) {
+ return ubc;
+ }
+ }
+ return null;
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java
new file mode 100644
index 000000000000..6fa1d3bae0f7
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Process;
+import android.os.UidBatteryConsumer;
+import android.os.UserBatteryConsumer;
+import android.os.UserHandle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UserPowerCalculatorTest {
+ public static final int USER1 = 0;
+ public static final int USER2 = 1625;
+
+ private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 272;
+ private static final int APP_UID3 = Process.FIRST_APPLICATION_UID + 314;
+
+ @Rule
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+
+ @Test
+ public void testAllUsers() {
+ prepareUidBatteryConsumers();
+
+ UserPowerCalculator calculator = new UserPowerCalculator();
+
+ mStatsRule.apply(BatteryUsageStatsQuery.DEFAULT, calculator, new FakeAudioPowerCalculator(),
+ new FakeVideoPowerCalculator());
+
+ assertThat(mStatsRule.getUserBatteryConsumer(USER1)).isNull();
+
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(3000);
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(7000);
+
+ assertThat(mStatsRule.getUserBatteryConsumer(USER2)).isNull();
+
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(5555);
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(9999);
+ }
+
+ @Test
+ public void testSpecificUser() {
+ prepareUidBatteryConsumers();
+
+ UserPowerCalculator calculator = new UserPowerCalculator();
+
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder().addUser(UserHandle.of(USER1)).build(),
+ calculator, new FakeAudioPowerCalculator(), new FakeVideoPowerCalculator());
+
+ assertThat(mStatsRule.getUserBatteryConsumer(USER1)).isNull();
+
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(3000);
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(7000);
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID2))).isNull();
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID3))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(7070);
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID3))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(11110);
+
+ UserBatteryConsumer user2 = mStatsRule.getUserBatteryConsumer(USER2);
+ assertThat(user2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO))
+ .isEqualTo(15308);
+ assertThat(user2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO))
+ .isEqualTo(24196);
+
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID1))).isNull();
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2))).isNull();
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID3))).isNull();
+ }
+
+ private void prepareUidBatteryConsumers() {
+ prepareUidBatteryConsumer(USER1, APP_UID1, 1000, 2000, 3000, 4000);
+ prepareUidBatteryConsumer(USER2, APP_UID2, 2222, 3333, 4444, 5555);
+ prepareUidBatteryConsumer(USER1, APP_UID3, 3030, 4040, 5050, 6060);
+ prepareUidBatteryConsumer(USER2, APP_UID3, 4321, 5432, 6543, 7654);
+ }
+
+ private void prepareUidBatteryConsumer(int userId, int uid, long audioDuration1Ms,
+ long audioDuration2Ms, long videoDuration1Ms, long videoDuration2Ms) {
+ BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(UserHandle.getUid(userId, uid));
+
+ // Use "audio" and "video" to fake some power consumption. Could be any other type of usage.
+ uidStats.noteAudioTurnedOnLocked(0);
+ uidStats.noteAudioTurnedOffLocked(audioDuration1Ms);
+ uidStats.noteAudioTurnedOnLocked(1000000);
+ uidStats.noteAudioTurnedOffLocked(1000000 + audioDuration2Ms);
+
+ uidStats.noteVideoTurnedOnLocked(0);
+ uidStats.noteVideoTurnedOffLocked(videoDuration1Ms);
+ uidStats.noteVideoTurnedOnLocked(2000000);
+ uidStats.noteVideoTurnedOffLocked(2000000 + videoDuration2Ms);
+ }
+
+ private static class FakeAudioPowerCalculator extends PowerCalculator {
+ @Override
+ protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ long durationMs = u.getAudioTurnedOnTimer().getTotalTimeLocked(rawRealtimeUs, 0);
+ app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO, durationMs / 1000);
+ }
+ }
+
+ private static class FakeVideoPowerCalculator extends PowerCalculator {
+ @Override
+ protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ long durationMs = u.getVideoTurnedOnTimer().getTotalTimeLocked(rawRealtimeUs, 0);
+ app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO, durationMs / 1000);
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index cb4dd9e8cacd..b70fa0e693c2 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -46,7 +46,6 @@ import java.io.File;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.stream.Stream;
@@ -1148,24 +1147,14 @@ public class HardwareRenderer {
// Default to SRGB if the display doesn't support wide color
.orElse(Dataspace.SRGB);
- float maxRefreshRate =
- (float) Arrays.stream(display.getSupportedModes())
- .mapToDouble(Mode::getRefreshRate)
- .max()
- .orElseGet(() -> {
- Log.i(LOG_TAG, "Failed to find the maximum display refresh rate");
- // Assume that the max refresh rate is 60hz if we can't find one.
- return 60.0;
- });
// Grab the physical screen dimensions from the active display mode
// Strictly speaking the screen resolution may not always be constant - it is for
// sizing the font cache for the underlying rendering thread. Since it's a
// heuristic we don't need to be always 100% correct.
Mode activeMode = display.getMode();
nInitDisplayInfo(activeMode.getPhysicalWidth(), activeMode.getPhysicalHeight(),
- display.getRefreshRate(), maxRefreshRate,
- wideColorDataspace.mNativeDataspace, display.getAppVsyncOffsetNanos(),
- display.getPresentationDeadlineNanos());
+ display.getRefreshRate(), wideColorDataspace.mNativeDataspace,
+ display.getAppVsyncOffsetNanos(), display.getPresentationDeadlineNanos());
// Defensively clear out the context
mContext = null;
@@ -1324,6 +1313,5 @@ public class HardwareRenderer {
private static native void nSetDisplayDensityDpi(int densityDpi);
private static native void nInitDisplayInfo(int width, int height, float refreshRate,
- float maxRefreshRate, int wideColorDataspace, long appVsyncOffsetNanos,
- long presentationDeadlineNanos);
+ int wideColorDataspace, long appVsyncOffsetNanos, long presentationDeadlineNanos);
}
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 372add9b7ecb..d188b6525579 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -190,7 +190,7 @@ public class KeyStoreSecurityLevel {
keyDescriptor.blob = wrappedKey;
keyDescriptor.domain = wrappedKeyDescriptor.domain;
- return handleExceptions(() -> mSecurityLevel.importWrappedKey(wrappedKeyDescriptor,
+ return handleExceptions(() -> mSecurityLevel.importWrappedKey(keyDescriptor,
wrappingKeyDescriptor, maskingKey,
args.toArray(new KeyParameter[args.size()]), authenticatorSpecs));
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 16bf5469296f..087151711138 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -434,14 +434,16 @@ public class AndroidKeyStoreProvider extends Provider {
@NonNull
public static java.security.KeyStore getKeyStoreForUid(int uid)
throws KeyStoreException, NoSuchProviderException {
- String providerName = PROVIDER_NAME;
+ final java.security.KeyStore.LoadStoreParameter loadParameter;
if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
- providerName = "AndroidKeyStoreLegacy";
+ loadParameter = new android.security.keystore2.AndroidKeyStoreLoadStoreParameter(
+ KeyProperties.legacyUidToNamespace(uid));
+ } else {
+ loadParameter = new AndroidKeyStoreLoadStoreParameter(uid);
}
- java.security.KeyStore result =
- java.security.KeyStore.getInstance(providerName);
+ java.security.KeyStore result = java.security.KeyStore.getInstance(PROVIDER_NAME);
try {
- result.load(new AndroidKeyStoreLoadStoreParameter(uid));
+ result.load(loadParameter);
} catch (NoSuchAlgorithmException | CertificateException | IOException e) {
throw new KeyStoreException(
"Failed to load AndroidKeyStore KeyStore for UID " + uid, e);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
index 8475ad9fd57b..0f777495a3fe 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -164,6 +164,9 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
List<KeyParameter> parameters = new ArrayList<>();
parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_SIGN
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC
));
parameters.add(KeyStore2ParameterUtils.makeEnum(
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
index 32650aeda1b1..5619585d9c3c 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
@@ -21,7 +21,6 @@ import android.security.KeyStoreSecurityLevel;
import android.system.keystore2.Authorization;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
-import android.util.Log;
import java.security.Key;
@@ -127,15 +126,6 @@ public class AndroidKeyStoreKey implements Key {
return false;
}
- // If the key ids are equal and the class matches all the other fields cannot differ
- // unless we have a bug.
- if (!mAlgorithm.equals(other.mAlgorithm)
- || !mAuthorizations.equals(other.mAuthorizations)
- || !mDescriptor.equals(other.mDescriptor)) {
- Log.e("AndroidKeyStoreKey", "Bug: key ids are identical, but key metadata"
- + "differs.");
- return false;
- }
return true;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 8c8acc418a0e..39607aeb3852 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -866,7 +866,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
try {
response = mKeyStore.getKeyEntry(wrappingkey);
} catch (android.security.KeyStoreException e) {
- throw new KeyStoreException("Failed to load wrapping key.", e);
+ throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
+ + e.getErrorCode(), e);
}
KeyDescriptor wrappedKey = makeKeyDescriptor(alias);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 40fdb97b0094..0ee1f0642352 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -398,6 +398,14 @@ public class Bubble implements BubbleViewProvider {
}
}
+ @Override
+ public void setExpandedContentAlpha(float alpha) {
+ if (mExpandedView != null) {
+ mExpandedView.setAlpha(alpha);
+ mExpandedView.setTaskViewAlpha(alpha);
+ }
+ }
+
/**
* Set visibility of bubble in the expanded state.
*
@@ -407,7 +415,7 @@ public class Bubble implements BubbleViewProvider {
* and setting {@code false} actually means rendering the expanded view in transparent.
*/
@Override
- public void setContentVisibility(boolean visibility) {
+ public void setTaskViewVisibility(boolean visibility) {
if (mExpandedView != null) {
mExpandedView.setContentVisibility(visibility);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 29458ef70e2d..21004280c952 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -85,6 +85,19 @@ public class BubbleExpandedView extends LinearLayout {
private boolean mImeVisible;
private boolean mNeedsNewHeight;
+ /**
+ * Whether we want the TaskView's content to be visible (alpha = 1f). If
+ * {@link #mIsAlphaAnimating} is true, this may not reflect the TaskView's actual alpha value
+ * until the animation ends.
+ */
+ private boolean mIsContentVisible = false;
+
+ /**
+ * Whether we're animating the TaskView's alpha value. If so, we will hold off on applying alpha
+ * changes from {@link #setContentVisibility} until the animation ends.
+ */
+ private boolean mIsAlphaAnimating = false;
+
private int mMinHeight;
private int mOverflowHeight;
private int mSettingsIconHeight;
@@ -465,6 +478,29 @@ public class BubbleExpandedView extends LinearLayout {
}
/**
+ * Whether we are currently animating the TaskView's alpha value. If this is set to true, calls
+ * to {@link #setContentVisibility} will not be applied until this is set to false again.
+ */
+ void setAlphaAnimating(boolean animating) {
+ mIsAlphaAnimating = animating;
+
+ // If we're done animating, apply the correct
+ if (!animating) {
+ setContentVisibility(mIsContentVisible);
+ }
+ }
+
+ /**
+ * Sets the alpha of the underlying TaskView, since changing the expanded view's alpha does not
+ * affect the TaskView since it uses a Surface.
+ */
+ void setTaskViewAlpha(float alpha) {
+ if (mTaskView != null) {
+ mTaskView.setAlpha(alpha);
+ }
+ }
+
+ /**
* Set visibility of contents in the expanded state.
*
* @param visibility {@code true} if the contents should be visible on the screen.
@@ -477,16 +513,19 @@ public class BubbleExpandedView extends LinearLayout {
Log.d(TAG, "setContentVisibility: visibility=" + visibility
+ " bubble=" + getBubbleKey());
}
+ mIsContentVisible = visibility;
+
final float alpha = visibility ? 1f : 0f;
mPointerView.setAlpha(alpha);
- if (mTaskView != null) {
+ if (mTaskView != null && !mIsAlphaAnimating) {
mTaskView.setAlpha(alpha);
}
}
+
@Nullable
- View getTaskView() {
+ TaskView getTaskView() {
return mTaskView;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 16cd3cf3686c..51d63cff385a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -167,8 +167,12 @@ class BubbleOverflow(
return dotPath
}
- override fun setContentVisibility(visible: Boolean) {
- expandedView?.setContentVisibility(visible)
+ override fun setExpandedContentAlpha(alpha: Float) {
+ expandedView?.alpha = alpha
+ }
+
+ override fun setTaskViewVisibility(visible: Boolean) {
+ // Overflow does not have a TaskView.
}
override fun getIconView(): BadgedImageView? {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index d54be0e62527..a3edc20e242a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -43,7 +43,6 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Bundle;
-import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import android.view.Choreographer;
@@ -123,6 +122,10 @@ public class BubbleStackView extends FrameLayout
@VisibleForTesting
static final int FLYOUT_HIDE_AFTER = 5000;
+ private static final float EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT = 0.1f;
+
+ private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
+
/**
* How long to wait to animate the stack temporarily invisible after a drag/flyout hide
* animation ends, if we are in fact temporarily invisible.
@@ -142,7 +145,7 @@ public class BubbleStackView extends FrameLayout
private final PhysicsAnimator.SpringConfig mTranslateSpringConfig =
new PhysicsAnimator.SpringConfig(
- SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY);
+ SpringForce.STIFFNESS_VERY_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY);
/**
* Handler to use for all delayed animations - this way, we can easily cancel them before
@@ -211,6 +214,9 @@ public class BubbleStackView extends FrameLayout
/** Container for the animating-out SurfaceView. */
private FrameLayout mAnimatingOutSurfaceContainer;
+ /** Animator for animating the alpha value of the animating out SurfaceView. */
+ private final ValueAnimator mAnimatingOutSurfaceAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);
+
/**
* Buffer containing a screenshot of the animating-out bubble. This is drawn into the
* SurfaceView during animations.
@@ -261,6 +267,12 @@ public class BubbleStackView extends FrameLayout
/** Whether we're in the middle of dragging the stack around by touch. */
private boolean mIsDraggingStack = false;
+ /** Whether the expanded view has been hidden, because we are dragging out a bubble. */
+ private boolean mExpandedViewHidden = false;
+
+ /** Animator for animating the expanded view's alpha (including the TaskView inside it). */
+ private final ValueAnimator mExpandedViewAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);
+
/**
* The pointer index of the ACTION_DOWN event we received prior to an ACTION_UP. We'll ignore
* touches from other pointer indices.
@@ -614,6 +626,12 @@ public class BubbleStackView extends FrameLayout
// Show the dismiss target, if we haven't already.
mDismissView.show();
+ if (mIsExpanded && mExpandedBubble != null && v.equals(mExpandedBubble.getIconView())) {
+ // Hide the expanded view if we're dragging out the expanded bubble, and we haven't
+ // already hidden it.
+ hideExpandedViewIfNeeded();
+ }
+
// First, see if the magnetized object consumes the event - if so, we shouldn't move the
// bubble since it's stuck to the target.
if (!passEventToMagnetizedObject(ev)) {
@@ -645,6 +663,9 @@ public class BubbleStackView extends FrameLayout
if (!passEventToMagnetizedObject(ev)) {
if (mBubbleData.isExpanded()) {
mExpandedAnimationController.snapBubbleBack(v, velX, velY);
+
+ // Re-show the expanded view if we hid it.
+ showExpandedViewIfNeeded();
} else {
// Fling the stack to the edge, and save whether or not it's going to end up on
// the left side of the screen.
@@ -937,6 +958,46 @@ public class BubbleStackView extends FrameLayout
animate()
.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED)
.setDuration(FADE_IN_DURATION);
+
+ mExpandedViewAlphaAnimator.setDuration(EXPANDED_VIEW_ALPHA_ANIMATION_DURATION);
+ mExpandedViewAlphaAnimator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
+ mExpandedViewAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ // We need to be Z ordered on top in order for alpha animations to work.
+ mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(true);
+ mExpandedBubble.getExpandedView().setAlphaAnimating(true);
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
+ mExpandedBubble.getExpandedView().setAlphaAnimating(false);
+ }
+ }
+ });
+ mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> {
+ if (mExpandedBubble != null) {
+ mExpandedBubble.setExpandedContentAlpha((float) valueAnimator.getAnimatedValue());
+ }
+ });
+
+ mAnimatingOutSurfaceAlphaAnimator.setDuration(EXPANDED_VIEW_ALPHA_ANIMATION_DURATION);
+ mAnimatingOutSurfaceAlphaAnimator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
+ mAnimatingOutSurfaceAlphaAnimator.addUpdateListener(valueAnimator -> {
+ if (!mExpandedViewHidden) {
+ mAnimatingOutSurfaceView.setAlpha((float) valueAnimator.getAnimatedValue());
+ }
+ });
+ mAnimatingOutSurfaceAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ releaseAnimatingOutBubbleBuffer();
+ }
+ });
}
/**
@@ -1539,7 +1600,8 @@ public class BubbleStackView extends FrameLayout
// If we're expanded, screenshot the currently expanded bubble (before expanding the newly
// selected bubble) so we can animate it out.
- if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null
+ && !mExpandedViewHidden) {
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
// Before screenshotting, have the real ActivityView show on top of other surfaces
// so that the screenshot doesn't flicker on top of it.
@@ -1575,7 +1637,7 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainer.setAlpha(0.0f);
mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
if (previouslySelected != null) {
- previouslySelected.setContentVisibility(false);
+ previouslySelected.setTaskViewVisibility(false);
}
updateExpandedBubble();
@@ -1656,6 +1718,58 @@ public class BubbleStackView extends FrameLayout
requestUpdate();
}
+ /** Animate the expanded view hidden. This is done while we're dragging out a bubble. */
+ private void hideExpandedViewIfNeeded() {
+ if (mExpandedViewHidden
+ || mExpandedBubble == null
+ || mExpandedBubble.getExpandedView() == null) {
+ return;
+ }
+
+ mExpandedViewHidden = true;
+
+ // Scale down.
+ PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
+ .spring(AnimatableScaleMatrix.SCALE_X,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
+ mScaleOutSpringConfig)
+ .spring(AnimatableScaleMatrix.SCALE_Y,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
+ mScaleOutSpringConfig)
+ .addUpdateListener((target, values) ->
+ mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix))
+ .start();
+
+ // Animate alpha from 1f to 0f.
+ mExpandedViewAlphaAnimator.reverse();
+ }
+
+ /**
+ * Animate the expanded view visible again. This is done when we're done dragging out a bubble.
+ */
+ private void showExpandedViewIfNeeded() {
+ if (!mExpandedViewHidden) {
+ return;
+ }
+
+ mExpandedViewHidden = false;
+
+ PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
+ .spring(AnimatableScaleMatrix.SCALE_X,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+ mScaleOutSpringConfig)
+ .spring(AnimatableScaleMatrix.SCALE_Y,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+ mScaleOutSpringConfig)
+ .addUpdateListener((target, values) ->
+ mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix))
+ .start();
+
+ mExpandedViewAlphaAnimator.start();
+ }
+
private void animateExpansion() {
cancelDelayedExpandCollapseSwitchAnimations();
final boolean showVertically = mPositioner.showBubblesVertically();
@@ -1714,7 +1828,7 @@ public class BubbleStackView extends FrameLayout
// Should not happen since we lay out before expanding, but just in case...
if (getWidth() > 0) {
startDelay = (long)
- (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION
+ (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 1.2f
+ (distanceAnimated / getWidth()) * 30);
}
@@ -1728,20 +1842,29 @@ public class BubbleStackView extends FrameLayout
pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
}
mExpandedViewContainerMatrix.setScale(
- 0f, 0f,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
pivotX, pivotY);
} else {
mExpandedViewContainerMatrix.setScale(
- 0f, 0f,
- bubbleWillBeAt + mBubbleSize / 2f, getExpandedViewY());
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+ bubbleWillBeAt + mBubbleSize / 2f,
+ getExpandedViewY());
}
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
- if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
+ if (mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.setExpandedContentAlpha(0f);
+
+ // We'll be starting the alpha animation after a slight delay, so set this flag early
+ // here.
+ mExpandedBubble.getExpandedView().setAlphaAnimating(true);
}
mDelayedAnimationExecutor.executeDelayed(() -> {
+ mExpandedViewAlphaAnimator.start();
+
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
.spring(AnimatableScaleMatrix.SCALE_X,
@@ -1796,22 +1919,15 @@ public class BubbleStackView extends FrameLayout
// since we're about to animate collapsed.
mExpandedAnimationController.notifyPreparingToCollapse();
- final long startDelay =
- (long) (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 0.6f);
- mDelayedAnimationExecutor.executeDelayed(() -> {
- mExpandedAnimationController.collapseBackToStack(
- mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
- /* collapseTo */,
- () -> mBubbleContainer.setActiveController(mStackAnimationController));
- }, startDelay);
+ mExpandedAnimationController.collapseBackToStack(
+ mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
+ /* collapseTo */,
+ () -> mBubbleContainer.setActiveController(mStackAnimationController));
if (mTaskbarScrim.getVisibility() == VISIBLE) {
mTaskbarScrim.animate().alpha(0f).start();
}
- // We want to visually collapse into this bubble during the animation.
- final View expandingFromBubble = mExpandedBubble.getIconView();
-
int index;
if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
index = mBubbleData.getBubbles().size();
@@ -1840,31 +1956,25 @@ public class BubbleStackView extends FrameLayout
getExpandedViewY());
}
+ mExpandedViewAlphaAnimator.reverse();
+
+ // When the animation completes, we should no longer be showing the content.
+ if (mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.getExpandedView().setContentVisibility(false);
+ }
+
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
- .spring(AnimatableScaleMatrix.SCALE_X, 0f, mScaleOutSpringConfig)
- .spring(AnimatableScaleMatrix.SCALE_Y, 0f, mScaleOutSpringConfig)
+ .spring(AnimatableScaleMatrix.SCALE_X,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
+ mScaleOutSpringConfig)
+ .spring(AnimatableScaleMatrix.SCALE_Y,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
+ mScaleOutSpringConfig)
.addUpdateListener((target, values) -> {
- if (expandingFromBubble != null) {
- // Follow the bubble as it translates!
- if (showVertically) {
- mExpandedViewContainerMatrix.postTranslate(
- 0f, expandingFromBubble.getTranslationY()
- - expandingFromBubbleAt);
- } else {
- mExpandedViewContainerMatrix.postTranslate(
- expandingFromBubble.getTranslationX()
- - expandingFromBubbleAt, 0f);
- }
- }
-
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
-
- // Hide early so we don't have a tiny little expanded view still visible at the
- // end of the scale animation.
- if (mExpandedViewContainerMatrix.getScaleX() < 0.05f) {
- mExpandedViewContainer.setVisibility(View.INVISIBLE);
- }
})
.withEndActions(() -> {
final BubbleViewProvider previouslySelected = mExpandedBubble;
@@ -1882,7 +1992,7 @@ public class BubbleStackView extends FrameLayout
updateBadgesAndZOrder(true /* setBadgeForCollapsedStack */);
afterExpandedViewAnimation();
if (previouslySelected != null) {
- previouslySelected.setContentVisibility(false);
+ previouslySelected.setTaskViewVisibility(false);
}
if (mPositioner.showingInTaskbar()) {
@@ -1903,22 +2013,21 @@ public class BubbleStackView extends FrameLayout
// The surface contains a screenshot of the animating out bubble, so we just need to animate
// it out (and then release the GraphicBuffer).
PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
- PhysicsAnimator animator = PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
- .spring(DynamicAnimation.SCALE_X, 0f, mScaleOutSpringConfig)
- .spring(DynamicAnimation.SCALE_Y, 0f, mScaleOutSpringConfig)
- .withEndActions(this::releaseAnimatingOutBubbleBuffer);
+
+ mAnimatingOutSurfaceAlphaAnimator.reverse();
+ mExpandedViewAlphaAnimator.start();
if (mPositioner.showBubblesVertically()) {
float translationX = mStackAnimationController.isStackOnLeftSide()
? mAnimatingOutSurfaceContainer.getTranslationX() + mBubbleSize * 2
: mAnimatingOutSurfaceContainer.getTranslationX();
- animator.spring(DynamicAnimation.TRANSLATION_X,
- translationX,
- mTranslateSpringConfig)
+ PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
+ .spring(DynamicAnimation.TRANSLATION_X, translationX, mTranslateSpringConfig)
.start();
} else {
- animator.spring(DynamicAnimation.TRANSLATION_Y,
- mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2,
+ PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
+ .spring(DynamicAnimation.TRANSLATION_Y,
+ mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize,
mTranslateSpringConfig)
.start();
}
@@ -1947,7 +2056,8 @@ public class BubbleStackView extends FrameLayout
pivotX, pivotY);
} else {
mExpandedViewContainerMatrix.setScale(
- 0f, 0f,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
expandingFromBubbleDestination + mBubbleSize / 2f,
getExpandedViewY());
}
@@ -1972,10 +2082,7 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
})
.withEndActions(() -> {
- if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
- }
-
+ mExpandedViewHidden = false;
mIsBubbleSwitchAnimating = false;
})
.start();
@@ -2489,6 +2596,7 @@ public class BubbleStackView extends FrameLayout
&& mExpandedBubble.getExpandedView() != null) {
BubbleExpandedView bev = mExpandedBubble.getExpandedView();
bev.setContentVisibility(false);
+ bev.setAlphaAnimating(!mIsExpansionAnimating);
mExpandedViewContainerMatrix.setScaleX(0f);
mExpandedViewContainerMatrix.setScaleY(0f);
mExpandedViewContainerMatrix.setTranslate(0f, 0f);
@@ -2586,7 +2694,14 @@ public class BubbleStackView extends FrameLayout
mAnimatingOutBubbleBuffer.getHardwareBuffer(),
mAnimatingOutBubbleBuffer.getColorSpace());
- mSurfaceSynchronizer.syncSurfaceAndRun(() -> post(() -> onComplete.accept(true)));
+ mAnimatingOutSurfaceView.setAlpha(1f);
+ mExpandedViewContainer.setVisibility(View.GONE);
+
+ mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
+ post(() -> {
+ onComplete.accept(true);
+ });
+ });
});
}
@@ -2618,7 +2733,9 @@ public class BubbleStackView extends FrameLayout
}
}
mExpandedViewContainer.setPadding(leftPadding, 0, rightPadding, 0);
- mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
+ if (mIsExpansionAnimating) {
+ mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
+ }
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
mExpandedViewContainer.setTranslationY(getExpandedViewY());
mExpandedViewContainer.setTranslationX(0f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
index ec900be13658..da4259c42558 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
@@ -29,7 +29,16 @@ import androidx.annotation.Nullable;
public interface BubbleViewProvider {
@Nullable BubbleExpandedView getExpandedView();
- void setContentVisibility(boolean visible);
+ /**
+ * Sets the alpha of the expanded view content. This will be applied to both the expanded view
+ * container itself (the manage button, etc.) as well as the TaskView within it.
+ */
+ void setExpandedContentAlpha(float alpha);
+
+ /**
+ * Sets whether the contents of the bubble's TaskView should be visible.
+ */
+ void setTaskViewVisibility(boolean visible);
@Nullable View getIconView();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 177646b22ea3..7ca569349633 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -106,6 +106,8 @@ public interface SplitScreen extends DragAndDropPolicy.Starter {
/** Removes the split-screen stages. */
void exitSplitScreen();
+ /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */
+ void exitSplitScreenOnHide(boolean exitSplitScreenOnHide);
/** Gets the stage bounds. */
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index bbad36dcc046..b0167afa2e4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -126,6 +126,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter {
mStageCoordinator.exitSplitScreen();
}
+ public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
+ }
+
public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
}
@@ -292,6 +296,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter {
}
@Override
+ public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.exitSplitScreenOnHide(exitSplitScreenOnHide);
+ });
+ }
+
+ @Override
public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
try {
mMainExecutor.executeBlocking(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 176852b148fa..2d4b77e0c630 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -79,6 +79,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
private DisplayAreaInfo mDisplayAreaInfo;
private final Context mContext;
private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
+ private boolean mExitSplitScreenOnHide = true;
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer) {
@@ -113,7 +114,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitScreen.StagePosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
- mSideStagePosition = sideStagePosition;
+ setSideStagePosition(sideStagePosition);
mMainStage.activate(getMainStageBounds(), wct);
mSideStage.addTask(task, getSideStageBounds(), wct);
mTaskOrganizer.applyTransaction(wct);
@@ -144,10 +145,14 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
}
void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
+ if (mSideStagePosition == sideStagePosition) return;
+
mSideStagePosition = sideStagePosition;
if (mSideStageListener.mVisible) {
onStageVisibilityChanged(mSideStageListener);
}
+
+ sendOnStagePositionChanged();
}
void setSideStageVisibility(boolean visible) {
@@ -162,6 +167,10 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
exitSplitScreen(null /* childrenToTop */);
}
+ void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ mExitSplitScreenOnHide = exitSplitScreenOnHide;
+ }
+
private void exitSplitScreen(StageTaskListener childrenToTop) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
@@ -202,6 +211,14 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
mListeners.remove(listener);
}
+ private void sendOnStagePositionChanged() {
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ final SplitScreen.SplitScreenListener l = mListeners.get(i);
+ l.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
+ l.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
+ }
+ }
+
private void onStageChildTaskStatusChanged(
StageListenerImpl stageListener, int taskId, boolean present) {
@@ -251,7 +268,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
}
}
- if (!mainStageVisible && !sideStageVisible) {
+ if (mExitSplitScreenOnHide && !mainStageVisible && !sideStageVisible) {
// Exit split-screen if both stage are not visible.
// TODO: This is only a temporary request from UX and is likely to be removed soon...
exitSplitScreen();
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 27be62269959..d5fee3f667a9 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -35,7 +35,6 @@ class DeviceInfo {
public:
static DeviceInfo* get();
- static float getMaxRefreshRate() { return get()->mMaxRefreshRate; }
static int32_t getWidth() { return get()->mWidth; }
static int32_t getHeight() { return get()->mHeight; }
// Gets the density in density-independent pixels
@@ -45,7 +44,6 @@ public:
static int64_t getAppOffset() { return get()->mAppVsyncOffsetNanos; }
// Sets the density in density-independent pixels
static void setDensity(float density) { sDensity.store(density); }
- static void setMaxRefreshRate(float refreshRate) { get()->mMaxRefreshRate = refreshRate; }
static void setWidth(int32_t width) { get()->mWidth = width; }
static void setHeight(int32_t height) { get()->mHeight = height; }
static void setRefreshRate(float refreshRate) {
@@ -91,7 +89,6 @@ private:
SkColorType mWideColorType = SkColorType::kN32_SkColorType;
int mDisplaysSize = 0;
int mPhysicalDisplayIndex = -1;
- float mMaxRefreshRate = 60.0;
int32_t mWidth = 1080;
int32_t mHeight = 1920;
int64_t mVsyncPeriod = 16666666;
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index e798f2a2bc69..971a53a8b2dc 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -79,7 +79,6 @@ bool Properties::debuggingEnabled = false;
bool Properties::isolatedProcess = false;
int Properties::contextPriority = 0;
-int Properties::defaultRenderAhead = -1;
float Properties::defaultSdrWhitePoint = 200.f;
bool Properties::load() {
@@ -129,10 +128,6 @@ bool Properties::load() {
runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false);
- defaultRenderAhead = std::max(
- -1,
- std::min(2, base::GetIntProperty(PROPERTY_RENDERAHEAD, render_ahead().value_or(-1))));
-
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 1639143ef87c..dcb79babad24 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -162,8 +162,6 @@ enum DebugLevel {
*/
#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu"
-#define PROPERTY_RENDERAHEAD "debug.hwui.render_ahead"
-
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -247,8 +245,6 @@ public:
static int contextPriority;
- static int defaultRenderAhead;
-
static float defaultSdrWhitePoint;
private:
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index a146b64e29cc..4966bfa1c1e9 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -603,14 +603,12 @@ static void android_view_ThreadedRenderer_setDisplayDensityDpi(JNIEnv*, jclass,
static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint physicalWidth,
jint physicalHeight, jfloat refreshRate,
- jfloat maxRefreshRate,
jint wideColorDataspace,
jlong appVsyncOffsetNanos,
jlong presentationDeadlineNanos) {
DeviceInfo::setWidth(physicalWidth);
DeviceInfo::setHeight(physicalHeight);
DeviceInfo::setRefreshRate(refreshRate);
- DeviceInfo::setMaxRefreshRate(maxRefreshRate);
DeviceInfo::setWideColorDataspace(static_cast<ADataSpace>(wideColorDataspace));
DeviceInfo::setAppVsyncOffsetNanos(appVsyncOffsetNanos);
DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos);
@@ -735,7 +733,7 @@ static const JNINativeMethod gMethods[] = {
{"nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark},
{"nSetDisplayDensityDpi", "(I)V",
(void*)android_view_ThreadedRenderer_setDisplayDensityDpi},
- {"nInitDisplayInfo", "(IIFFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
+ {"nInitDisplayInfo", "(IIFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
{"preload", "()V", (void*)android_view_ThreadedRenderer_preload},
};
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 3392dac02493..c8471a9b8feb 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -219,7 +219,7 @@ static jlong Font_getBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
}
// Critical Native
-static jlong Font_getReleaseNativeFontFunc() {
+static jlong Font_getReleaseNativeFontFunc(CRITICAL_JNI_PARAMS) {
return reinterpret_cast<jlong>(releaseFont);
}
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index 80964794efb2..b68213549938 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -101,13 +101,13 @@ static jint FontFamily_getVariant(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) {
}
// CriticalNative
-static jint FontFamily_getFontSize(jlong familyPtr) {
+static jint FontFamily_getFontSize(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) {
FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
return family->family->getNumFonts();
}
// CriticalNative
-static jlong FontFamily_getFont(jlong familyPtr, jint index) {
+static jlong FontFamily_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr, jint index) {
FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
std::shared_ptr<minikin::Font> font = family->family->getFontRef(index);
return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 37a6ee71c4a6..65afcc3a2558 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -108,7 +108,6 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode*
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
mProfiler.setDensity(DeviceInfo::getDensity());
- setRenderAheadDepth(Properties::defaultRenderAhead);
}
CanvasContext::~CanvasContext() {
@@ -157,24 +156,17 @@ static void setBufferCount(ANativeWindow* window) {
void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
ATRACE_CALL();
- if (mFixedRenderAhead) {
- mRenderAheadCapacity = mRenderAheadDepth;
- } else {
- if (DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
- mRenderAheadCapacity = 1;
- } else {
- mRenderAheadCapacity = 0;
- }
- }
-
if (window) {
+ int extraBuffers = 0;
+ native_window_get_extra_buffer_count(window, &extraBuffers);
+
mNativeSurface = std::make_unique<ReliableSurface>(window);
mNativeSurface->init();
if (enableTimeout) {
// TODO: Fix error handling & re-shorten timeout
ANativeWindow_setDequeueTimeout(window, 4000_ms);
}
- mNativeSurface->setExtraBufferCount(mRenderAheadCapacity);
+ mNativeSurface->setExtraBufferCount(extraBuffers);
} else {
mNativeSurface = nullptr;
}
@@ -441,24 +433,6 @@ void CanvasContext::notifyFramePending() {
mRenderThread.pushBackFrameCallback(this);
}
-void CanvasContext::setPresentTime() {
- int64_t presentTime = NATIVE_WINDOW_TIMESTAMP_AUTO;
- int renderAhead = 0;
- const auto frameIntervalNanos = mRenderThread.timeLord().frameIntervalNanos();
- if (mFixedRenderAhead) {
- renderAhead = std::min(mRenderAheadDepth, mRenderAheadCapacity);
- } else if (frameIntervalNanos < 15_ms) {
- renderAhead = std::min(1, static_cast<int>(mRenderAheadCapacity));
- }
-
- if (renderAhead) {
- presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
- (frameIntervalNanos * (renderAhead + 1)) - DeviceInfo::get()->getAppOffset() +
- (frameIntervalNanos / 2);
- }
- native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime);
-}
-
void CanvasContext::draw() {
SkRect dirty;
mDamageAccumulator.finish(&dirty);
@@ -478,8 +452,6 @@ void CanvasContext::draw() {
mCurrentFrameInfo->markIssueDrawCommandsStart();
Frame frame = mRenderPipeline->getFrame();
- setPresentTime();
-
SkRect windowDirty = computeDirtyRect(frame, &dirty);
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
@@ -765,19 +737,6 @@ bool CanvasContext::surfaceRequiresRedraw() {
return width != mLastFrameWidth || height != mLastFrameHeight;
}
-void CanvasContext::setRenderAheadDepth(int renderAhead) {
- if (renderAhead > 2 || renderAhead < -1 || mNativeSurface) {
- return;
- }
- if (renderAhead == -1) {
- mFixedRenderAhead = false;
- mRenderAheadDepth = 0;
- } else {
- mFixedRenderAhead = true;
- mRenderAheadDepth = static_cast<uint32_t>(renderAhead);
- }
-}
-
SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) {
// can't rely on prior content of window if viewport size changes
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index cc4eb3285bbe..b31883b9ae94 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -193,9 +193,6 @@ public:
return mUseForceDark;
}
- // Must be called before setSurface
- void setRenderAheadDepth(int renderAhead);
-
SkISize getNextFrameSize() const;
private:
@@ -211,7 +208,6 @@ private:
bool isSwapChainStuffed();
bool surfaceRequiresRedraw();
- void setPresentTime();
void setupPipelineSurface();
SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
@@ -232,9 +228,6 @@ private:
// painted onto its surface.
bool mIsDirty = false;
SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default;
- bool mFixedRenderAhead = false;
- uint32_t mRenderAheadDepth = 0;
- uint32_t mRenderAheadCapacity = 0;
struct SwapHistory {
SkRect damage;
nsecs_t vsyncTime;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b51f6dcfc66f..0ade8dde12eb 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -295,11 +295,6 @@ void RenderProxy::setForceDark(bool enable) {
mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
}
-void RenderProxy::setRenderAheadDepth(int renderAhead) {
- mRenderThread.queue().post(
- [context = mContext, renderAhead] { context->setRenderAheadDepth(renderAhead); });
-}
-
int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom,
SkBitmap* bitmap) {
auto& thread = RenderThread::getInstance();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 33dabc9895b1..a4adb16a930e 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -123,23 +123,6 @@ public:
void removeFrameMetricsObserver(FrameMetricsObserver* observer);
void setForceDark(bool enable);
- /**
- * Sets a render-ahead depth on the backing renderer. This will increase latency by
- * <swapInterval> * renderAhead and increase memory usage by (3 + renderAhead) * <resolution>.
- * In return the renderer will be less susceptible to jitter, resulting in a smoother animation.
- *
- * Not recommended to use in response to anything touch driven, but for canned animations
- * where latency is not a concern careful use may be beneficial.
- *
- * Note that when increasing this there will be a frame gap of N frames where N is
- * renderAhead - <current renderAhead>. When decreasing this if there are any pending
- * frames they will retain their prior renderAhead value, so it will take a few frames
- * for the decrease to flush through.
- *
- * @param renderAhead How far to render ahead, must be in the range [0..2]
- */
- void setRenderAheadDepth(int renderAhead);
-
static int copySurfaceInto(ANativeWindow* window, int left, int top, int right,
int bottom, SkBitmap* bitmap);
static void prepareToDraw(Bitmap& bitmap);
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index 74a039b3d090..91022cfe734b 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -38,7 +38,6 @@ public:
int count = 0;
int reportFrametimeWeight = 0;
bool renderOffscreen = true;
- int renderAhead = 0;
};
template <class T>
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index eda5d2266dcf..8c7d2612f39b 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -153,11 +153,6 @@ void run(const TestScene::Info& info, const TestScene::Options& opts,
proxy->resetProfileInfo();
proxy->fence();
- if (opts.renderAhead) {
- usleep(33000);
- }
- proxy->setRenderAheadDepth(opts.renderAhead);
-
ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight);
nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC);
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 88d33c315a09..174a14080eff 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -69,7 +69,6 @@ OPTIONS:
are offscreen rendered
--benchmark_format Set output format. Possible values are tabular, json, csv
--renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk
- --render-ahead=NUM Sets how far to render-ahead. Must be 0 (default), 1, or 2.
)");
}
@@ -171,7 +170,6 @@ enum {
Onscreen,
Offscreen,
Renderer,
- RenderAhead,
};
}
@@ -187,7 +185,6 @@ static const struct option LONG_OPTIONS[] = {
{"onscreen", no_argument, nullptr, LongOpts::Onscreen},
{"offscreen", no_argument, nullptr, LongOpts::Offscreen},
{"renderer", required_argument, nullptr, LongOpts::Renderer},
- {"render-ahead", required_argument, nullptr, LongOpts::RenderAhead},
{0, 0, 0, 0}};
static const char* SHORT_OPTIONS = "c:r:h";
@@ -286,16 +283,6 @@ void parseOptions(int argc, char* argv[]) {
gOpts.renderOffscreen = true;
break;
- case LongOpts::RenderAhead:
- if (!optarg) {
- error = true;
- }
- gOpts.renderAhead = atoi(optarg);
- if (gOpts.renderAhead < 0 || gOpts.renderAhead > 2) {
- error = true;
- }
- break;
-
case 'h':
printHelp();
exit(EXIT_SUCCESS);
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index d40de76431e4..205c1f4b4057 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -354,7 +354,7 @@ public final class AudioDeviceInfo {
/**
* @hide
- * @return the internal device tyoe
+ * @return the internal device type
*/
public int getInternalType() {
return mPort.type();
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 84dacfdaa2e4..d10ff402aa3a 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -251,5 +251,5 @@
<integer name="def_accessibility_magnification_capabilities">3</integer>
<!-- Default for Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW -->
- <bool name="def_enable_non_resizable_multi_window">false</bool>
+ <bool name="def_enable_non_resizable_multi_window">true</bool>
</resources>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 35a2195accab..7a9e7c7e73f6 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -76,7 +76,6 @@
android:textSize="100dp"
android:includeFontPadding="false"
android:fontFamily="@font/clock"
- android:lineSpacingMultiplier=".7"
android:typeface="monospace"
android:elegantTextHeight="false"
dozeWeight="200"
@@ -97,7 +96,6 @@
android:gravity="center_horizontal"
android:textSize="@dimen/large_clock_text_size"
android:includeFontPadding="false"
- android:lineSpacingMultiplier=".7"
android:fontFamily="@font/clock"
android:typeface="monospace"
android:elegantTextHeight="false"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5bb46a83f259..12b8ccfd09d6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -163,10 +163,14 @@
<!-- heads up elevation that is added if the view is pinned -->
<dimen name="heads_up_pinned_elevation">16dp</dimen>
- <!-- Height of a messaging notifications with actions at least. Not that this is an upper bound
+ <!-- Height of a messaging notifications with actions at least. Note that this is an upper bound
and the notification won't use this much, but is measured with wrap_content -->
<dimen name="notification_messaging_actions_min_height">196dp</dimen>
+ <!-- Height of a call notification. Note that this is an upper bound
+ and the notification won't use this much, but is measured with wrap_content -->
+ <dimen name="call_notification_full_height">172dp</dimen>
+
<!-- a threshold in dp per second that is considered fast scrolling -->
<dimen name="scroll_fast_threshold">1500dp</dimen>
@@ -688,6 +692,11 @@
<!-- The amount to shift the clocks during a small/large transition -->
<dimen name="keyguard_clock_switch_y_shift">10dp</dimen>
+ <!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
+ <item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
+ <!-- Burmese line spacing multiplier between hours and minutes of the keyguard clock -->
+ <item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item>
+
<item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
<!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index e38cf237d36b..5c943f63a9a1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -245,9 +245,11 @@ interface ISystemUiProxy {
void setSideStageVisibility(in boolean visible) = 36;
/** Removes the split-screen stages. */
void exitSplitScreen() = 37;
- void startTask(in int taskId, in int stage, in int position, in Bundle options) = 38;
+ /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */
+ void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 38;
+ void startTask(in int taskId, in int stage, in int position, in Bundle options) = 39;
void startShortcut(in String packageName, in String shortcutId, in int stage, in int position,
- in Bundle options, in UserHandle user) = 39;
+ in Bundle options, in UserHandle user) = 40;
void startIntent(
- in PendingIntent intent, in int stage, in int position, in Bundle options) = 40;
+ in PendingIntent intent, in int stage, in int position, in Bundle options) = 41;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 59e81cf96bb2..0a117c17a354 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -16,37 +16,72 @@
package com.android.keyguard;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.Color;
import android.graphics.Paint;
+import android.icu.text.NumberFormat;
import android.util.MathUtils;
import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.util.ViewController;
+import java.util.Locale;
+import java.util.Objects;
import java.util.TimeZone;
/**
- * Controls the color of a GradientTextClock.
+ * Controller for an AnimatableClockView.
*/
public class AnimatableClockController extends ViewController<AnimatableClockView> {
+ private static final int FORMAT_NUMBER = 1234567890;
private final StatusBarStateController mStatusBarStateController;
- private final int[] mDozingColors = new int[] {Color.WHITE, Color.WHITE};
- private int[] mLockScreenColors = new int[2];
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final int mDozingColor = Color.WHITE;
+ private int mLockScreenColor;
private boolean mIsDozing;
+ private Locale mLocale;
+
+ private final NumberFormat mBurmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"));
+ private final String mBurmeseNumerals;
+ private final float mBurmeseLineSpacing;
+ private final float mDefaultLineSpacing;
public AnimatableClockController(
AnimatableClockView view,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ BroadcastDispatcher broadcastDispatcher) {
super(view);
mStatusBarStateController = statusBarStateController;
mIsDozing = mStatusBarStateController.isDozing();
+ mBroadcastDispatcher = broadcastDispatcher;
+
+ mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
+ mBurmeseLineSpacing = getContext().getResources().getFloat(
+ R.dimen.keyguard_clock_line_spacing_scale_burmese);
+ mDefaultLineSpacing = getContext().getResources().getFloat(
+ R.dimen.keyguard_clock_line_spacing_scale);
}
+ private BroadcastReceiver mLocaleBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateLocale();
+ }
+ };
+
@Override
protected void onViewAttached() {
+ updateLocale();
+ mBroadcastDispatcher.registerReceiver(mLocaleBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
mStatusBarStateController.addCallback(mStatusBarStateListener);
mIsDozing = mStatusBarStateController.isDozing();
refreshTime();
@@ -55,6 +90,7 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
@Override
protected void onViewDetached() {
+ mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
@@ -84,11 +120,23 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
mView.refreshFormat();
}
+ private void updateLocale() {
+ Locale currLocale = Locale.getDefault();
+ if (!Objects.equals(currLocale, mLocale)) {
+ mLocale = currLocale;
+ NumberFormat nf = NumberFormat.getInstance(mLocale);
+ if (nf.format(FORMAT_NUMBER).equals(mBurmeseNumerals)) {
+ mView.setLineSpacingScale(mBurmeseLineSpacing);
+ } else {
+ mView.setLineSpacingScale(mDefaultLineSpacing);
+ }
+ }
+ }
+
private void initColors() {
- mLockScreenColors[0] = Utils.getColorAttrDefaultColor(getContext(),
+ mLockScreenColor = Utils.getColorAttrDefaultColor(getContext(),
com.android.systemui.R.attr.wallpaperTextColor);
- mLockScreenColors[1] = mLockScreenColors[0]; // same color
- mView.setColors(mDozingColors, mLockScreenColors);
+ mView.setColors(mDozingColor, mLockScreenColor);
mView.animateDoze(mIsDozing, false);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index ca99563986b4..64b3d7356d3c 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -44,12 +44,13 @@ public class AnimatableClockView extends TextView {
private final Calendar mTime = Calendar.getInstance();
- private CharSequence mFormat;
- private CharSequence mDescFormat;
- private int[] mDozingColors;
- private int[] mLockScreenColors;
private final int mDozingWeight;
private final int mLockScreenWeight;
+ private CharSequence mFormat;
+ private CharSequence mDescFormat;
+ private int mDozingColor;
+ private int mLockScreenColor;
+ private float mLineSpacingScale = 1f;
private TextAnimator mTextAnimator = null;
private Runnable mOnTextAnimatorInitialized;
@@ -111,8 +112,7 @@ public class AnimatableClockView extends TextView {
() -> {
invalidate();
return Unit.INSTANCE;
- },
- 2 /* number of lines (each can have a unique Paint) */);
+ });
if (mOnTextAnimatorInitialized != null) {
mOnTextAnimatorInitialized.run();
mOnTextAnimatorInitialized = null;
@@ -127,15 +127,20 @@ public class AnimatableClockView extends TextView {
mTextAnimator.draw(canvas);
}
- void setColors(int[] dozingColors, int[] lockScreenColors) {
- mDozingColors = dozingColors;
- mLockScreenColors = lockScreenColors;
+ void setLineSpacingScale(float scale) {
+ mLineSpacingScale = scale;
+ setLineSpacing(0, mLineSpacingScale);
+ }
+
+ void setColors(int dozingColor, int lockScreenColor) {
+ mDozingColor = dozingColor;
+ mLockScreenColor = lockScreenColor;
}
void animateDoze(boolean isDozing, boolean animate) {
setTextStyle(isDozing ? mDozingWeight : mLockScreenWeight /* weight */,
-1,
- isDozing ? mDozingColors : mLockScreenColors,
+ isDozing ? mDozingColor : mLockScreenColor,
animate);
}
@@ -152,15 +157,15 @@ public class AnimatableClockView extends TextView {
private void setTextStyle(
@IntRange(from = 0, to = 1000) int weight,
@FloatRange(from = 0) float textSize,
- int[] colors,
+ int color,
boolean animate) {
if (mTextAnimator != null) {
- mTextAnimator.setTextStyle(weight, textSize, colors, animate, ANIM_DURATION, null);
+ mTextAnimator.setTextStyle(weight, textSize, color, animate, ANIM_DURATION, null);
} else {
// when the text animator is set, update its start values
mOnTextAnimatorInitialized =
() -> mTextAnimator.setTextStyle(
- weight, textSize, colors, false, ANIM_DURATION, null);
+ weight, textSize, color, false, ANIM_DURATION, null);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index e0de180e657c..e375877ed6cf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -28,6 +28,7 @@ import android.widget.FrameLayout;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ClockPlugin;
@@ -56,6 +57,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private final ClockManager mClockManager;
private final KeyguardSliceViewController mKeyguardSliceViewController;
private final NotificationIconAreaController mNotificationIconAreaController;
+ private final BroadcastDispatcher mBroadcastDispatcher;
/**
* Gradient clock for usage when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL.
@@ -101,7 +103,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
SysuiColorExtractor colorExtractor, ClockManager clockManager,
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
- ContentResolver contentResolver) {
+ ContentResolver contentResolver,
+ BroadcastDispatcher broadcastDispatcher) {
super(keyguardClockSwitch);
mResources = resources;
mStatusBarStateController = statusBarStateController;
@@ -109,6 +112,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mClockManager = clockManager;
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
+ mBroadcastDispatcher = broadcastDispatcher;
mTimeFormat = Settings.System.getString(contentResolver, Settings.System.TIME_12_24);
}
@@ -231,12 +235,14 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mNewLockScreenClockViewController =
new AnimatableClockController(
mView.findViewById(R.id.animatable_clock_view),
- mStatusBarStateController);
+ mStatusBarStateController,
+ mBroadcastDispatcher);
mNewLockScreenClockViewController.init();
mNewLockScreenLargeClockViewController =
new AnimatableClockController(
mView.findViewById(R.id.animatable_clock_view_large),
- mStatusBarStateController);
+ mStatusBarStateController,
+ mBroadcastDispatcher);
mNewLockScreenLargeClockViewController.init();
}
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
index f2d36d19afcf..5735a4fc0172 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
@@ -54,11 +54,10 @@ private const val DEFAULT_ANIMATION_DURATION: Long = 300
*/
class TextAnimator(
layout: Layout,
- private val invalidateCallback: () -> Unit,
- private val numLines: Int = 1
+ private val invalidateCallback: () -> Unit
) {
// Following two members are for mutable for testing purposes.
- internal var textInterpolator: TextInterpolator = TextInterpolator(layout, numLines)
+ internal var textInterpolator: TextInterpolator = TextInterpolator(layout)
internal var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply {
duration = DEFAULT_ANIMATION_DURATION
addUpdateListener {
@@ -99,7 +98,7 @@ class TextAnimator(
fun setTextStyle(
weight: Int = -1,
textSize: Float = -1f,
- colors: IntArray? = null,
+ color: Int? = null,
animate: Boolean = true,
duration: Long = -1L,
interpolator: TimeInterpolator? = null
@@ -110,21 +109,13 @@ class TextAnimator(
}
if (textSize >= 0) {
- for (targetPaint in textInterpolator.targetPaint)
- targetPaint.textSize = textSize
+ textInterpolator.targetPaint.textSize = textSize
}
if (weight >= 0) {
- for (targetPaint in textInterpolator.targetPaint)
- targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
+ textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
}
- if (colors != null) {
- require(colors.size == textInterpolator.targetPaint.size) {
- "colors size (${colors.size}) must be the same size as" +
- " targetPaints size (${textInterpolator.targetPaint.size})," +
- " which was initialized as numLines ($numLines)"
- }
- for ((index, targetPaint) in textInterpolator.targetPaint.withIndex())
- targetPaint.color = colors[index]
+ if (color != null) {
+ textInterpolator.targetPaint.color = color
}
textInterpolator.onTargetPaintModified()
diff --git a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
index 0d41a2f56618..5d5797cbbb3d 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
@@ -30,8 +30,7 @@ import java.lang.Math.max
* Provide text style linear interpolation for plain text.
*/
class TextInterpolator(
- layout: Layout,
- lines: Int = 1
+ layout: Layout
) {
/**
@@ -40,11 +39,9 @@ class TextInterpolator(
* Once you modified the style parameters, you have to call reshapeText to recalculate base text
* layout.
*
- * @return an array list of paint objects representing one paint per line of text. If this
- * list has a smaller size than the number of lines, all extra lines will use the Paint an
- * index 0.
+ * @return a paint object
*/
- val basePaint = createDefaultPaint(layout.paint, lines)
+ val basePaint = TextPaint(layout.paint)
/**
* Returns target paint used for interpolation.
@@ -52,18 +49,9 @@ class TextInterpolator(
* Once you modified the style parameters, you have to call reshapeText to recalculate target
* text layout.
*
- * @return an array list of paint objects representing one paint per line of text. If this
- * list has a smaller size than the number of lines, all extra lines will use the Paint an
- * index 0.
+ * @return a paint object
*/
- val targetPaint = createDefaultPaint(layout.paint, lines)
-
- private fun createDefaultPaint(paint: TextPaint, lines: Int): ArrayList<TextPaint> {
- val paintList = ArrayList<TextPaint>()
- for (i in 0 until lines)
- paintList.add(TextPaint(paint))
- return paintList
- }
+ val targetPaint = TextPaint(layout.paint)
/**
* A class represents a single font run.
@@ -102,7 +90,7 @@ class TextInterpolator(
private val fontInterpolator = FontInterpolator()
// Recycling object for glyph drawing. Will be extended for the longest font run if needed.
- private val tmpDrawPaints = ArrayList<TextPaint>()
+ private val tmpDrawPaint = TextPaint()
private var tmpPositionArray = FloatArray(20)
/**
@@ -216,10 +204,10 @@ class TextInterpolator(
if (progress == 0f) {
return
} else if (progress == 1f) {
- updatePaint(basePaint, targetPaint)
+ basePaint.set(targetPaint)
} else {
- lerp(basePaint, targetPaint, progress, tmpDrawPaints)
- updatePaint(basePaint, tmpDrawPaints)
+ lerp(basePaint, targetPaint, progress, tmpDrawPaint)
+ basePaint.set(tmpDrawPaint)
}
lines.forEach { line ->
@@ -237,21 +225,13 @@ class TextInterpolator(
progress = 0f
}
- companion object {
- fun updatePaint(toUpdate: ArrayList<TextPaint>, newValues: ArrayList<TextPaint>) {
- toUpdate.clear()
- for (paint in newValues)
- toUpdate.add(TextPaint(paint))
- }
- }
-
/**
* Draws interpolated text at the given progress.
*
* @param canvas a canvas.
*/
fun draw(canvas: Canvas) {
- lerp(basePaint, targetPaint, progress, tmpDrawPaints)
+ lerp(basePaint, targetPaint, progress, tmpDrawPaint)
lines.forEachIndexed { lineNo, line ->
line.runs.forEach { run ->
canvas.save()
@@ -261,10 +241,7 @@ class TextInterpolator(
canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat())
run.fontRuns.forEach { fontRun ->
- if (lineNo >= tmpDrawPaints.size)
- drawFontRun(canvas, run, fontRun, tmpDrawPaints[0])
- else
- drawFontRun(canvas, run, fontRun, tmpDrawPaints[lineNo])
+ drawFontRun(canvas, run, fontRun, tmpDrawPaint)
}
} finally {
canvas.restore()
@@ -430,27 +407,19 @@ class TextInterpolator(
}
// Linear interpolate the paint.
- private fun lerp(
- from: ArrayList<TextPaint>,
- to: ArrayList<TextPaint>,
- progress: Float,
- out: ArrayList<TextPaint>
- ) {
- out.clear()
+ private fun lerp(from: Paint, to: Paint, progress: Float, out: Paint) {
+ out.set(from)
+
// Currently only font size & colors are interpolated.
// TODO(172943390): Add other interpolation or support custom interpolator.
- for (index in from.indices) {
- val paint = TextPaint(from[index])
- paint.textSize = MathUtils.lerp(from[index].textSize, to[index].textSize, progress)
- paint.color = ColorUtils.blendARGB(from[index].color, to[index].color, progress)
- out.add(paint)
- }
+ out.textSize = MathUtils.lerp(from.textSize, to.textSize, progress)
+ out.color = ColorUtils.blendARGB(from.color, to.color, progress)
}
// Shape the text and stores the result to out argument.
private fun shapeText(
layout: Layout,
- paints: ArrayList<TextPaint>
+ paint: TextPaint
): List<List<PositionedGlyphs>> {
val out = mutableListOf<List<PositionedGlyphs>>()
for (lineNo in 0 until layout.lineCount) { // Shape all lines.
@@ -458,7 +427,7 @@ class TextInterpolator(
val count = layout.getLineEnd(lineNo) - lineStart
val runs = mutableListOf<PositionedGlyphs>()
TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic,
- paints[lineNo]) { _, _, glyphs, _ ->
+ paint) { _, _, glyphs, _ ->
runs.add(glyphs)
}
out.add(runs)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index ec4a91c24464..2b362b94d1f5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import com.android.systemui.ForegroundServicesDialog;
import com.android.systemui.keyguard.WorkLockActivity;
+import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.screenrecord.ScreenRecordDialog;
import com.android.systemui.settings.brightness.BrightnessDialog;
import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
@@ -92,4 +93,10 @@ public abstract class DefaultActivityBinder {
@IntoMap
@ClassKey(TvNotificationPanelActivity.class)
public abstract Activity bindTvNotificationPanelActivity(TvNotificationPanelActivity activity);
+
+ /** Inject into PeopleSpaceActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(PeopleSpaceActivity.class)
+ public abstract Activity bindPeopleSpaceActivity(PeopleSpaceActivity activity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index d0070d8d9ae9..8c04143abc54 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -289,7 +289,8 @@ public class KeyguardIndicationRotateTextViewController extends
if (hasIndications()) {
pw.println(" All messages:");
for (int type : mIndicationMessages.keySet()) {
- pw.println(" type=" + type + " message=" + mIndicationMessages.get(type));
+ pw.println(" type=" + type
+ + " message=" + mIndicationMessages.get(type).getMessage());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 5a918d4808d6..c55fdf4783e3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -67,6 +67,7 @@ import android.telephony.TelephonyManager;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.RemoteAnimationTarget;
@@ -306,6 +307,13 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
*/
private final SparseIntArray mLastSimStates = new SparseIntArray();
+ /**
+ * Indicates if a SIM card had the SIM PIN enabled during the initialization, before
+ * reaching the SIM_STATE_READY state. The flag is reset to false at SIM_STATE_READY.
+ * Index is the slotId - in case of multiple SIM cards.
+ */
+ private final SparseBooleanArray mSimWasLocked = new SparseBooleanArray();
+
private boolean mDeviceInteractive;
private boolean mGoingToSleep;
@@ -477,10 +485,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
}
}
- boolean simWasLocked;
+ boolean lastSimStateWasLocked;
synchronized (KeyguardViewMediator.this) {
int lastState = mLastSimStates.get(slotId);
- simWasLocked = (lastState == TelephonyManager.SIM_STATE_PIN_REQUIRED
+ lastSimStateWasLocked = (lastState == TelephonyManager.SIM_STATE_PIN_REQUIRED
|| lastState == TelephonyManager.SIM_STATE_PUK_REQUIRED);
mLastSimStates.append(slotId, simState);
}
@@ -504,17 +512,19 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
if (simState == TelephonyManager.SIM_STATE_ABSENT) {
// MVNO SIMs can become transiently NOT_READY when switching networks,
// so we should only lock when they are ABSENT.
- if (simWasLocked) {
+ if (lastSimStateWasLocked) {
if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the "
+ "previous state was locked. Reset the state.");
resetStateLocked();
}
+ mSimWasLocked.append(slotId, false);
}
}
break;
case TelephonyManager.SIM_STATE_PIN_REQUIRED:
case TelephonyManager.SIM_STATE_PUK_REQUIRED:
synchronized (KeyguardViewMediator.this) {
+ mSimWasLocked.append(slotId, true);
if (!mShowing) {
if (DEBUG_SIM_STATES) Log.d(TAG,
"INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
@@ -541,9 +551,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
case TelephonyManager.SIM_STATE_READY:
synchronized (KeyguardViewMediator.this) {
if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
- if (mShowing && simWasLocked) {
+ if (mShowing && mSimWasLocked.get(slotId, false)) {
if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to READY when the "
- + "previous state was locked. Reset the state.");
+ + "previously was locked. Reset the state.");
+ mSimWasLocked.append(slotId, false);
resetStateLocked();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 580cbcf8ce47..c67aef618652 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -37,9 +37,12 @@ import android.view.ViewGroup;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.util.List;
+import javax.inject.Inject;
+
/**
* Shows the user their tiles for their priority People (go/live-status).
*/
@@ -54,10 +57,17 @@ public class PeopleSpaceActivity extends Activity {
private LauncherApps mLauncherApps;
private Context mContext;
private AppWidgetManager mAppWidgetManager;
+ private NotificationEntryManager mNotificationEntryManager;
private int mAppWidgetId;
private boolean mShowSingleConversation;
private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+ @Inject
+ public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager) {
+ super();
+ mNotificationEntryManager = notificationEntryManager;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -91,8 +101,8 @@ public class PeopleSpaceActivity extends Activity {
*/
private void setTileViewsWithPriorityConversations() {
try {
- List<PeopleSpaceTile> tiles = PeopleSpaceUtils.getTiles(
- mContext, mNotificationManager, mPeopleManager, mLauncherApps);
+ List<PeopleSpaceTile> tiles = PeopleSpaceUtils.getTiles(mContext, mNotificationManager,
+ mPeopleManager, mLauncherApps, mNotificationEntryManager);
for (PeopleSpaceTile tile : tiles) {
PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext, mPeopleSpaceLayout,
tile.getId());
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 994dc6d78507..dd054848aed2 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -60,9 +60,12 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ArrayUtils;
import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.text.SimpleDateFormat;
import java.time.Duration;
@@ -137,7 +140,7 @@ public class PeopleSpaceUtils {
/** Returns a list of map entries corresponding to user's conversations. */
public static List<PeopleSpaceTile> getTiles(
Context context, INotificationManager notificationManager, IPeopleManager peopleManager,
- LauncherApps launcherApps)
+ LauncherApps launcherApps, NotificationEntryManager notificationEntryManager)
throws Exception {
boolean showOnlyPriority = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 1;
@@ -173,6 +176,8 @@ public class PeopleSpaceUtils {
getSortedTiles(peopleManager, launcherApps, mergedStream);
tiles.addAll(recentTiles);
}
+
+ tiles = augmentTilesFromVisibleNotifications(tiles, notificationEntryManager);
return tiles;
}
@@ -258,7 +263,8 @@ public class PeopleSpaceUtils {
ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE)),
- context.getSystemService(LauncherApps.class));
+ context.getSystemService(LauncherApps.class),
+ Dependency.get(NotificationEntryManager.class));
Optional<PeopleSpaceTile> entry = tiles.stream().filter(
e -> e.getId().equals(shortcutId)).findFirst();
if (entry.isPresent()) {
@@ -339,6 +345,41 @@ public class PeopleSpaceUtils {
&& storedUserId == userId;
}
+ static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(List<PeopleSpaceTile> tiles,
+ NotificationEntryManager notificationEntryManager) {
+ if (notificationEntryManager == null) {
+ Log.w(TAG, "NotificationEntryManager is null");
+ return tiles;
+ }
+ Map<String, NotificationEntry> visibleNotifications = notificationEntryManager
+ .getVisibleNotifications()
+ .stream()
+ .filter(entry -> entry.getRanking() != null
+ && entry.getRanking().getConversationShortcutInfo() != null)
+ .collect(Collectors.toMap(PeopleSpaceUtils::getKey, e -> e));
+ if (DEBUG) {
+ Log.d(TAG, "Number of visible notifications:" + visibleNotifications.size());
+ }
+ return tiles
+ .stream()
+ .map(entry -> augmentTileFromVisibleNotifications(entry, visibleNotifications))
+ .collect(Collectors.toList());
+ }
+
+ static PeopleSpaceTile augmentTileFromVisibleNotifications(PeopleSpaceTile tile,
+ Map<String, NotificationEntry> visibleNotifications) {
+ String shortcutId = tile.getId();
+ String packageName = tile.getPackageName();
+ int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+ String key = getKey(shortcutId, packageName, userId);
+ if (!visibleNotifications.containsKey(key)) {
+ if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key);
+ return tile;
+ }
+ if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key);
+ return augmentTileFromNotification(tile, visibleNotifications.get(key).getSbn());
+ }
+
/**
* If incoming notification changed tile, store the changes in the tile options.
*/
@@ -355,17 +396,7 @@ public class PeopleSpaceUtils {
}
if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
- Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn);
- if (message == null) {
- if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping.");
- return;
- }
- storedTile = storedTile
- .toBuilder()
- .setNotificationKey(sbn.getKey())
- .setNotificationContent(message.getText())
- .setNotificationDataUri(message.getDataUri())
- .build();
+ storedTile = augmentTileFromNotification(storedTile, sbn);
} else {
if (DEBUG) {
Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
@@ -380,6 +411,21 @@ public class PeopleSpaceUtils {
updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
}
+ static PeopleSpaceTile augmentTileFromNotification(PeopleSpaceTile tile,
+ StatusBarNotification sbn) {
+ Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn);
+ if (message == null) {
+ if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping.");
+ return tile;
+ }
+ return tile
+ .toBuilder()
+ .setNotificationKey(sbn.getKey())
+ .setNotificationContent(message.getText())
+ .setNotificationDataUri(message.getDataUri())
+ .build();
+ }
+
private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
PeopleSpaceTile tile) {
if (tile == null) {
@@ -792,6 +838,16 @@ public class PeopleSpaceUtils {
return lookupKeysWithBirthdaysToday;
}
+ static String getKey(NotificationEntry entry) {
+ if (entry.getRanking() == null || entry.getRanking().getConversationShortcutInfo() == null
+ || entry.getSbn() == null || entry.getSbn().getUser() == null) {
+ return null;
+ }
+ return getKey(entry.getRanking().getConversationShortcutInfo().getId(),
+ entry.getSbn().getPackageName(),
+ entry.getSbn().getUser().getIdentifier());
+ }
+
/**
* Returns the uniquely identifying key for the conversation.
*
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index bee54e4dca0a..bee9889eaa4e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -135,12 +135,14 @@ public class PeopleSpaceWidgetManager {
try {
String sbnShortcutId = sbn.getShortcutId();
if (sbnShortcutId == null) {
+ if (DEBUG) Log.d(TAG, "Sbn shortcut id is null");
return;
}
int[] widgetIds = mAppWidgetService.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
);
if (widgetIds.length == 0) {
+ Log.d(TAG, "No app widget ids returned");
return;
}
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
@@ -148,6 +150,7 @@ public class PeopleSpaceWidgetManager {
String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId);
Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
if (storedWidgetIds.isEmpty()) {
+ Log.d(TAG, "No stored widget ids");
return;
}
for (String widgetIdString : storedWidgetIds) {
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
index fb33affcbac5..80794cb64883 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -28,9 +28,11 @@ import android.util.Log;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.people.PeopleSpaceTileView;
import com.android.systemui.people.PeopleSpaceUtils;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.util.ArrayList;
import java.util.List;
@@ -42,6 +44,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R
private IPeopleManager mPeopleManager;
private INotificationManager mNotificationManager;
+ private NotificationEntryManager mNotificationEntryManager;
private PackageManager mPackageManager;
private LauncherApps mLauncherApps;
private List<PeopleSpaceTile> mTiles = new ArrayList<>();
@@ -56,6 +59,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R
if (DEBUG) Log.d(TAG, "onCreate called");
mNotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
mPackageManager = mContext.getPackageManager();
mPeopleManager = IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE));
@@ -70,7 +74,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R
private void setTileViewsWithPriorityConversations() {
try {
mTiles = PeopleSpaceUtils.getTiles(mContext, mNotificationManager,
- mPeopleManager, mLauncherApps);
+ mPeopleManager, mLauncherApps, mNotificationEntryManager);
} catch (Exception e) {
Log.e(TAG, "Couldn't retrieve conversations", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index d248ab544656..c8edaec98ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -362,7 +362,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
if(view == parent || view == null) return;
// Ignore tile pages as they can have some offset we don't want to take into account in
// RTL.
- if (!(view instanceof PagedTileLayout.TilePage)) {
+ if (!(view instanceof PagedTileLayout.TilePage || view instanceof SideLabelTileLayout)) {
loc1[0] += view.getLeft();
loc1[1] += view.getTop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 01a8c1c89f84..5b2a7e7ff617 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -620,6 +620,19 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
@Override
+ public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ if (!verifyCaller("exitSplitScreenOnHide")) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSplitScreenOptional.ifPresent(s -> s.exitSplitScreenOnHide(exitSplitScreenOnHide));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void startTask(int taskId, int stage, int position, Bundle options) {
if (!verifyCaller("startTask")) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index a03fc136da61..845d321416ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -71,6 +71,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.CachingIconView;
+import com.android.internal.widget.CallLayout;
import com.android.internal.widget.MessagingLayout;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
@@ -165,6 +166,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private int mMaxSmallHeightLarge;
private int mMaxSmallHeightMedia;
private int mMaxExpandedHeight;
+ private int mMaxCallHeight;
private int mIncreasedPaddingBetweenElements;
private int mNotificationLaunchHeight;
private boolean mMustStayOnScreen;
@@ -645,8 +647,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
private void updateLimitsForView(NotificationContentView layout) {
- boolean customView = layout.getContractedChild() != null
- && layout.getContractedChild().getId()
+ View contractedView = layout.getContractedChild();
+ boolean customView = contractedView != null
+ && contractedView.getId()
!= com.android.internal.R.id.status_bar_latest_event_content;
boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
boolean beforeP = mEntry.targetSdk < Build.VERSION_CODES.P;
@@ -661,7 +664,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
View expandedView = layout.getExpandedChild();
boolean isMediaLayout = expandedView != null
&& expandedView.findViewById(com.android.internal.R.id.media_actions) != null;
- boolean isMessagingLayout = layout.getContractedChild() instanceof MessagingLayout;
+ boolean isMessagingLayout = contractedView instanceof MessagingLayout;
+ boolean isCallLayout = contractedView instanceof CallLayout;
boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar();
if (customView && beforeS && !mIsSummaryWithChildren) {
@@ -684,6 +688,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// make sure we don't crop them terribly. We actually need to revisit this and give
// them a headerless design, then remove this hack.
smallHeight = mMaxSmallHeightLarge;
+ } else if (isCallLayout) {
+ smallHeight = mMaxCallHeight;
} else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
smallHeight = mMaxSmallHeightLarge;
} else {
@@ -1645,6 +1651,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
R.dimen.notification_min_height_media);
mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_height);
+ mMaxCallHeight = NotificationUtils.getFontScaledHeight(mContext,
+ R.dimen.call_notification_full_height);
mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_heads_up_height_legacy);
mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
new file mode 100644
index 000000000000..4541ebf4c4f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 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.row.wrapper
+
+import android.content.Context
+import android.view.View
+import com.android.internal.widget.CachingIconView
+import com.android.internal.widget.CallLayout
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationUtils
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+
+/**
+ * Wraps a notification containing a call template
+ */
+class NotificationCallTemplateViewWrapper constructor(
+ ctx: Context,
+ view: View,
+ row: ExpandableNotificationRow
+) : NotificationTemplateViewWrapper(ctx, view, row) {
+
+ private val minHeightWithActions: Int =
+ NotificationUtils.getFontScaledHeight(ctx, R.dimen.call_notification_full_height)
+ private val callLayout: CallLayout = view as CallLayout
+
+ private lateinit var conversationIconView: CachingIconView
+ private lateinit var conversationBadgeBg: View
+ private lateinit var expandBtn: View
+ private lateinit var appName: View
+ private lateinit var conversationTitleView: View
+
+ private fun resolveViews() {
+ with(callLayout) {
+ conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
+ conversationBadgeBg =
+ requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
+ expandBtn = requireViewById(com.android.internal.R.id.expand_button)
+ appName = requireViewById(com.android.internal.R.id.app_name_text)
+ conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
+ }
+ }
+
+ override fun onContentUpdated(row: ExpandableNotificationRow) {
+ // Reinspect the notification. Before the super call, because the super call also updates
+ // the transformation types and we need to have our values set by then.
+ resolveViews()
+ super.onContentUpdated(row)
+ }
+
+ override fun updateTransformedTypes() {
+ // This also clears the existing types
+ super.updateTransformedTypes()
+ addTransformedViews(
+ appName,
+ conversationTitleView
+ )
+ addViewsTransformingToSimilar(
+ conversationIconView,
+ conversationBadgeBg,
+ expandBtn
+ )
+ }
+
+ override fun disallowSingleClick(x: Float, y: Float): Boolean {
+ val isOnExpandButton = expandBtn.visibility == View.VISIBLE &&
+ isOnView(expandBtn, x, y)
+ return isOnExpandButton || super.disallowSingleClick(x, y)
+ }
+
+ override fun getMinLayoutHeight(): Int = minHeightWithActions
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index c49f6cbda8ac..905bccfa6cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.row.wrapper
import android.content.Context
import android.view.View
-import android.view.View.GONE
import android.view.ViewGroup
import com.android.internal.widget.CachingIconView
import com.android.internal.widget.ConversationLayout
@@ -48,8 +47,8 @@ class NotificationConversationTemplateViewWrapper constructor(
private lateinit var conversationIconView: CachingIconView
private lateinit var conversationBadgeBg: View
- private lateinit var expandButton: View
- private lateinit var expandButtonContainer: View
+ private lateinit var expandBtn: View
+ private lateinit var expandBtnContainer: View
private lateinit var imageMessageContainer: ViewGroup
private lateinit var messagingLinearLayout: MessagingLinearLayout
private lateinit var conversationTitleView: View
@@ -66,9 +65,8 @@ class NotificationConversationTemplateViewWrapper constructor(
conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
conversationBadgeBg =
requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
- expandButton = requireViewById(com.android.internal.R.id.expand_button)
- expandButtonContainer =
- requireViewById(com.android.internal.R.id.expand_button_container)
+ expandBtn = requireViewById(com.android.internal.R.id.expand_button)
+ expandBtnContainer = requireViewById(com.android.internal.R.id.expand_button_container)
importanceRing = requireViewById(com.android.internal.R.id.conversation_icon_badge_ring)
appName = requireViewById(com.android.internal.R.id.app_name_text)
conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
@@ -126,7 +124,7 @@ class NotificationConversationTemplateViewWrapper constructor(
addViewsTransformingToSimilar(
conversationIconView,
conversationBadgeBg,
- expandButton,
+ expandBtn,
importanceRing,
facePileTop,
facePileBottom,
@@ -134,11 +132,9 @@ class NotificationConversationTemplateViewWrapper constructor(
)
}
- override fun getExpandButton() = super.getExpandButton()
-
override fun setShelfIconVisible(visible: Boolean) {
if (conversationLayout.isImportantConversation) {
- if (conversationIconView.visibility != GONE) {
+ if (conversationIconView.visibility != View.GONE) {
conversationIconView.isForceHidden = visible
// We don't want the small icon to be hidden by the extended wrapper, as force
// hiding the conversationIcon will already do that via its listener.
@@ -152,7 +148,7 @@ class NotificationConversationTemplateViewWrapper constructor(
override fun getShelfTransformationTarget(): View? =
if (conversationLayout.isImportantConversation)
- if (conversationIconView.visibility != GONE)
+ if (conversationIconView.visibility != View.GONE)
conversationIconView
else
// A notification with a fallback icon was set to important. Currently
@@ -169,8 +165,8 @@ class NotificationConversationTemplateViewWrapper constructor(
conversationLayout.updateExpandability(expandable, onClickListener)
override fun disallowSingleClick(x: Float, y: Float): Boolean {
- val isOnExpandButton = expandButtonContainer.visibility == View.VISIBLE &&
- isOnView(expandButtonContainer, x, y)
+ val isOnExpandButton = expandBtnContainer.visibility == View.VISIBLE &&
+ isOnView(expandBtnContainer, x, y)
return isOnExpandButton || super.disallowSingleClick(x, y)
}
@@ -179,10 +175,4 @@ class NotificationConversationTemplateViewWrapper constructor(
minHeightWithActions
else
super.getMinLayoutHeight()
-
- private fun addTransformedViews(vararg vs: View?) =
- vs.forEach { view -> view?.let(mTransformationHelper::addTransformedView) }
-
- private fun addViewsTransformingToSimilar(vararg vs: View?) =
- vs.forEach { view -> view?.let(mTransformationHelper::addViewTransformingToSimilar) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 97201f5c9a34..34bc5370e8f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -36,7 +36,6 @@ import android.widget.TextView;
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.NotificationExpandButton;
-import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
@@ -60,6 +59,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
private CachingIconView mIcon;
private NotificationExpandButton mExpandButton;
private View mAltExpandTarget;
+ private View mIconContainer;
protected NotificationHeaderView mNotificationHeader;
protected NotificationTopLineView mNotificationTopLine;
private TextView mHeaderText;
@@ -112,6 +112,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text);
mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
mAltExpandTarget = mView.findViewById(com.android.internal.R.id.alternate_expand_target);
+ mIconContainer = mView.findViewById(com.android.internal.R.id.conversation_icon_container);
mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon);
mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon);
mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
@@ -203,11 +204,8 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
public void clearConversationSkin() {
if (mAppNameText != null) {
final ColorStateList colors = mAppNameText.getTextColors();
- final int textAppearance = Utils.getThemeAttr(
- mAppNameText.getContext(),
- com.android.internal.R.attr.notificationHeaderTextAppearance,
+ mAppNameText.setTextAppearance(
com.android.internal.R.style.TextAppearance_DeviceDefault_Notification_Info);
- mAppNameText.setTextAppearance(textAppearance);
mAppNameText.setTextColor(colors);
MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams();
final int marginStart = mAppNameText.getResources().getDimensionPixelSize(
@@ -265,19 +263,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon);
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_EXPANDER,
mExpandButton);
- if (mWorkProfileImage != null) {
- mTransformationHelper.addViewTransformingToSimilar(mWorkProfileImage);
- }
if (mIsLowPriority && mHeaderText != null) {
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
mHeaderText);
}
- if (mAudiblyAlertedIcon != null) {
- mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon);
- }
- if (mFeedbackIcon != null) {
- mTransformationHelper.addViewTransformingToSimilar(mFeedbackIcon);
- }
+ addViewsTransformingToSimilar(mWorkProfileImage, mAudiblyAlertedIcon, mFeedbackIcon);
}
@Override
@@ -287,6 +277,9 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
if (mAltExpandTarget != null) {
mAltExpandTarget.setOnClickListener(expandable ? onClickListener : null);
}
+ if (mIconContainer != null) {
+ mIconContainer.setOnClickListener(expandable ? onClickListener : null);
+ }
if (mNotificationHeader != null) {
mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
}
@@ -371,4 +364,20 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
super.setVisible(visible);
mTransformationHelper.setVisible(visible);
}
+
+ protected void addTransformedViews(View... views) {
+ for (View view : views) {
+ if (view != null) {
+ mTransformationHelper.addTransformedView(view);
+ }
+ }
+ }
+
+ protected void addViewsTransformingToSimilar(View... views) {
+ for (View view : views) {
+ if (view != null) {
+ mTransformationHelper.addViewTransformingToSimilar(view);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index b3d1a94beaa9..5fff8c83048f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -74,6 +74,8 @@ public abstract class NotificationViewWrapper implements TransformableView {
return new NotificationMessagingTemplateViewWrapper(ctx, v, row);
} else if ("conversation".equals(v.getTag())) {
return new NotificationConversationTemplateViewWrapper(ctx, v, row);
+ } else if ("call".equals(v.getTag())) {
+ return new NotificationCallTemplateViewWrapper(ctx, v, row);
}
Class<? extends Notification.Style> style =
row.getEntry().getSbn().getNotification().getNotificationStyle();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 0552396bab07..68d74ef760b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -113,7 +113,9 @@ public class UserSwitcherController implements Dumpable {
private int mLastNonGuestUser = UserHandle.USER_SYSTEM;
private boolean mResumeUserOnGuestLogout = true;
private boolean mSimpleUserSwitcher;
- private boolean mAddUsersWhenLocked;
+ // When false, there won't be any visual affordance to add a new user from the keyguard even if
+ // the user is unlocked
+ private boolean mAddUsersFromLockScreen;
private boolean mPauseRefreshUsers;
private int mSecondaryUser = UserHandle.USER_NULL;
private Intent mSecondaryUserServiceIntent;
@@ -204,7 +206,7 @@ public class UserSwitcherController implements Dumpable {
}
mForcePictureLoadForUserId.clear();
- final boolean addUsersWhenLocked = mAddUsersWhenLocked;
+ final boolean addUsersWhenLocked = mAddUsersFromLockScreen;
new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
@SuppressWarnings("unchecked")
@Override
@@ -554,7 +556,7 @@ public class UserSwitcherController implements Dumpable {
private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
public void onChange(boolean selfChange) {
mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
- mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(),
+ mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
refreshUsers(UserHandle.USER_NULL);
};
@@ -610,43 +612,26 @@ public class UserSwitcherController implements Dumpable {
}
public int getUserCount() {
- boolean secureKeyguardShowing = mKeyguardStateController.isShowing()
- && mKeyguardStateController.isMethodSecure()
- && !mKeyguardStateController.canDismissLockScreen();
- if (!secureKeyguardShowing) {
- return getUsers().size();
- }
- // The lock screen is secure and showing. Filter out restricted records.
- final int userSize = getUsers().size();
- int count = 0;
- for (int i = 0; i < userSize; i++) {
- if (getUsers().get(i).isGuest) continue;
- if (getUsers().get(i).isRestricted) {
- break;
- } else {
- count++;
- }
- }
- return count;
+ return countUsers(false);
}
@Override
public int getCount() {
- boolean secureKeyguardShowing = mKeyguardStateController.isShowing()
- && mKeyguardStateController.isMethodSecure()
- && !mKeyguardStateController.canDismissLockScreen();
- if (!secureKeyguardShowing) {
- return getUsers().size();
- }
- // The lock screen is secure and showing. Filter out restricted records.
+ return countUsers(true);
+ }
+
+ private int countUsers(boolean includeGuest) {
+ boolean keyguardShowing = mKeyguardStateController.isShowing();
final int userSize = getUsers().size();
int count = 0;
for (int i = 0; i < userSize; i++) {
- if (getUsers().get(i).isRestricted) {
+ if (getUsers().get(i).isGuest && !includeGuest) {
+ continue;
+ }
+ if (getUsers().get(i).isRestricted && keyguardShowing) {
break;
- } else {
- count++;
}
+ count++;
}
return count;
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index a2eaea1a37c5..70a7b7a5acbc 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -34,6 +34,7 @@ import android.widget.FrameLayout;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -75,6 +76,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
NotificationIconAreaController mNotificationIconAreaController;
@Mock
ContentResolver mContentResolver;
+ @Mock
+ BroadcastDispatcher mBroadcastDispatcher;
private KeyguardClockSwitchController mController;
@@ -94,7 +97,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
mClockManager,
mKeyguardSliceViewController,
mNotificationIconAreaController,
- mContentResolver);
+ mContentResolver,
+ mBroadcastDispatcher);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
index 53d84dba6fb2..7b4f14dd3595 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
@@ -34,9 +34,9 @@ import org.mockito.Mockito.verify
import kotlin.math.ceil
-private val PAINT = arrayListOf(TextPaint().apply {
+private val PAINT = TextPaint().apply {
textSize = 32f
-})
+}
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -49,10 +49,10 @@ class TextAnimatorTest : SysuiTestCase() {
@Test
fun testAnimationStarted() {
- val layout = makeLayout("Hello, World", PAINT[0])
+ val layout = makeLayout("Hello, World", PAINT)
val valueAnimator = mock(ValueAnimator::class.java)
val textInterpolator = mock(TextInterpolator::class.java)
- val paint = arrayListOf(mock(TextPaint::class.java))
+ val paint = mock(TextPaint::class.java)
`when`(textInterpolator.targetPaint).thenReturn(paint)
val textAnimator = TextAnimator(layout, {}).apply {
@@ -81,10 +81,10 @@ class TextAnimatorTest : SysuiTestCase() {
@Test
fun testAnimationNotStarted() {
- val layout = makeLayout("Hello, World", PAINT[0])
+ val layout = makeLayout("Hello, World", PAINT)
val valueAnimator = mock(ValueAnimator::class.java)
val textInterpolator = mock(TextInterpolator::class.java)
- val paint = arrayListOf(mock(TextPaint::class.java))
+ val paint = mock(TextPaint::class.java)
`when`(textInterpolator.targetPaint).thenReturn(paint)
val textAnimator = TextAnimator(layout, {}).apply {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
index 1206dab7b56d..149e179de5a6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
@@ -21,9 +21,9 @@ import android.graphics.Canvas
import android.testing.AndroidTestingRunner
import android.text.Layout
import android.text.StaticLayout
-import android.text.TextPaint
import android.text.TextDirectionHeuristic
import android.text.TextDirectionHeuristics
+import android.text.TextPaint
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -40,13 +40,13 @@ private val PAINT = TextPaint().apply {
textSize = 32f
}
-private val START_PAINT = arrayListOf(TextPaint(PAINT).apply {
+private val START_PAINT = TextPaint(PAINT).apply {
fontVariationSettings = "'wght' 400"
-})
+}
-private val END_PAINT = arrayListOf(TextPaint(PAINT).apply {
+private val END_PAINT = TextPaint(PAINT).apply {
fontVariationSettings = "'wght' 700"
-})
+}
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -67,16 +67,16 @@ class TextInterpolatorTest : SysuiTestCase() {
val layout = makeLayout(TEXT, PAINT)
val interp = TextInterpolator(layout)
- TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+ interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
- TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+ interp.targetPaint.set(END_PAINT)
interp.onTargetPaintModified()
// Just after created TextInterpolator, it should have 0 progress.
assertThat(interp.progress).isEqualTo(0f)
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(TEXT, START_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT)
+ val expected = makeLayout(TEXT, START_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT)
assertThat(expected.sameAs(actual)).isTrue()
}
@@ -86,15 +86,15 @@ class TextInterpolatorTest : SysuiTestCase() {
val layout = makeLayout(TEXT, PAINT)
val interp = TextInterpolator(layout)
- TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+ interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
- TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+ interp.targetPaint.set(END_PAINT)
interp.onTargetPaintModified()
interp.progress = 1f
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(TEXT, END_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT)
+ val expected = makeLayout(TEXT, END_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT)
assertThat(expected.sameAs(actual)).isTrue()
}
@@ -104,10 +104,10 @@ class TextInterpolatorTest : SysuiTestCase() {
val layout = makeLayout(TEXT, PAINT)
val interp = TextInterpolator(layout)
- TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+ interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
- TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+ interp.targetPaint.set(END_PAINT)
interp.onTargetPaintModified()
// We cannot expect exact text layout of the middle position since we don't use text shaping
@@ -115,9 +115,9 @@ class TextInterpolatorTest : SysuiTestCase() {
// end state.
interp.progress = 0.5f
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT[0])
+ assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT)
.toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse()
- assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT[0])
+ assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT)
.toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse()
}
@@ -126,10 +126,10 @@ class TextInterpolatorTest : SysuiTestCase() {
val layout = makeLayout(TEXT, PAINT)
val interp = TextInterpolator(layout)
- TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+ interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
- TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+ interp.targetPaint.set(END_PAINT)
interp.onTargetPaintModified()
interp.progress = 0.5f
@@ -148,16 +148,16 @@ class TextInterpolatorTest : SysuiTestCase() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.LTR)
val interp = TextInterpolator(layout)
- TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+ interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
- TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+ interp.targetPaint.set(END_PAINT)
interp.onTargetPaintModified()
// Just after created TextInterpolator, it should have 0 progress.
assertThat(interp.progress).isEqualTo(0f)
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.LTR)
+ val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.LTR)
.toBitmap(BMP_WIDTH, BMP_HEIGHT)
assertThat(expected.sameAs(actual)).isTrue()
@@ -168,16 +168,16 @@ class TextInterpolatorTest : SysuiTestCase() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
val interp = TextInterpolator(layout)
- TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+ interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
- TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+ interp.targetPaint.set(END_PAINT)
interp.onTargetPaintModified()
// Just after created TextInterpolator, it should have 0 progress.
assertThat(interp.progress).isEqualTo(0f)
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.RTL)
+ val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
.toBitmap(BMP_WIDTH, BMP_HEIGHT)
assertThat(expected.sameAs(actual)).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index 6db21f9159ec..4ee2759028a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.people;
import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
+import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
import static com.google.common.truth.Truth.assertThat;
@@ -52,6 +53,7 @@ import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
@@ -64,6 +66,9 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import org.junit.Before;
import org.junit.Test;
@@ -82,10 +87,17 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
private static final int WIDGET_ID_WITH_SHORTCUT = 1;
private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
- private static final String SHORTCUT_ID = "101";
+ private static final String SHORTCUT_ID_1 = "101";
+ private static final String SHORTCUT_ID_2 = "202";
+ private static final String SHORTCUT_ID_3 = "303";
+ private static final String SHORTCUT_ID_4 = "404";
private static final String NOTIFICATION_KEY = "notification_key";
private static final String NOTIFICATION_CONTENT = "notification_content";
private static final String TEST_LOOKUP_KEY = "lookup_key";
+ private static final String NOTIFICATION_TEXT_1 = "notification_text_1";
+ private static final String NOTIFICATION_TEXT_2 = "notification_text_2";
+ private static final String NOTIFICATION_TEXT_3 = "notification_text_3";
+ private static final String NOTIFICATION_TEXT_4 = "notification_text_4";
private static final int TEST_COLUMN_INDEX = 1;
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
@@ -97,20 +109,66 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
.build();
private static final PeopleSpaceTile PERSON_TILE =
new PeopleSpaceTile
- .Builder(SHORTCUT_ID, "username", ICON, new Intent())
+ .Builder(SHORTCUT_ID_1, "username", ICON, new Intent())
.setNotificationKey(NOTIFICATION_KEY)
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(URI)
.build();
private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
- SHORTCUT_ID).setLongLabel(
+ SHORTCUT_ID_1).setLongLabel(
"name").setPerson(PERSON)
.build();
private final ShortcutInfo mShortcutInfoWithoutPerson = new ShortcutInfo.Builder(mContext,
- SHORTCUT_ID).setLongLabel(
+ SHORTCUT_ID_1).setLongLabel(
"name")
.build();
+ private final Notification mNotification1 = new Notification.Builder(mContext, "test")
+ .setContentTitle("TEST_TITLE")
+ .setContentText("TEST_TEXT")
+ .setShortcutId(SHORTCUT_ID_1)
+ .setStyle(new Notification.MessagingStyle(PERSON)
+ .addMessage(new Notification.MessagingStyle.Message(
+ NOTIFICATION_TEXT_1, 0, PERSON))
+ .addMessage(new Notification.MessagingStyle.Message(
+ NOTIFICATION_TEXT_2, 20, PERSON))
+ .addMessage(new Notification.MessagingStyle.Message(
+ NOTIFICATION_TEXT_3, 10, PERSON))
+ )
+ .build();
+ private final Notification mNotification2 = new Notification.Builder(mContext, "test2")
+ .setContentTitle("TEST_TITLE")
+ .setContentText("OTHER_TEXT")
+ .setShortcutId(SHORTCUT_ID_2)
+ .setStyle(new Notification.MessagingStyle(PERSON)
+ .addMessage(new Notification.MessagingStyle.Message(
+ NOTIFICATION_TEXT_4, 0, PERSON))
+ )
+ .build();
+ private final Notification mNotification3 = new Notification.Builder(mContext, "test2")
+ .setContentTitle("TEST_TITLE")
+ .setContentText("OTHER_TEXT")
+ .setShortcutId(SHORTCUT_ID_3)
+ .setStyle(new Notification.MessagingStyle(PERSON))
+ .build();
+ private final NotificationEntry mNotificationEntry1 = new NotificationEntryBuilder()
+ .setNotification(mNotification1)
+ .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_1).build())
+ .setUser(UserHandle.of(0))
+ .setPkg(PACKAGE_NAME)
+ .build();
+ private final NotificationEntry mNotificationEntry2 = new NotificationEntryBuilder()
+ .setNotification(mNotification2)
+ .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_2).build())
+ .setUser(UserHandle.of(0))
+ .setPkg(PACKAGE_NAME)
+ .build();
+ private final NotificationEntry mNotificationEntry3 = new NotificationEntryBuilder()
+ .setNotification(mNotification3)
+ .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_3).build())
+ .setUser(UserHandle.of(0))
+ .setPkg(PACKAGE_NAME)
+ .build();
@Mock
private NotificationListener mListenerService;
@@ -130,6 +188,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
private ContentResolver mMockContentResolver;
@Mock
private Context mMockContext;
+ @Mock
+ private NotificationEntryManager mNotificationEntryManager;
@Before
public void setUp() throws RemoteException {
@@ -152,15 +212,17 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
isNull())).thenReturn(mMockCursor);
when(mMockContext.getString(R.string.birthday_status)).thenReturn(
mContext.getString(R.string.birthday_status));
+ when(mNotificationEntryManager.getVisibleNotifications())
+ .thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3));
}
@Test
public void testGetTilesReturnsSortedListWithMultipleRecentConversations() throws Exception {
// Ensure the less-recent Important conversation is before more recent conversations.
ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID, false, 3);
+ SHORTCUT_ID_1, false, 3);
ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID + 1,
+ SHORTCUT_ID_1 + 1,
true, 1);
when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
new ParceledListSlice(Arrays.asList(
@@ -169,9 +231,9 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
// Ensure the non-Important conversation is sorted between these recent conversations.
ConversationChannel recentConversationBeforeNonImportantConversation =
getConversationChannel(
- SHORTCUT_ID + 2, 4);
+ SHORTCUT_ID_1 + 2, 4);
ConversationChannel recentConversationAfterNonImportantConversation =
- getConversationChannel(SHORTCUT_ID + 3,
+ getConversationChannel(SHORTCUT_ID_1 + 3,
2);
when(mPeopleManager.getRecentConversations()).thenReturn(
new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
@@ -179,7 +241,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
List<String> orderedShortcutIds = PeopleSpaceUtils.getTiles(
mContext, mNotificationManager, mPeopleManager,
- mLauncherApps).stream().map(tile -> tile.getId()).collect(Collectors.toList());
+ mLauncherApps, mNotificationEntryManager)
+ .stream().map(tile -> tile.getId()).collect(Collectors.toList());
assertThat(orderedShortcutIds).containsExactly(
// Even though the oldest conversation, should be first since "important"
@@ -196,11 +259,11 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
throws Exception {
// Ensure the less-recent Important conversation is before more recent conversations.
ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID, false, 3);
+ SHORTCUT_ID_1, false, 3);
ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID + 1, true, 3);
+ SHORTCUT_ID_1 + 1, true, 3);
ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID + 2,
+ SHORTCUT_ID_1 + 2,
true, 1);
when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
new ParceledListSlice(Arrays.asList(
@@ -210,9 +273,9 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
// Ensure the non-Important conversation is sorted between these recent conversations.
ConversationChannel recentConversationBeforeNonImportantConversation =
getConversationChannel(
- SHORTCUT_ID + 3, 4);
+ SHORTCUT_ID_1 + 3, 4);
ConversationChannel recentConversationAfterNonImportantConversation =
- getConversationChannel(SHORTCUT_ID + 4,
+ getConversationChannel(SHORTCUT_ID_1 + 4,
2);
when(mPeopleManager.getRecentConversations()).thenReturn(
new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
@@ -220,7 +283,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
List<String> orderedShortcutIds = PeopleSpaceUtils.getTiles(
mContext, mNotificationManager, mPeopleManager,
- mLauncherApps).stream().map(tile -> tile.getId()).collect(Collectors.toList());
+ mLauncherApps, mNotificationEntryManager)
+ .stream().map(tile -> tile.getId()).collect(Collectors.toList());
assertThat(orderedShortcutIds).containsExactly(
// Important conversations should be sorted at the beginning.
@@ -238,7 +302,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
Notification notification = new Notification.Builder(mContext, "test")
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
- .setShortcutId(SHORTCUT_ID)
+ .setShortcutId(SHORTCUT_ID_1)
.build();
StatusBarNotification sbn = new SbnBuilder()
.setNotification(notification)
@@ -341,24 +405,126 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
@Test
public void testGetLastMessagingStyleMessage() {
- Notification notification = new Notification.Builder(mContext, "test")
- .setContentTitle("TEST_TITLE")
- .setContentText("TEST_TEXT")
- .setShortcutId(SHORTCUT_ID)
- .setStyle(new Notification.MessagingStyle(PERSON)
- .addMessage(new Notification.MessagingStyle.Message("text1", 0, PERSON))
- .addMessage(new Notification.MessagingStyle.Message("text2", 20, PERSON))
- .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
- )
- .build();
StatusBarNotification sbn = new SbnBuilder()
- .setNotification(notification)
+ .setNotification(mNotification1)
.build();
Notification.MessagingStyle.Message lastMessage =
PeopleSpaceUtils.getLastMessagingStyleMessage(sbn);
- assertThat(lastMessage.getText()).isEqualTo("text2");
+ assertThat(lastMessage.getText().toString()).isEqualTo(NOTIFICATION_TEXT_2);
+ }
+
+ @Test
+ public void testAugmentTileFromNotification() {
+ StatusBarNotification sbn = new SbnBuilder()
+ .setNotification(mNotification1)
+ .build();
+
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ PeopleSpaceTile actual = PeopleSpaceUtils
+ .augmentTileFromNotification(tile, sbn);
+
+ assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
+ }
+
+ @Test
+ public void testAugmentTileFromNotificationNoContent() {
+ StatusBarNotification sbn = new SbnBuilder()
+ .setNotification(mNotification3)
+ .build();
+
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_3, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ PeopleSpaceTile actual = PeopleSpaceUtils
+ .augmentTileFromNotification(tile, sbn);
+
+ assertThat(actual.getNotificationKey()).isEqualTo(null);
+ assertThat(actual.getNotificationContent()).isEqualTo(null);
+ }
+
+ @Test
+ public void testAugmentTileFromVisibleNotifications() {
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ PeopleSpaceTile actual = PeopleSpaceUtils
+ .augmentTileFromVisibleNotifications(tile,
+ Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
+
+ assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
+ }
+
+ @Test
+ public void testAugmentTileFromVisibleNotificationsDifferentShortcutId() {
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_4, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ PeopleSpaceTile actual = PeopleSpaceUtils
+ .augmentTileFromVisibleNotifications(tile,
+ Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
+
+ assertThat(actual.getNotificationContent()).isEqualTo(null);
+ }
+
+ @Test
+ public void testAugmentTilesFromVisibleNotificationsSingleTile() {
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ List<PeopleSpaceTile> actualList = PeopleSpaceUtils
+ .augmentTilesFromVisibleNotifications(List.of(tile), mNotificationEntryManager);
+
+ assertThat(actualList.size()).isEqualTo(1);
+ assertThat(actualList.get(0).getNotificationContent().toString())
+ .isEqualTo(NOTIFICATION_TEXT_2);
+
+ verify(mNotificationEntryManager, times(1)).getVisibleNotifications();
+ }
+
+ @Test
+ public void testAugmentTilesFromVisibleNotificationsMultipleTiles() {
+ PeopleSpaceTile tile1 =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(1)
+ .build();
+ PeopleSpaceTile tile2 =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_2, "userName2", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ List<PeopleSpaceTile> actualList = PeopleSpaceUtils
+ .augmentTilesFromVisibleNotifications(List.of(tile1, tile2),
+ mNotificationEntryManager);
+
+ assertThat(actualList.size()).isEqualTo(2);
+ assertThat(actualList.get(0).getNotificationContent().toString())
+ .isEqualTo(NOTIFICATION_TEXT_2);
+ assertThat(actualList.get(1).getNotificationContent().toString())
+ .isEqualTo(NOTIFICATION_TEXT_4);
+
+ verify(mNotificationEntryManager, times(1)).getVisibleNotifications();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index ef314ad16556..9470141178dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -421,6 +421,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
@@ -429,15 +430,21 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
.build();
StatusBarNotification sbn = new SbnBuilder()
.setNotification(notificationWithoutMessagingStyle)
+ .setPkg(TEST_PACKAGE_A)
+ .setUid(0)
.build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mAppWidgetManager, never())
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
- verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle options = requireNonNull(mBundleArgumentCaptor.getValue());
+ assertThat((PeopleSpaceTile) options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE))
+ .isEqualTo(PERSON_TILE);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index 25f4abe2cb14..903a07140eae 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -19,12 +19,12 @@ package com.android.server.accessibility.gestures;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
-import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
@@ -147,15 +147,15 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this));
mMultiFingerGestures.add(
- new MultiFingerMultiTapAndHold(
- mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD, this));
- mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
new MultiFingerMultiTapAndHold(
mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, this));
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP, this));
+ mMultiFingerGestures.add(
+ new MultiFingerMultiTapAndHold(
+ mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD, this));
// Three-finger taps.
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 3, 1, GESTURE_3_FINGER_SINGLE_TAP, this));
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 67f654e6360f..6d72ca7bae25 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1492,7 +1492,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
final Dataset dataset = (Dataset) result;
final Dataset oldDataset = authenticatedResponse.getDatasets().get(datasetIdx);
- if (!isPinnedDataset(oldDataset)) {
+ if (!isAuthResultDatasetEphemeral(oldDataset, data)) {
authenticatedResponse.getDatasets().set(datasetIdx, dataset);
}
autoFill(requestId, datasetIdx, dataset, false);
@@ -1513,6 +1513,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
+ * Returns whether the dataset returned from the authentication result is ephemeral or not.
+ * See {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more
+ * information.
+ */
+ private static boolean isAuthResultDatasetEphemeral(@Nullable Dataset oldDataset,
+ @NonNull Bundle authResultData) {
+ if (authResultData.containsKey(
+ AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
+ return authResultData.getBoolean(
+ AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET);
+ }
+ return isPinnedDataset(oldDataset);
+ }
+
+ /**
* A dataset can potentially have multiple fields, and it's possible that some of the fields'
* has inline presentation and some don't. It's also possible that some of the fields'
* inline presentation is pinned and some isn't. So the concept of whether a dataset is
diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index c56cef2d58dc..a83c981235df 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -16,12 +16,6 @@
package com.android.server;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -33,10 +27,15 @@ import android.os.Message;
import android.os.SystemProperties;
import android.util.Slog;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+
/**
* A service designed to load and periodically save &quot;randomness&quot;
- * for the Linux kernel RNG and to mix in data from Hardware RNG (if present)
- * into the Linux RNG.
+ * for the Linux kernel RNG.
*
* <p>When a Linux system starts up, the entropy pool associated with
* {@code /dev/random} may be in a fairly predictable state. Applications which
@@ -45,15 +44,8 @@ import android.util.Slog;
* this effect, it's helpful to carry the entropy pool information across
* shutdowns and startups.
*
- * <p>On systems with Hardware RNG (/dev/hw_random), a block of output from HW
- * RNG is mixed into the Linux RNG on EntropyMixer's startup and whenever
- * EntropyMixer periodically runs to save a block of output from Linux RNG on
- * disk. This mixing is done in a way that does not increase the Linux RNG's
- * entropy estimate is not increased. This is to avoid having to trust/verify
- * the quality and authenticity of the &quot;randomness&quot; of the HW RNG.
- *
* <p>This class was modeled after the script in the
- * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">
+ * <a href="https://man7.org/linux/man-pages/man4/random.4.html">
* random(4) manual page</a>.
*/
public class EntropyMixer extends Binder {
@@ -64,7 +56,6 @@ public class EntropyMixer extends Binder {
private static final long START_NANOTIME = System.nanoTime();
private final String randomDevice;
- private final String hwRandomDevice;
private final String entropyFile;
/**
@@ -80,7 +71,6 @@ public class EntropyMixer extends Binder {
Slog.e(TAG, "Will not process invalid message");
return;
}
- addHwRandomEntropy();
writeEntropy();
scheduleEntropyWriter();
}
@@ -94,25 +84,21 @@ public class EntropyMixer extends Binder {
};
public EntropyMixer(Context context) {
- this(context, getSystemDir() + "/entropy.dat", "/dev/urandom", "/dev/hw_random");
+ this(context, getSystemDir() + "/entropy.dat", "/dev/urandom");
}
/** Test only interface, not for public use */
public EntropyMixer(
Context context,
String entropyFile,
- String randomDevice,
- String hwRandomDevice) {
+ String randomDevice) {
if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
- if (hwRandomDevice == null) { throw new NullPointerException("hwRandomDevice"); }
if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
this.randomDevice = randomDevice;
- this.hwRandomDevice = hwRandomDevice;
this.entropyFile = entropyFile;
loadInitialEntropy();
addDeviceSpecificEntropy();
- addHwRandomEntropy();
writeEntropy();
scheduleEntropyWriter();
IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
@@ -192,23 +178,6 @@ public class EntropyMixer extends Binder {
}
}
- /**
- * Mixes in the output from HW RNG (if present) into the Linux RNG.
- */
- private void addHwRandomEntropy() {
- if (!new File(hwRandomDevice).exists()) {
- // HW RNG not present/exposed -- ignore
- return;
- }
-
- try {
- RandomBlock.fromFile(hwRandomDevice).toFile(randomDevice, false);
- Slog.i(TAG, "Added HW RNG output to entropy pool");
- } catch (IOException e) {
- Slog.w(TAG, "Failed to add HW RNG output to entropy pool", e);
- }
- }
-
private static String getSystemDir() {
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9382e1aa2a9e..f0f29a9be7a7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -10700,13 +10700,13 @@ public class ActivityManagerService extends IActivityManager.Stub
long kernelUsed = memInfo.getKernelUsedSizeKb();
final long ionHeap = Debug.getIonHeapsSizeKb();
final long ionPool = Debug.getIonPoolsSizeKb();
+ final long dmabufMapped = Debug.getDmabufMappedSizeKb();
if (ionHeap >= 0 && ionPool >= 0) {
- final long ionMapped = Debug.getIonMappedSizeKb();
- final long ionUnmapped = ionHeap - ionMapped;
+ final long ionUnmapped = ionHeap - dmabufMapped;
pw.print(" ION: ");
pw.print(stringifyKBSize(ionHeap + ionPool));
pw.print(" (");
- pw.print(stringifyKBSize(ionMapped));
+ pw.print(stringifyKBSize(dmabufMapped));
pw.print(" mapped + ");
pw.print(stringifyKBSize(ionUnmapped));
pw.print(" unmapped + ");
@@ -10715,11 +10715,34 @@ public class ActivityManagerService extends IActivityManager.Stub
// Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
// set on ION VMAs, therefore consider the entire ION heap as used kernel memory
kernelUsed += ionHeap;
+ } else {
+ final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();
+ if (totalExportedDmabuf >= 0) {
+ final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped;
+ pw.print("DMA-BUF: ");
+ pw.print(stringifyKBSize(totalExportedDmabuf));
+ pw.print(" (");
+ pw.print(stringifyKBSize(dmabufMapped));
+ pw.print(" mapped + ");
+ pw.print(stringifyKBSize(dmabufUnmapped));
+ pw.println(" unmapped)");
+ kernelUsed += totalExportedDmabuf;
+ }
+ final long totalDmabufHeapPool = Debug.getDmabufHeapPoolsSizeKb();
+ if (totalDmabufHeapPool >= 0) {
+ pw.print("DMA-BUF Heaps pool: ");
+ pw.println(stringifyKBSize(totalDmabufHeapPool));
+ }
}
final long gpuUsage = Debug.getGpuTotalUsageKb();
if (gpuUsage >= 0) {
pw.print(" GPU: "); pw.println(stringifyKBSize(gpuUsage));
}
+
+ /*
+ * Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of
+ * memInfo.getCachedSizeKb().
+ */
final long lostRAM = memInfo.getTotalSizeKb()
- (ss[INDEX_TOTAL_PSS] - ss[INDEX_TOTAL_SWAP_PSS])
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 8f11a5ab900b..3ff58729f807 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1512,15 +1512,29 @@ public class AppProfiler {
final long ionHeap = Debug.getIonHeapsSizeKb();
final long ionPool = Debug.getIonPoolsSizeKb();
if (ionHeap >= 0 && ionPool >= 0) {
- final long ionMapped = Debug.getIonMappedSizeKb();
- final long ionUnmapped = ionHeap - ionMapped;
memInfoBuilder.append(" ION: ");
memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
memInfoBuilder.append("\n");
// Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
// set on ION VMAs, therefore consider the entire ION heap as used kernel memory
kernelUsed += ionHeap;
+ } else {
+ final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();
+ if (totalExportedDmabuf >= 0) {
+ memInfoBuilder.append("DMA-BUF: ");
+ memInfoBuilder.append(stringifyKBSize(totalExportedDmabuf));
+ memInfoBuilder.append("\n");
+ kernelUsed += totalExportedDmabuf;
+ }
+
+ final long totalDmabufHeapPool = Debug.getDmabufHeapPoolsSizeKb();
+ if (totalDmabufHeapPool >= 0) {
+ memInfoBuilder.append("DMA-BUF Heaps pool: ");
+ memInfoBuilder.append(stringifyKBSize(totalDmabufHeapPool));
+ memInfoBuilder.append("\n");
+ }
}
+
final long gpuUsage = Debug.getGpuTotalUsageKb();
if (gpuUsage >= 0) {
memInfoBuilder.append(" GPU: ");
@@ -1531,6 +1545,11 @@ public class AppProfiler {
memInfoBuilder.append(stringifyKBSize(
totalPss - cachedPss + kernelUsed));
memInfoBuilder.append("\n");
+
+ /*
+ * Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of
+ * memInfo.getCachedSizeKb().
+ */
memInfoBuilder.append(" Lost RAM: ");
memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb()
- (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 773e3137c247..336a59576d8d 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -126,15 +126,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.replaceWith("?");
- private static final int MAX_LOW_POWER_STATS_SIZE = 4096;
+ private static final int MAX_LOW_POWER_STATS_SIZE = 8192;
private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000;
+ private static final String EMPTY = "Empty";
private final HandlerThread mHandlerThread;
private final Handler mHandler;
private final Object mLock = new Object();
+ private final Object mPowerStatsLock = new Object();
+ @GuardedBy("mPowerStatsLock")
private PowerStatsInternal mPowerStatsInternal = null;
+ @GuardedBy("mPowerStatsLock")
private Map<Integer, String> mEntityNames = new HashMap();
+ @GuardedBy("mPowerStatsLock")
private Map<Integer, Map<Integer, String>> mStateNames = new HashMap();
@GuardedBy("mStats")
@@ -172,13 +177,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub
};
private void populatePowerEntityMaps() {
- if (mPowerStatsInternal == null) {
- // PowerStatsInternal unavailable, don't bother populating maps.
- mEntityNames = null;
- mStateNames = null;
- return;
- }
-
PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo();
if (entities == null) {
return;
@@ -202,6 +200,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub
*/
@Override
public void fillLowPowerStats(RpmStats rpmStats) {
+ synchronized (mPowerStatsLock) {
+ if (mPowerStatsInternal == null || mEntityNames.isEmpty() || mStateNames.isEmpty()) {
+ return;
+ }
+ }
+
final StateResidencyResult[] results;
try {
results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
@@ -237,16 +241,22 @@ public final class BatteryStatsService extends IBatteryStats.Stub
@Override
public String getSubsystemLowPowerStats() {
+ synchronized (mPowerStatsLock) {
+ if (mPowerStatsInternal == null || mEntityNames.isEmpty() || mStateNames.isEmpty()) {
+ return EMPTY;
+ }
+ }
+
final StateResidencyResult[] results;
try {
results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
.get(POWER_STATS_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Slog.e(TAG, "Failed to getStateResidencyAsync", e);
- return "Empty";
+ return EMPTY;
}
- if (results.length == 0) return "Empty";
+ if (results.length == 0) return EMPTY;
int charsLeft = MAX_LOW_POWER_STATS_SIZE;
StringBuilder builder = new StringBuilder("SubsystemPowerState");
@@ -322,9 +332,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub
} catch (RemoteException e) {
Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
}
- mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
- if (mPowerStatsInternal != null) {
- populatePowerEntityMaps();
+
+ synchronized (mPowerStatsLock) {
+ mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
+ if (mPowerStatsInternal != null) {
+ populatePowerEntityMaps();
+ } else {
+ Slog.e(TAG, "Could not register PowerStatsInternal");
+ }
}
Watchdog.getInstance().addMonitor(this);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c18031fd6de6..7bdf43c9c744 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -538,6 +538,12 @@ public final class CachedAppOptimizer {
private static native int getBinderFreezeInfo(int pid);
/**
+ * Returns the path to be checked to verify whether the freezer is supported by this system.
+ * @return absolute path to the file
+ */
+ private static native String getFreezerCheckPath();
+
+ /**
* Determines whether the freezer is supported by this system
*/
public static boolean isFreezerSupported() {
@@ -545,11 +551,15 @@ public final class CachedAppOptimizer {
FileReader fr = null;
try {
- fr = new FileReader("/sys/fs/cgroup/uid_0/cgroup.freeze");
+ fr = new FileReader(getFreezerCheckPath());
char state = (char) fr.read();
if (state == '1' || state == '0') {
supported = true;
+ // This is a workaround after reverting the cgroup v2 uid/pid hierarchy due to
+ // http://b/179006802.
+ // TODO: remove once the uid/pid hierarchy is restored
+ enableFreezerInternal(true);
} else {
Slog.e(TAG_AM, "unexpected value in cgroup.freeze");
}
@@ -1149,7 +1159,7 @@ public final class CachedAppOptimizer {
}
return;
}
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG_AM, "Not freezing. Unable to check file locks for " + name + "(" + pid
+ "): " + e);
return;
@@ -1244,7 +1254,7 @@ public final class CachedAppOptimizer {
}
}
}
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
synchronized (mAm) {
synchronized (mProcLock) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index da76af800d3d..25b7add0a7d8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -16,9 +16,12 @@
package com.android.server.biometrics.sensors;
+import android.annotation.NonNull;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceEnrollFrame;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -174,4 +177,33 @@ public class ClientMonitorCallbackConverter {
mFingerprintServiceReceiver.onUdfpsPointerUp(sensorId);
}
}
+
+ // Face-specific callbacks for FaceManager only
+
+ /**
+ * Called each time a new frame is received during face authentication.
+ *
+ * @param frame Information about the current frame.
+ *
+ * @throws RemoteException If the binder call to {@link IFaceServiceReceiver} fails.
+ */
+ public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame)
+ throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onAuthenticationFrame(frame);
+ }
+ }
+
+ /**
+ * Called each time a new frame is received during face enrollment.
+ *
+ * @param frame Information about the current frame.
+ *
+ * @throws RemoteException If the binder call to {@link IFaceServiceReceiver} fails.
+ */
+ public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onEnrollmentFrame(frame);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index ce24e5efdc07..de571863dbd4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -69,6 +69,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates();
+ Slog.d(TAG, "Enumerate onClientFinished: " + clientMonitor + ", success: " + success);
+
if (!unknownHALTemplates.isEmpty()) {
Slog.w(TAG, "Adding " + unknownHALTemplates.size() + " templates for deletion");
}
@@ -90,6 +92,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
private final Callback mRemoveCallback = new Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
+ Slog.d(TAG, "Remove onClientFinished: " + clientMonitor + ", success: " + success);
mCallback.onClientFinished(InternalCleanupClient.this, success);
}
};
@@ -115,6 +118,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
}
private void startCleanupUnknownHalTemplates() {
+ Slog.d(TAG, "startCleanupUnknownHalTemplates, size: " + mUnknownHALTemplates.size());
+
UserTemplate template = mUnknownHALTemplates.get(0);
mUnknownHALTemplates.remove(template);
mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
@@ -138,6 +143,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
// Start enumeration. Removal will start if necessary, when enumeration is completed.
mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(),
getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId());
+
+ Slog.d(TAG, "Starting enumerate: " + mCurrentTask);
mCurrentTask.start(mEnumerateCallback);
}
@@ -165,6 +172,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
+ mCurrentTask.getClass().getSimpleName());
return;
}
+ Slog.d(TAG, "onEnumerated, remaining: " + remaining);
((EnumerateConsumer) mCurrentTask).onEnumerationResult(identifier, remaining);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 9d19fdf4868d..e3feb74248ec 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -82,6 +82,7 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
if (identifier == null) {
+ Slog.d(TAG, "Null identifier");
return;
}
Slog.v(TAG, "handleEnumeratedTemplate: " + identifier.getBiometricId());
@@ -103,6 +104,7 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
private void doTemplateCleanup() {
if (mEnrolledList == null) {
+ Slog.d(TAG, "Null enrolledList");
return;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index d2673d2969c9..897ebd719da4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -24,6 +24,8 @@ import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.face.AuthenticationFrame;
import android.hardware.biometrics.face.BaseFrame;
import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceEnrollFrame;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
import android.util.Slog;
@@ -117,6 +119,16 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
public void onChallengeInterruptFinished(int sensorId) {
}
+
+ @Override
+ public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
+
+ }
+
+ @Override
+ public void onEnrollmentFrame(FaceEnrollFrame frame) {
+
+ }
};
BiometricTestSessionImpl(@NonNull Context context, int sensorId,
@@ -183,7 +195,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFailed();
}
- // TODO(b/174619156): replace with notifyAuthenticationFrame and notifyEnrollmentFrame.
+ // TODO(b/178414967): replace with notifyAuthenticationFrame and notifyEnrollmentFrame.
@Override
public void notifyAcquired(int userId, int acquireInfo) {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
@@ -194,7 +206,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
AuthenticationFrame authenticationFrame = new AuthenticationFrame();
authenticationFrame.data = data;
- // TODO(b/174619156): Currently onAuthenticationFrame and onEnrollmentFrame are the same.
+ // TODO(b/178414967): Currently onAuthenticationFrame and onEnrollmentFrame are the same.
// This will need to call the correct callback once the onAcquired callback is removed.
mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFrame(
authenticationFrame);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 30577667e5e4..8f554028ebfd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -29,7 +29,6 @@ import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.FaceAuthenticationFrame;
-import android.hardware.face.FaceDataFrame;
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.RemoteException;
@@ -179,19 +178,16 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
return isBiometricPrompt() ? mBiometricPromptIgnoreListVendor : mKeyguardIgnoreListVendor;
}
- private boolean shouldSend(int acquireInfo, int vendorCode) {
- if (acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR) {
- return !Utils.listContains(getAcquireVendorIgnorelist(), vendorCode);
- } else {
- return !Utils.listContains(getAcquireIgnorelist(), acquireInfo);
- }
+ private boolean shouldSendAcquiredMessage(int acquireInfo, int vendorCode) {
+ return acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR
+ ? !Utils.listContains(getAcquireVendorIgnorelist(), vendorCode)
+ : !Utils.listContains(getAcquireIgnorelist(), acquireInfo);
}
@Override
public void onAcquired(int acquireInfo, int vendorCode) {
mLastAcquire = acquireInfo;
-
- final boolean shouldSend = shouldSend(acquireInfo, vendorCode);
+ final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
}
@@ -201,9 +197,21 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
* @param frame Information about the current frame.
*/
public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame) {
- // TODO(b/178414967): Send additional frame data to the client callback.
- final FaceDataFrame data = frame.getData();
- onAcquired(data.getAcquiredInfo(), data.getVendorCode());
+ // Log acquisition but don't send it to the client yet, since that's handled below.
+ final int acquireInfo = frame.getData().getAcquiredInfo();
+ final int vendorCode = frame.getData().getVendorCode();
+ mLastAcquire = acquireInfo;
+ onAcquiredInternal(acquireInfo, vendorCode, false /* shouldSend */);
+
+ final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
+ if (shouldSend && getListener() != null) {
+ try {
+ getListener().onAuthenticationFrame(frame);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to send authentication frame", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
}
@Override public void onLockoutTimed(long durationMillis) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index da657b96afd5..898d81b0c8c4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -27,7 +27,6 @@ import android.hardware.biometrics.face.Feature;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
-import android.hardware.face.FaceDataFrame;
import android.hardware.face.FaceEnrollFrame;
import android.hardware.face.FaceManager;
import android.os.IBinder;
@@ -101,14 +100,15 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
getTargetUserId()).size() >= mMaxTemplatesPerUser;
}
+ private boolean shouldSendAcquiredMessage(int acquireInfo, int vendorCode) {
+ return acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR
+ ? !Utils.listContains(mEnrollIgnoreListVendor, vendorCode)
+ : !Utils.listContains(mEnrollIgnoreList, acquireInfo);
+ }
+
@Override
public void onAcquired(int acquireInfo, int vendorCode) {
- final boolean shouldSend;
- if (acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR) {
- shouldSend = !Utils.listContains(mEnrollIgnoreListVendor, vendorCode);
- } else {
- shouldSend = !Utils.listContains(mEnrollIgnoreList, acquireInfo);
- }
+ final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
}
@@ -118,9 +118,20 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
* @param frame Information about the current frame.
*/
public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) {
- // TODO(b/178414967): Send additional frame data to the client callback.
- final FaceDataFrame data = frame.getData();
- onAcquired(data.getAcquiredInfo(), data.getVendorCode());
+ // Log acquisition but don't send it to the client yet, since that's handled below.
+ final int acquireInfo = frame.getData().getAcquiredInfo();
+ final int vendorCode = frame.getData().getVendorCode();
+ onAcquiredInternal(acquireInfo, vendorCode, false /* shouldSend */);
+
+ final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
+ if (shouldSend && getListener() != null) {
+ try {
+ getListener().onEnrollmentFrame(frame);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to send enrollment frame", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index 4142a52c9253..d519d60881c0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -22,6 +22,8 @@ import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceEnrollFrame;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
import android.util.Slog;
@@ -106,6 +108,16 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
public void onChallengeInterruptFinished(int sensorId) {
}
+
+ @Override
+ public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
+
+ }
+
+ @Override
+ public void onEnrollmentFrame(FaceEnrollFrame frame) {
+
+ }
};
BiometricTestSessionImpl(@NonNull Context context, int sensorId, @NonNull Face10 face10,
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index ea759bf500dd..28dc5167487d 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -79,6 +79,7 @@ import android.os.UserHandle;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.stats.location.LocationStatsEnums;
+import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -87,6 +88,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.geofence.GeofenceManager;
import com.android.server.location.geofence.GeofenceProxy;
import com.android.server.location.gnss.GnssConfiguration;
@@ -98,7 +100,6 @@ import com.android.server.location.injector.AppOpsHelper;
import com.android.server.location.injector.EmergencyHelper;
import com.android.server.location.injector.Injector;
import com.android.server.location.injector.LocationAttributionHelper;
-import com.android.server.location.injector.LocationEventLog;
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationPowerSaveModeHelper;
import com.android.server.location.injector.LocationUsageLogger;
@@ -147,9 +148,10 @@ public class LocationManagerService extends ILocationManager.Stub {
public Lifecycle(Context context) {
super(context);
+ LocationEventLog eventLog = new LocationEventLog();
mUserInfoHelper = new LifecycleUserInfoHelper(context);
- mSystemInjector = new SystemInjector(context, mUserInfoHelper);
- mService = new LocationManagerService(context, mSystemInjector);
+ mSystemInjector = new SystemInjector(context, mUserInfoHelper, eventLog);
+ mService = new LocationManagerService(context, mSystemInjector, eventLog);
}
@Override
@@ -159,7 +161,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// client caching behavior is only enabled after seeing the first invalidate
LocationManager.invalidateLocalLocationEnabledCaches();
// disable caching for our own process
- Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class))
+ Objects.requireNonNull(getContext().getSystemService(LocationManager.class))
.disableLocalLocationEnabledCaches();
}
@@ -221,6 +223,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private final Context mContext;
private final Injector mInjector;
+ private final LocationEventLog mEventLog;
private final LocalService mLocalService;
private final GeofenceManager mGeofenceManager;
@@ -245,10 +248,10 @@ public class LocationManagerService extends ILocationManager.Stub {
private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
new CopyOnWriteArrayList<>();
- LocationManagerService(Context context, Injector injector) {
+ LocationManagerService(Context context, Injector injector, LocationEventLog eventLog) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mInjector = injector;
-
+ mEventLog = eventLog;
mLocalService = new LocalService();
LocalServices.addService(LocationManagerInternal.class, mLocalService);
@@ -256,7 +259,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// set up passive provider first since it will be required for all other location providers,
// which are loaded later once the system is ready.
- mPassiveManager = new PassiveLocationProviderManager(mContext, injector);
+ mPassiveManager = new PassiveLocationProviderManager(mContext, injector, mEventLog);
addLocationProviderManager(mPassiveManager, new PassiveLocationProvider(mContext));
// TODO: load the gps provider here as well, which will require refactoring
@@ -297,7 +300,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
LocationProviderManager manager = new LocationProviderManager(mContext, mInjector,
- providerName, mPassiveManager);
+ mEventLog, providerName, mPassiveManager);
addLocationProviderManager(manager, null);
return manager;
}
@@ -341,7 +344,7 @@ public class LocationManagerService extends ILocationManager.Stub {
com.android.internal.R.string.config_networkLocationProviderPackageName);
if (networkProvider != null) {
LocationProviderManager networkManager = new LocationProviderManager(mContext,
- mInjector, NETWORK_PROVIDER, mPassiveManager);
+ mInjector, mEventLog, NETWORK_PROVIDER, mPassiveManager);
addLocationProviderManager(networkManager, networkProvider);
} else {
Log.w(TAG, "no network location provider found");
@@ -360,7 +363,7 @@ public class LocationManagerService extends ILocationManager.Stub {
com.android.internal.R.string.config_fusedLocationProviderPackageName);
if (fusedProvider != null) {
LocationProviderManager fusedManager = new LocationProviderManager(mContext, mInjector,
- FUSED_PROVIDER, mPassiveManager);
+ mEventLog, FUSED_PROVIDER, mPassiveManager);
addLocationProviderManager(fusedManager, fusedProvider);
} else {
Log.wtf(TAG, "no fused location provider found");
@@ -375,7 +378,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mGnssManagerService.onSystemReady();
LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector,
- GPS_PROVIDER, mPassiveManager);
+ mEventLog, GPS_PROVIDER, mPassiveManager);
addLocationProviderManager(gnssManager, mGnssManagerService.getGnssLocationProvider());
}
@@ -431,7 +434,7 @@ public class LocationManagerService extends ILocationManager.Stub {
Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
}
- mInjector.getLocationEventLog().logLocationEnabled(userId, enabled);
+ mEventLog.logLocationEnabled(userId, enabled);
Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
@@ -1193,9 +1196,27 @@ public class LocationManagerService extends ILocationManager.Stub {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- if (mGnssManagerService != null && args.length > 0 && args[0].equals("--gnssmetrics")) {
- mGnssManagerService.dump(fd, ipw, args);
- return;
+ if (args.length > 0) {
+ LocationProviderManager manager = getLocationProviderManager(args[0]);
+ if (manager != null) {
+ ipw.println("Provider:");
+ ipw.increaseIndent();
+ manager.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+
+ ipw.println("Event Log:");
+ ipw.increaseIndent();
+ mEventLog.iterate(manager.getName(), ipw::println);
+ ipw.decreaseIndent();
+ return;
+ }
+
+ if ("--gnssmetrics".equals(args[0])) {
+ if (mGnssManagerService != null) {
+ mGnssManagerService.dump(fd, ipw, args);
+ }
+ return;
+ }
}
ipw.println("Location Manager State:");
@@ -1227,6 +1248,25 @@ public class LocationManagerService extends ILocationManager.Stub {
}
ipw.decreaseIndent();
+ ipw.println("Historical Aggregate Location Provider Data:");
+ ipw.increaseIndent();
+ ArrayMap<String, ArrayMap<String, LocationEventLog.AggregateStats>> aggregateStats =
+ mEventLog.copyAggregateStats();
+ for (int i = 0; i < aggregateStats.size(); i++) {
+ ipw.println(aggregateStats.keyAt(i));
+ ipw.increaseIndent();
+ ArrayMap<String, LocationEventLog.AggregateStats> providerStats =
+ aggregateStats.valueAt(i);
+ for (int j = 0; j < providerStats.size(); j++) {
+ ipw.print(providerStats.keyAt(j));
+ ipw.print(": ");
+ providerStats.valueAt(j).updateTotals();
+ ipw.println(providerStats.valueAt(j));
+ }
+ ipw.decreaseIndent();
+ }
+ ipw.decreaseIndent();
+
if (mGnssManagerService != null) {
ipw.println("GNSS Manager:");
ipw.increaseIndent();
@@ -1241,7 +1281,7 @@ public class LocationManagerService extends ILocationManager.Stub {
ipw.println("Event Log:");
ipw.increaseIndent();
- mInjector.getLocationEventLog().iterate(ipw::println);
+ mEventLog.iterate(ipw::println);
ipw.decreaseIndent();
}
@@ -1320,7 +1360,6 @@ public class LocationManagerService extends ILocationManager.Stub {
private final Context mContext;
private final UserInfoHelper mUserInfoHelper;
- private final LocationEventLog mLocationEventLog;
private final AlarmHelper mAlarmHelper;
private final SystemAppOpsHelper mAppOpsHelper;
private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
@@ -1339,19 +1378,17 @@ public class LocationManagerService extends ILocationManager.Stub {
@GuardedBy("this")
private boolean mSystemReady;
- SystemInjector(Context context, UserInfoHelper userInfoHelper) {
+ SystemInjector(Context context, UserInfoHelper userInfoHelper, LocationEventLog eventLog) {
mContext = context;
mUserInfoHelper = userInfoHelper;
- mLocationEventLog = new LocationEventLog();
mAlarmHelper = new SystemAlarmHelper(context);
mAppOpsHelper = new SystemAppOpsHelper(context);
mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
mAppOpsHelper);
mSettingsHelper = new SystemSettingsHelper(context);
mAppForegroundHelper = new SystemAppForegroundHelper(context);
- mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context,
- mLocationEventLog);
+ mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context, eventLog);
mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
mLocationUsageLogger = new LocationUsageLogger();
@@ -1430,11 +1467,6 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public LocationEventLog getLocationEventLog() {
- return mLocationEventLog;
- }
-
- @Override
public LocationUsageLogger getLocationUsageLogger() {
return mLocationUsageLogger;
}
diff --git a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index b5746bbf310a..12dd3e6a4a4c 100644
--- a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
@@ -16,12 +16,13 @@
package com.android.server.location.eventlog;
+import android.annotation.Nullable;
import android.os.SystemClock;
import android.util.TimeUtils;
import com.android.internal.util.Preconditions;
-import java.util.ListIterator;
+import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
@@ -35,6 +36,7 @@ public abstract class LocalEventLog {
boolean isFiller();
long getTimeDeltaMs();
String getLogString();
+ boolean filter(@Nullable String filter);
}
private static final class FillerEvent implements Log {
@@ -62,6 +64,11 @@ public abstract class LocalEventLog {
public String getLogString() {
throw new AssertionError();
}
+
+ @Override
+ public boolean filter(String filter) {
+ return false;
+ }
}
/**
@@ -87,6 +94,11 @@ public abstract class LocalEventLog {
public final long getTimeDeltaMs() {
return Integer.toUnsignedLong(mTimeDelta);
}
+
+ @Override
+ public boolean filter(String filter) {
+ return false;
+ }
}
// circular buffer of log entries
@@ -198,6 +210,17 @@ public abstract class LocalEventLog {
}
}
+ /**
+ * Iterates over the event log, passing each filter-matching log string to the given
+ * consumer.
+ */
+ public synchronized void iterate(String filter, Consumer<String> consumer) {
+ LogIterator it = new LogIterator(filter);
+ while (it.hasNext()) {
+ consumer.accept(it.next());
+ }
+ }
+
// returns the index of the first element
private int startIndex() {
return wrapIndex(mLogEndIndex - mLogSize);
@@ -205,12 +228,13 @@ public abstract class LocalEventLog {
// returns the index after this one
private int incrementIndex(int index) {
- return wrapIndex(index + 1);
- }
-
- // returns the index before this one
- private int decrementIndex(int index) {
- return wrapIndex(index - 1);
+ if (index == -1) {
+ return startIndex();
+ } else if (index >= 0) {
+ return wrapIndex(index + 1);
+ } else {
+ throw new IllegalArgumentException();
+ }
}
// rolls over the given index if necessary
@@ -219,7 +243,9 @@ public abstract class LocalEventLog {
return (index % mLog.length + mLog.length) % mLog.length;
}
- private class LogIterator implements ListIterator<String> {
+ private class LogIterator implements Iterator<String> {
+
+ private final @Nullable String mFilter;
private final long mSystemTimeDeltaMs;
@@ -228,10 +254,17 @@ public abstract class LocalEventLog {
private int mCount;
LogIterator() {
+ this(null);
+ }
+
+ LogIterator(@Nullable String filter) {
+ mFilter = filter;
mSystemTimeDeltaMs = System.currentTimeMillis() - SystemClock.elapsedRealtime();
mCurrentRealtimeMs = mStartRealtimeMs;
- mIndex = startIndex();
- mCount = 0;
+ mIndex = -1;
+ mCount = -1;
+
+ increment();
}
@Override
@@ -239,75 +272,17 @@ public abstract class LocalEventLog {
return mCount < mLogSize;
}
- @Override
- public boolean hasPrevious() {
- return mCount > 0;
- }
-
- @Override
- // return then increment
public String next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Log log = mLog[mIndex];
- long nextDeltaMs = log.getTimeDeltaMs();
- long realtimeMs = mCurrentRealtimeMs + nextDeltaMs;
-
- // calculate next index, skipping filler events
- do {
- mCurrentRealtimeMs += nextDeltaMs;
- mIndex = incrementIndex(mIndex);
- if (++mCount < mLogSize) {
- nextDeltaMs = mLog[mIndex].getTimeDeltaMs();
- }
- } while (mCount < mLogSize && mLog[mIndex].isFiller());
-
- return getTimePrefix(realtimeMs + mSystemTimeDeltaMs) + log.getLogString();
- }
-
- @Override
- // decrement then return
- public String previous() {
- Log log;
- long currentDeltaMs;
- long realtimeMs;
-
- // calculate previous index, skipping filler events with MAX_TIME_DELTA
- do {
- if (!hasPrevious()) {
- throw new NoSuchElementException();
- }
-
- mIndex = decrementIndex(mIndex);
- mCount--;
-
- log = mLog[mIndex];
- realtimeMs = mCurrentRealtimeMs;
-
- if (mCount > 0) {
- currentDeltaMs = log.getTimeDeltaMs();
- mCurrentRealtimeMs -= currentDeltaMs;
- }
- } while (mCount >= 0 && log.isFiller());
-
- return getTimePrefix(realtimeMs + mSystemTimeDeltaMs) + log.getLogString();
- }
-
- @Override
- public int nextIndex() {
- throw new UnsupportedOperationException();
- }
+ long timeMs = mCurrentRealtimeMs + log.getTimeDeltaMs() + mSystemTimeDeltaMs;
- @Override
- public int previousIndex() {
- throw new UnsupportedOperationException();
- }
+ increment();
- @Override
- public void add(String s) {
- throw new UnsupportedOperationException();
+ return getTimePrefix(timeMs) + log.getLogString();
}
@Override
@@ -315,9 +290,16 @@ public abstract class LocalEventLog {
throw new UnsupportedOperationException();
}
- @Override
- public void set(String s) {
- throw new UnsupportedOperationException();
+ private void increment() {
+ long nextDeltaMs = mIndex == -1 ? 0 : mLog[mIndex].getTimeDeltaMs();
+ do {
+ mCurrentRealtimeMs += nextDeltaMs;
+ mIndex = incrementIndex(mIndex);
+ if (++mCount < mLogSize) {
+ nextDeltaMs = mLog[mIndex].getTimeDeltaMs();
+ }
+ } while (mCount < mLogSize && (mLog[mIndex].isFiller() || (mFilter != null
+ && !mLog[mIndex].filter(mFilter))));
}
}
}
diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index 8d73518bced1..67060fc2c082 100644
--- a/services/core/java/com/android/server/location/injector/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,24 +14,32 @@
* limitations under the License.
*/
-package com.android.server.location.injector;
+package com.android.server.location.eventlog;
import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+import static android.util.TimeUtils.formatDuration;
import static com.android.server.location.LocationManagerService.D;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
import android.annotation.Nullable;
import android.location.LocationRequest;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Build;
import android.os.PowerManager.LocationPowerSaveMode;
+import android.os.SystemClock;
+import android.util.ArrayMap;
-import com.android.server.location.eventlog.LocalEventLog;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
/** In memory event log for location events. */
public class LocationEventLog extends LocalEventLog {
@@ -54,8 +62,39 @@ public class LocationEventLog extends LocalEventLog {
private static final int EVENT_PROVIDER_DELIVER_LOCATION = 8;
private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 9;
+ @GuardedBy("mAggregateStats")
+ private final ArrayMap<String, ArrayMap<String, AggregateStats>> mAggregateStats;
+
public LocationEventLog() {
super(getLogSize());
+ mAggregateStats = new ArrayMap<>(4);
+ }
+
+ public ArrayMap<String, ArrayMap<String, AggregateStats>> copyAggregateStats() {
+ synchronized (mAggregateStats) {
+ ArrayMap<String, ArrayMap<String, AggregateStats>> copy = new ArrayMap<>(
+ mAggregateStats);
+ for (int i = 0; i < copy.size(); i++) {
+ copy.setValueAt(i, new ArrayMap<>(copy.valueAt(i)));
+ }
+ return copy;
+ }
+ }
+
+ private AggregateStats getAggregateStats(String provider, String packageName) {
+ synchronized (mAggregateStats) {
+ ArrayMap<String, AggregateStats> packageMap = mAggregateStats.get(provider);
+ if (packageMap == null) {
+ packageMap = new ArrayMap<>(2);
+ mAggregateStats.put(provider, packageMap);
+ }
+ AggregateStats stats = packageMap.get(packageName);
+ if (stats == null) {
+ stats = new AggregateStats();
+ packageMap.put(packageName, stats);
+ }
+ return stats;
+ }
}
/** Logs a location enabled/disabled event. */
@@ -77,12 +116,34 @@ public class LocationEventLog extends LocalEventLog {
public void logProviderClientRegistered(String provider, CallerIdentity identity,
LocationRequest request) {
addLogEvent(EVENT_PROVIDER_REGISTER_CLIENT, provider, identity, request);
+ getAggregateStats(provider, identity.getPackageName())
+ .markRequestAdded(request.getIntervalMillis());
}
/** Logs a client unregistration for a location provider. */
- public void logProviderClientUnregistered(String provider,
- CallerIdentity identity) {
+ public void logProviderClientUnregistered(String provider, CallerIdentity identity) {
addLogEvent(EVENT_PROVIDER_UNREGISTER_CLIENT, provider, identity);
+ getAggregateStats(provider, identity.getPackageName()).markRequestRemoved();
+ }
+
+ /** Logs a client for a location provider entering the active state. */
+ public void logProviderClientActive(String provider, CallerIdentity identity) {
+ getAggregateStats(provider, identity.getPackageName()).markRequestActive();
+ }
+
+ /** Logs a client for a location provider leaving the active state. */
+ public void logProviderClientInactive(String provider, CallerIdentity identity) {
+ getAggregateStats(provider, identity.getPackageName()).markRequestInactive();
+ }
+
+ /** Logs a client for a location provider entering the foreground state. */
+ public void logProviderClientForeground(String provider, CallerIdentity identity) {
+ getAggregateStats(provider, identity.getPackageName()).markRequestForeground();
+ }
+
+ /** Logs a client for a location provider leaving the foreground state. */
+ public void logProviderClientBackground(String provider, CallerIdentity identity) {
+ getAggregateStats(provider, identity.getPackageName()).markRequestBackground();
}
/** Logs a change to the provider request for a location provider. */
@@ -143,16 +204,29 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderEnabledEvent extends LogEvent {
+ private abstract static class ProviderEvent extends LogEvent {
+
+ protected final String mProvider;
+
+ protected ProviderEvent(long timeDelta, String provider) {
+ super(timeDelta);
+ mProvider = provider;
+ }
+
+ @Override
+ public boolean filter(String filter) {
+ return mProvider.equals(filter);
+ }
+ }
+
+ private static final class ProviderEnabledEvent extends ProviderEvent {
- private final String mProvider;
private final int mUserId;
private final boolean mEnabled;
protected ProviderEnabledEvent(long timeDelta, String provider, int userId,
boolean enabled) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mUserId = userId;
mEnabled = enabled;
}
@@ -164,14 +238,12 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderMockedEvent extends LogEvent {
+ private static final class ProviderMockedEvent extends ProviderEvent {
- private final String mProvider;
private final boolean mMocked;
protected ProviderMockedEvent(long timeDelta, String provider, boolean mocked) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mMocked = mocked;
}
@@ -185,17 +257,15 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderRegisterEvent extends LogEvent {
+ private static final class ProviderRegisterEvent extends ProviderEvent {
- private final String mProvider;
private final boolean mRegistered;
private final CallerIdentity mIdentity;
@Nullable private final LocationRequest mLocationRequest;
private ProviderRegisterEvent(long timeDelta, String provider, boolean registered,
CallerIdentity identity, @Nullable LocationRequest locationRequest) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mRegistered = registered;
mIdentity = identity;
mLocationRequest = locationRequest;
@@ -212,14 +282,12 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderUpdateEvent extends LogEvent {
+ private static final class ProviderUpdateEvent extends ProviderEvent {
- private final String mProvider;
private final ProviderRequest mRequest;
private ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mRequest = request;
}
@@ -229,14 +297,12 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderReceiveLocationEvent extends LogEvent {
+ private static final class ProviderReceiveLocationEvent extends ProviderEvent {
- private final String mProvider;
private final int mNumLocations;
private ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mNumLocations = numLocations;
}
@@ -246,16 +312,14 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderDeliverLocationEvent extends LogEvent {
+ private static final class ProviderDeliverLocationEvent extends ProviderEvent {
- private final String mProvider;
private final int mNumLocations;
@Nullable private final CallerIdentity mIdentity;
private ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations,
@Nullable CallerIdentity identity) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mNumLocations = numLocations;
mIdentity = identity;
}
@@ -267,7 +331,7 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class LocationPowerSaveModeEvent extends LogEvent {
+ private static final class LocationPowerSaveModeEvent extends LogEvent {
@LocationPowerSaveMode
private final int mLocationPowerSaveMode;
@@ -305,7 +369,7 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class LocationEnabledEvent extends LogEvent {
+ private static final class LocationEnabledEvent extends LogEvent {
private final int mUserId;
private final boolean mEnabled;
@@ -321,4 +385,118 @@ public class LocationEventLog extends LocalEventLog {
return "[u" + mUserId + "] location setting " + (mEnabled ? "enabled" : "disabled");
}
}
+
+ /**
+ * Aggregate statistics for a single package under a single provider.
+ */
+ public static final class AggregateStats {
+
+ @GuardedBy("this")
+ private int mAddedRequestCount;
+ @GuardedBy("this")
+ private int mActiveRequestCount;
+ @GuardedBy("this")
+ private int mForegroundRequestCount;
+
+ @GuardedBy("this")
+ private long mFastestIntervalMs = Long.MAX_VALUE;
+ @GuardedBy("this")
+ private long mSlowestIntervalMs = 0;
+
+ @GuardedBy("this")
+ private long mAddedTimeTotalMs;
+ @GuardedBy("this")
+ private long mAddedTimeLastUpdateRealtimeMs;
+
+ @GuardedBy("this")
+ private long mActiveTimeTotalMs;
+ @GuardedBy("this")
+ private long mActiveTimeLastUpdateRealtimeMs;
+
+ @GuardedBy("this")
+ private long mForegroundTimeTotalMs;
+ @GuardedBy("this")
+ private long mForegroundTimeLastUpdateRealtimeMs;
+
+ AggregateStats() {}
+
+ synchronized void markRequestAdded(long intervalMillis) {
+ if (mAddedRequestCount++ == 0) {
+ mAddedTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
+ }
+
+ mFastestIntervalMs = min(intervalMillis, mFastestIntervalMs);
+ mSlowestIntervalMs = max(intervalMillis, mSlowestIntervalMs);
+ }
+
+ synchronized void markRequestRemoved() {
+ updateTotals();
+ --mAddedRequestCount;
+ Preconditions.checkState(mAddedRequestCount >= 0);
+
+ mActiveRequestCount = min(mAddedRequestCount, mActiveRequestCount);
+ mForegroundRequestCount = min(mAddedRequestCount, mForegroundRequestCount);
+ }
+
+ synchronized void markRequestActive() {
+ Preconditions.checkState(mAddedRequestCount > 0);
+ if (mActiveRequestCount++ == 0) {
+ mActiveTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
+ }
+ }
+
+ synchronized void markRequestInactive() {
+ updateTotals();
+ --mActiveRequestCount;
+ Preconditions.checkState(mActiveRequestCount >= 0);
+ }
+
+ synchronized void markRequestForeground() {
+ Preconditions.checkState(mAddedRequestCount > 0);
+ if (mForegroundRequestCount++ == 0) {
+ mForegroundTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
+ }
+ }
+
+ synchronized void markRequestBackground() {
+ updateTotals();
+ --mForegroundRequestCount;
+ Preconditions.checkState(mForegroundRequestCount >= 0);
+ }
+
+ public synchronized void updateTotals() {
+ if (mAddedRequestCount > 0) {
+ long realtimeMs = SystemClock.elapsedRealtime();
+ mAddedTimeTotalMs += realtimeMs - mAddedTimeLastUpdateRealtimeMs;
+ mAddedTimeLastUpdateRealtimeMs = realtimeMs;
+ }
+ if (mActiveRequestCount > 0) {
+ long realtimeMs = SystemClock.elapsedRealtime();
+ mActiveTimeTotalMs += realtimeMs - mActiveTimeLastUpdateRealtimeMs;
+ mActiveTimeLastUpdateRealtimeMs = realtimeMs;
+ }
+ if (mForegroundRequestCount > 0) {
+ long realtimeMs = SystemClock.elapsedRealtime();
+ mForegroundTimeTotalMs += realtimeMs - mForegroundTimeLastUpdateRealtimeMs;
+ mForegroundTimeLastUpdateRealtimeMs = realtimeMs;
+ }
+ }
+
+ @Override
+ public synchronized String toString() {
+ return "min/max interval = " + intervalToString(mFastestIntervalMs) + "/"
+ + intervalToString(mSlowestIntervalMs)
+ + ", total/active/foreground duration = " + formatDuration(mAddedTimeTotalMs)
+ + "/" + formatDuration(mActiveTimeTotalMs) + "/"
+ + formatDuration(mForegroundTimeTotalMs);
+ }
+
+ private static String intervalToString(long intervalMs) {
+ if (intervalMs == LocationRequest.PASSIVE_INTERVAL) {
+ return "passive";
+ } else {
+ return MILLISECONDS.toSeconds(intervalMs) + "s";
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index 03938b2b8ba2..0e157c22a32b 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -56,7 +56,4 @@ public interface Injector {
/** Returns a LocationUsageLogger. */
LocationUsageLogger getLocationUsageLogger();
-
- /** Returns a LocationEventLog. */
- LocationEventLog getLocationEventLog();
}
diff --git a/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
index 532826a02ab0..cc00d5684991 100644
--- a/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
@@ -24,6 +24,8 @@ import static com.android.server.location.LocationManagerService.TAG;
import android.os.PowerManager.LocationPowerSaveMode;
import android.util.Log;
+import com.android.server.location.eventlog.LocationEventLog;
+
import java.util.concurrent.CopyOnWriteArrayList;
/**
diff --git a/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
index 1b74865b268e..c47a64d6d9a7 100644
--- a/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
@@ -25,6 +25,7 @@ import android.os.PowerSaveState;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.location.eventlog.LocationEventLog;
import java.util.Objects;
import java.util.function.Consumer;
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 48a012e57a02..388b5a4dd54e 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -89,6 +89,7 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.location.LocationPermissions;
import com.android.server.location.LocationPermissions.PermissionLevel;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.fudger.LocationFudger;
import com.android.server.location.injector.AlarmHelper;
import com.android.server.location.injector.AppForegroundHelper;
@@ -96,7 +97,6 @@ import com.android.server.location.injector.AppForegroundHelper.AppForegroundLis
import com.android.server.location.injector.AppOpsHelper;
import com.android.server.location.injector.Injector;
import com.android.server.location.injector.LocationAttributionHelper;
-import com.android.server.location.injector.LocationEventLog;
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationPermissionsHelper.LocationPermissionsListener;
import com.android.server.location.injector.LocationPowerSaveModeHelper;
@@ -323,7 +323,7 @@ public class LocationProviderManager extends
+ getRequest());
}
- mLocationEventLog.logProviderClientRegistered(mName, getIdentity(), super.getRequest());
+ mEventLog.logProviderClientRegistered(mName, getIdentity(), super.getRequest());
// initialization order is important as there are ordering dependencies
mPermitted = mLocationPermissionsHelper.hasLocationPermissions(mPermissionLevel,
@@ -333,6 +333,10 @@ public class LocationProviderManager extends
mIsUsingHighPower = isUsingHighPower();
onProviderListenerRegister();
+
+ if (mForeground) {
+ mEventLog.logProviderClientForeground(mName, getIdentity());
+ }
}
@GuardedBy("mLock")
@@ -344,7 +348,7 @@ public class LocationProviderManager extends
onProviderListenerUnregister();
- mLocationEventLog.logProviderClientUnregistered(mName, getIdentity());
+ mEventLog.logProviderClientUnregistered(mName, getIdentity());
if (D) {
Log.d(TAG, mName + " provider removed registration from " + getIdentity());
@@ -369,6 +373,8 @@ public class LocationProviderManager extends
Preconditions.checkState(Thread.holdsLock(mLock));
}
+ mEventLog.logProviderClientActive(mName, getIdentity());
+
if (!getRequest().isHiddenFromAppOps()) {
mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
}
@@ -389,6 +395,8 @@ public class LocationProviderManager extends
}
onProviderListenerInactive();
+
+ mEventLog.logProviderClientInactive(mName, getIdentity());
}
/**
@@ -524,6 +532,12 @@ public class LocationProviderManager extends
mForeground = foreground;
+ if (mForeground) {
+ mEventLog.logProviderClientForeground(mName, getIdentity());
+ } else {
+ mEventLog.logProviderClientBackground(mName, getIdentity());
+ }
+
// note that onProviderLocationRequestChanged() is always called
return onProviderLocationRequestChanged()
|| mLocationPowerSaveModeHelper.getLocationPowerSaveMode()
@@ -855,7 +869,7 @@ public class LocationProviderManager extends
listener.deliverOnLocationChanged(deliverLocationResult,
mUseWakeLock ? mWakeLock::release : null);
- mLocationEventLog.logProviderDeliveredLocations(mName, locationResult.size(),
+ mEventLog.logProviderDeliveredLocations(mName, locationResult.size(),
getIdentity());
}
@@ -1154,7 +1168,7 @@ public class LocationProviderManager extends
// we currently don't hold a wakelock for getCurrentLocation deliveries
listener.deliverOnLocationChanged(deliverLocationResult, null);
- mLocationEventLog.logProviderDeliveredLocations(mName,
+ mEventLog.logProviderDeliveredLocations(mName,
locationResult != null ? locationResult.size() : 0, getIdentity());
}
@@ -1223,6 +1237,7 @@ public class LocationProviderManager extends
private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners;
+ protected final LocationEventLog mEventLog;
protected final LocationManagerInternal mLocationManagerInternal;
protected final SettingsHelper mSettingsHelper;
protected final UserInfoHelper mUserHelper;
@@ -1235,7 +1250,6 @@ public class LocationProviderManager extends
protected final LocationAttributionHelper mLocationAttributionHelper;
protected final LocationUsageLogger mLocationUsageLogger;
protected final LocationFudger mLocationFudger;
- protected final LocationEventLog mLocationEventLog;
private final UserListener mUserChangedListener = this::onUserChanged;
private final UserSettingChangedListener mLocationEnabledChangedListener =
@@ -1273,8 +1287,8 @@ public class LocationProviderManager extends
@GuardedBy("mLock")
private @Nullable OnAlarmListener mDelayedRegister;
- public LocationProviderManager(Context context, Injector injector, String name,
- @Nullable PassiveLocationProviderManager passiveManager) {
+ public LocationProviderManager(Context context, Injector injector, LocationEventLog eventLog,
+ String name, @Nullable PassiveLocationProviderManager passiveManager) {
mContext = context;
mName = Objects.requireNonNull(name);
mPassiveManager = passiveManager;
@@ -1285,6 +1299,7 @@ public class LocationProviderManager extends
mEnabledListeners = new ArrayList<>();
mProviderRequestListeners = new CopyOnWriteArrayList<>();
+ mEventLog = eventLog;
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
mSettingsHelper = injector.getSettingsHelper();
@@ -1297,7 +1312,6 @@ public class LocationProviderManager extends
mScreenInteractiveHelper = injector.getScreenInteractiveHelper();
mLocationAttributionHelper = injector.getLocationAttributionHelper();
mLocationUsageLogger = injector.getLocationUsageLogger();
- mLocationEventLog = injector.getLocationEventLog();
mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
mProvider = new MockableLocationProvider(mLock);
@@ -1437,7 +1451,7 @@ public class LocationProviderManager extends
synchronized (mLock) {
Preconditions.checkState(mState != STATE_STOPPED);
- mLocationEventLog.logProviderMocked(mName, provider != null);
+ mEventLog.logProviderMocked(mName, provider != null);
final long identity = Binder.clearCallingIdentity();
try {
@@ -1925,7 +1939,7 @@ public class LocationProviderManager extends
@GuardedBy("mLock")
private void setProviderRequest(ProviderRequest request) {
- mLocationEventLog.logProviderUpdateRequest(mName, request);
+ mEventLog.logProviderUpdateRequest(mName, request);
mProvider.getController().setRequest(request);
FgThread.getHandler().post(() -> {
@@ -2261,7 +2275,7 @@ public class LocationProviderManager extends
}
// don't log location received for passive provider because it's spammy
- mLocationEventLog.logProviderReceivedLocations(mName, filtered.size());
+ mEventLog.logProviderReceivedLocations(mName, filtered.size());
} else {
// passive provider should get already filtered results as input
filtered = locationResult;
@@ -2361,7 +2375,7 @@ public class LocationProviderManager extends
if (D) {
Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled);
}
- mLocationEventLog.logProviderEnabled(mName, userId, enabled);
+ mEventLog.logProviderEnabled(mName, userId, enabled);
}
// clear last locations if we become disabled
diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
index b35af4f6475c..027f4e94f55b 100644
--- a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
@@ -24,6 +24,7 @@ import android.location.provider.ProviderRequest;
import android.os.Binder;
import com.android.internal.util.Preconditions;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.Injector;
import java.util.Collection;
@@ -33,8 +34,9 @@ import java.util.Collection;
*/
public class PassiveLocationProviderManager extends LocationProviderManager {
- public PassiveLocationProviderManager(Context context, Injector injector) {
- super(context, injector, LocationManager.PASSIVE_PROVIDER, null);
+ public PassiveLocationProviderManager(Context context, Injector injector,
+ LocationEventLog eventLog) {
+ super(context, injector, eventLog, LocationManager.PASSIVE_PROVIDER, null);
}
@Override
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 15e1d5281bfa..7bf704299373 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -59,6 +59,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.utils.Snappable;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
+import com.android.server.utils.Watched;
import com.android.server.utils.WatchedSparseArray;
import com.android.server.utils.WatchedSparseBooleanArray;
import com.android.server.utils.Watcher;
@@ -123,6 +124,7 @@ class InstantAppRegistry implements Watchable, Snappable {
private final CookiePersistence mCookiePersistence;
/** State for uninstalled instant apps */
+ @Watched
@GuardedBy("mService.mLock")
private final WatchedSparseArray<List<UninstalledInstantAppState>> mUninstalledInstantApps;
@@ -132,10 +134,12 @@ class InstantAppRegistry implements Watchable, Snappable {
* The value is a set of instant app UIDs.
* UserID -> TargetAppId -> InstantAppId
*/
+ @Watched
@GuardedBy("mService.mLock")
private final WatchedSparseArray<WatchedSparseArray<WatchedSparseBooleanArray>> mInstantGrants;
/** The set of all installed instant apps. UserID -> AppID */
+ @Watched
@GuardedBy("mService.mLock")
private final WatchedSparseArray<WatchedSparseBooleanArray> mInstalledInstantAppUids;
@@ -189,6 +193,7 @@ class InstantAppRegistry implements Watchable, Snappable {
mUninstalledInstantApps.registerObserver(mObserver);
mInstantGrants.registerObserver(mObserver);
mInstalledInstantAppUids.registerObserver(mObserver);
+ Watchable.verifyWatchedAttributes(this, mObserver);
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b725c9644ae0..0c143c97e1aa 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1749,6 +1749,11 @@ public class PackageManagerService extends IPackageManager.Stub
public AndroidPackage getPackage(@NonNull String packageName) {
return getPackageLocked(packageName);
}
+
+ @Override
+ public boolean filterAppAccess(String packageName, int callingUid, int userId) {
+ return mPmInternal.filterAppAccess(packageName, callingUid, userId);
+ }
}
/**
@@ -16157,8 +16162,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Deprecated
@Override
public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
- mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
- return true;
+ return mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
}
@Deprecated
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index e73486a45ac1..a8a6bcec2313 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2712,7 +2712,6 @@ public final class Settings implements Watchable, Snappable {
writeSigningKeySetLPr(serializer, pkg.keySetData);
writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
writeKeySetAliasesLPr(serializer, pkg.keySetData);
- mDomainVerificationManager.writeLegacySettings(serializer, pkg.name);
writeMimeGroupLPr(serializer, pkg.mimeGroups);
serializer.endTag(null, "package");
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
index c521f828ade9..275dd053fdde 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -18,8 +18,10 @@ package com.android.server.pm.verify.domain;
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Process;
@@ -30,10 +32,17 @@ public class DomainVerificationEnforcer {
@NonNull
private final Context mContext;
+ @NonNull
+ private Callback mCallback;
+
public DomainVerificationEnforcer(@NonNull Context context) {
mContext = context;
}
+ public void setCallback(@NonNull Callback callback) {
+ mCallback = callback;
+ }
+
/**
* Enforced when mutating any state from shell or internally in the system process.
*/
@@ -67,6 +76,11 @@ public class DomainVerificationEnforcer {
"Caller " + callingUid
+ " is not allowed to query domain verification state");
}
+
+ mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
+ Binder.getCallingPid(), callingUid,
+ "Caller " + callingUid + " does not hold "
+ + android.Manifest.permission.QUERY_ALL_PACKAGES);
break;
}
}
@@ -84,28 +98,42 @@ public class DomainVerificationEnforcer {
isAllowed = true;
break;
default:
- // TODO(b/159952358): Remove permission check? The component package should
- // have been checked when the verifier component was first scanned in PMS.
- mContext.enforcePermission(
- android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
- Binder.getCallingPid(), callingUid,
- "Caller " + callingUid + " does not hold DOMAIN_VERIFICATION_AGENT");
+ final int callingPid = Binder.getCallingPid();
+ boolean isLegacyVerificationAgent = false;
+ if (mContext.checkPermission(
+ android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, callingPid,
+ callingUid) != PackageManager.PERMISSION_GRANTED) {
+ isLegacyVerificationAgent = mContext.checkPermission(
+ android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+ callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+ if (!isLegacyVerificationAgent) {
+ throw new SecurityException("Caller " + callingUid + " does not hold "
+ + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT);
+ }
+ }
+
+ // If the caller isn't a legacy verifier, it needs the QUERY_ALL permission
+ if (!isLegacyVerificationAgent) {
+ mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
+ callingPid, callingUid, "Caller " + callingUid + " does not hold "
+ + android.Manifest.permission.QUERY_ALL_PACKAGES);
+ }
+
isAllowed = proxy.isCallerVerifier(callingUid);
break;
}
if (!isAllowed) {
throw new SecurityException("Caller " + callingUid
- + " is not the approved domain verification agent, isVerifier = "
- + proxy.isCallerVerifier(callingUid));
+ + " is not the approved domain verification agent");
}
}
/**
* Enforced when mutating user selection state inside an exposed API method.
*/
- public void assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
- @UserIdInt int targetUserId) throws SecurityException {
+ public boolean assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
+ @Nullable String packageName, @UserIdInt int targetUserId) throws SecurityException {
if (callingUserId != targetUserId) {
mContext.enforcePermission(
Manifest.permission.INTERACT_ACROSS_USERS,
@@ -117,12 +145,51 @@ public class DomainVerificationEnforcer {
android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
Binder.getCallingPid(), callingUid,
"Caller is not allowed to edit user selections");
+
+ if (packageName == null) {
+ return true;
+ }
+
+ return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
}
- public void callerIsLegacyUserSelector(int callingUid) {
+ public boolean callerIsLegacyUserSelector(int callingUid, @UserIdInt int callingUserId,
+ @NonNull String packageName, @UserIdInt int targetUserId) {
mContext.enforcePermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS,
Binder.getCallingPid(), callingUid,
"Caller is not allowed to edit user state");
+
+ if (callingUserId != targetUserId) {
+ if (mContext.checkPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS,
+ Binder.getCallingPid(), callingUid) != PackageManager.PERMISSION_GRANTED) {
+ // Legacy API did not enforce this, so for backwards compatibility, fail silently
+ return false;
+ }
+ }
+
+ return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
+ }
+
+ public boolean callerIsLegacyUserQuerent(int callingUid, @UserIdInt int callingUserId,
+ @NonNull String packageName, @UserIdInt int targetUserId) {
+ if (callingUserId != targetUserId) {
+ // The legacy API enforces the _FULL variant, so maintain that here
+ mContext.enforcePermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Binder.getCallingPid(), callingUid,
+ "Caller is not allowed to edit other users");
+ }
+
+ return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
+ }
+
+ public interface Callback {
+ /**
+ * @return true if access to the given package should be filtered and the method failed as
+ * if the package was not installed
+ */
+ boolean filterAppAccess(@NonNull String packageName, int callingUid, @UserIdInt int userId);
}
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 0474d78a3e53..50fd6e3ddea1 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -174,8 +174,10 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
* Set aside a legacy user selection that will be restored to a pending
* {@link DomainVerificationPkgState} once it's added through
* {@link #addPackage(PackageSetting)}.
+ *
+ * @return true if state changed successfully
*/
- void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state);
+ boolean setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state);
/**
* Until the legacy APIs are entirely removed, returns the legacy state from the previously
@@ -184,12 +186,6 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
int getLegacyState(@NonNull String packageName, @UserIdInt int userId);
/**
- * Serialize a legacy setting that wasn't attached yet.
- * TODO: Does this even matter? Should consider for removal.
- */
- void writeLegacySettings(TypedXmlSerializer serializer, String name);
-
- /**
* Print the verification state and user selection state of a package.
*
* @param packageName the package whose state to change, or all packages if none is
@@ -235,7 +231,8 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
throws IllegalArgumentException, NameNotFoundException;
- interface Connection extends Function<String, PackageSetting> {
+ interface Connection extends DomainVerificationEnforcer.Callback,
+ Function<String, PackageSetting> {
/**
* Notify that a settings change has been made and that eventually
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index e24e5bbfa4f6..fa0327414914 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -47,12 +47,12 @@ import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyUnavailable;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import org.xmlpull.v1.XmlPullParserException;
@@ -92,9 +92,9 @@ public class DomainVerificationService extends SystemService
* immediately attached once its available.
* <p>
* Generally this should be not accessed directly. Prefer calling {@link
- * #getAndValidateAttachedLocked(UUID, Set, boolean)}.
+ * #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer)}.
*
- * @see #getAndValidateAttachedLocked(UUID, Set, boolean)
+ * @see #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer)
**/
@GuardedBy("mLock")
@NonNull
@@ -160,6 +160,7 @@ public class DomainVerificationService extends SystemService
@Override
public void setConnection(@NonNull Connection connection) {
mConnection = connection;
+ mEnforcer.setCallback(mConnection);
}
@NonNull
@@ -285,7 +286,7 @@ public class DomainVerificationService extends SystemService
mEnforcer.assertApprovedVerifier(callingUid, mProxy);
synchronized (mLock) {
DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
- true /* forAutoVerify */);
+ true /* forAutoVerify */, callingUid, null /* userId */);
ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
for (String domain : domains) {
Integer previousState = stateMap.get(domain);
@@ -389,8 +390,10 @@ public class DomainVerificationService extends SystemService
public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
boolean allowed, @UserIdInt int userId) throws NameNotFoundException {
- mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
- mConnection.getCallingUserId(), userId);
+ if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), packageName, userId)) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
synchronized (mLock) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
if (pkgState == null) {
@@ -455,11 +458,18 @@ public class DomainVerificationService extends SystemService
public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
@NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
throws InvalidDomainSetException, NameNotFoundException {
- mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
- mConnection.getCallingUserId(), userId);
synchronized (mLock) {
+ final int callingUid = mConnection.getCallingUid();
+ // Pass null for package name here and do the app visibility enforcement inside
+ // getAndValidateAttachedLocked instead, since this has to fail with the same invalid
+ // ID reason if the target app is invisible
+ if (!mEnforcer.assertApprovedUserSelector(callingUid, mConnection.getCallingUserId(),
+ null /* packageName */, userId)) {
+ throw new InvalidDomainSetException(domainSetId, null,
+ InvalidDomainSetException.REASON_ID_INVALID);
+ }
DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
- false /* forAutoVerify */);
+ false /* forAutoVerify */, callingUid, userId);
DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
if (enabled) {
userState.addHosts(domains);
@@ -556,8 +566,10 @@ public class DomainVerificationService extends SystemService
@Override
public DomainVerificationUserSelection getDomainVerificationUserSelection(
@NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException {
- mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
- mConnection.getCallingUserId(), userId);
+ if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), packageName, userId)) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
synchronized (mLock) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
if (pkgState == null) {
@@ -844,23 +856,27 @@ public class DomainVerificationService extends SystemService
}
@Override
- public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) {
- mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid());
+ public boolean setLegacyUserState(@NonNull String packageName, @UserIdInt int userId,
+ int state) {
+ if (!mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), packageName, userId)) {
+ return false;
+ }
mLegacySettings.add(packageName, userId, state);
mConnection.scheduleWriteSettings();
+ return true;
}
@Override
public int getLegacyState(@NonNull String packageName, @UserIdInt int userId) {
+ if (!mEnforcer.callerIsLegacyUserQuerent(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), packageName, userId)) {
+ return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ }
return mLegacySettings.getUserState(packageName, userId);
}
@Override
- public void writeLegacySettings(TypedXmlSerializer serializer, String name) {
-
- }
-
- @Override
public void clearPackage(@NonNull String packageName) {
synchronized (mLock) {
mAttachedPkgStates.remove(packageName);
@@ -935,10 +951,14 @@ public class DomainVerificationService extends SystemService
* Validates parameters provided by an external caller. Checks that an ID is still live and that
* any provided domains are valid. Should be called at the beginning of each API that takes in a
* {@link UUID} domain set ID.
+ *
+ * @param userIdForFilter which user to filter app access to, or null if the caller has already
+ * validated package visibility
*/
@GuardedBy("mLock")
private DomainVerificationPkgState getAndValidateAttachedLocked(@NonNull UUID domainSetId,
- @NonNull Set<String> domains, boolean forAutoVerify)
+ @NonNull Set<String> domains, boolean forAutoVerify, int callingUid,
+ @Nullable Integer userIdForFilter)
throws InvalidDomainSetException, NameNotFoundException {
if (domainSetId == null) {
throw new InvalidDomainSetException(null, null,
@@ -952,6 +972,13 @@ public class DomainVerificationService extends SystemService
}
String pkgName = pkgState.getPackageName();
+
+ if (userIdForFilter != null
+ && mConnection.filterAppAccess(pkgName, callingUid, userIdForFilter)) {
+ throw new InvalidDomainSetException(domainSetId, null,
+ InvalidDomainSetException.REASON_ID_INVALID);
+ }
+
PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
throw DomainVerificationUtils.throwPackageUnavailable(pkgName);
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index 57cf986842da..e57d4ce9ec31 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -66,7 +66,7 @@ public class RotationResolverManagerService extends
private static final String KEY_SERVICE_ENABLED = "service_enabled";
/** Default value in absence of {@link DeviceConfig} override. */
- private static final boolean DEFAULT_SERVICE_ENABLED = false;
+ private static final boolean DEFAULT_SERVICE_ENABLED = true;
static final int ORIENTATION_UNKNOWN =
FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__UNKNOWN;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f97af6259c9c..f16a646d00a1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -788,7 +788,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final boolean sizeCompatFreeform = Settings.Global.getInt(
resolver, DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0;
final boolean supportsNonResizableMultiWindow = Settings.Global.getInt(
- resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) != 0;
+ resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0;
// Transfer any global setting for forcing RTL layout, into a System Property
DisplayProperties.debug_force_rtl(forceRtl);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ceebe9550846..bd93e045cdc7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -847,6 +847,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction();
+ // Send any pending task-info changes that were queued-up during a layout deferment
+ mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
mWmService.mSyncEngine.onSurfacePlacement();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
@@ -859,8 +861,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
- // Send any pending task-info changes that were queued-up during a layout deferment
- mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
checkAppTransitionReady(surfacePlacer);
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1f8daf6f361f..8b186796db8d 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -104,9 +104,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final boolean mCanAddInternalSystemWindow;
private final boolean mCanStartTasksFromRecents;
- // If non-system overlays from this process can be hidden by the user or app using
- // HIDE_NON_SYSTEM_OVERLAY_WINDOWS.
- final boolean mOverlaysCanBeHidden;
final boolean mCanCreateSystemApplicationOverlay;
final boolean mCanHideNonSystemOverlayWindows;
final boolean mCanAcquireSleepToken;
@@ -136,8 +133,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
== PERMISSION_GRANTED;
mCanStartTasksFromRecents = service.mContext.checkCallingOrSelfPermission(
START_TASKS_FROM_RECENTS) == PERMISSION_GRANTED;
- mOverlaysCanBeHidden = !mCanAddInternalSystemWindow
- && !mService.mAtmInternal.isCallerRecents(mUid);
mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
== PERMISSION_GRANTED;
mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
@@ -253,11 +248,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public void setTransparentRegion(IWindow window, Region region) {
- mService.setTransparentRegionWindow(this, window, region);
- }
-
- @Override
public void setInsets(IWindow window, int touchableInsets,
Rect contentInsets, Rect visibleInsets, Region touchableArea) {
mService.setInsetsWindow(this, window, touchableInsets, contentInsets,
@@ -682,7 +672,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
boolean changed;
- if (mOverlaysCanBeHidden && !mCanCreateSystemApplicationOverlay) {
+ if (!mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay) {
// We want to track non-system apps adding alert windows so we can post an
// on-going notification for the user to control their visibility.
if (visible) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3c7bab3da279..c54c9786c00d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -977,7 +977,7 @@ public class WindowManagerService extends IWindowManager.Stub
void updateSupportsNonResizableMultiWindow() {
ContentResolver resolver = mContext.getContentResolver();
final boolean supportsNonResizableMultiWindow = Settings.Global.getInt(resolver,
- DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) != 0;
+ DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0;
mAtmService.mSupportsNonResizableMultiWindow = supportsNonResizableMultiWindow;
}
@@ -2150,23 +2150,6 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.i(tag, s, e);
}
- void setTransparentRegionWindow(Session session, IWindow client, Region region) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- WindowState w = windowForClientLocked(session, client, false);
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE transparentRegionHint=%s: %s",
- region, w);
-
- if ((w != null) && w.mHasSurface) {
- w.mWinAnimator.setTransparentRegionHintLocked(region);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets,
Rect visibleInsets, Region touchableRegion) {
int uid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fd3d9ba499b7..a94b0aa9b72f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3100,7 +3100,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) {
- if (!mSession.mOverlaysCanBeHidden
+ if (mSession.mCanAddInternalSystemWindow
|| (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index fe70dc12d3a2..ece256e8c591 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -678,14 +678,6 @@ class WindowStateAnimator {
}
}
- void setTransparentRegionHintLocked(final Region region) {
- if (mSurfaceController == null) {
- Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
- return;
- }
- mSurfaceController.setTransparentRegionHint(region);
- }
-
boolean setWallpaperOffset(int dx, int dy, float scale) {
if (mXOffset == dx && mYOffset == dy && Float.compare(mWallpaperScale, scale) == 0) {
return false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 82ba3c188b76..636f0bb6086f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -194,22 +194,6 @@ class WindowSurfaceController {
return true;
}
- void setTransparentRegionHint(final Region region) {
- if (mSurfaceControl == null) {
- Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
- return;
- }
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setTransparentRegion");
- mService.openSurfaceTransaction();
- try {
- getGlobalTransaction().setTransparentRegionHint(mSurfaceControl, region);
- } finally {
- mService.closeSurfaceTransaction("setTransparentRegion");
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION setTransparentRegion");
- }
- }
-
void setOpaque(boolean isOpaque) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isOpaque=%b: %s", isOpaque, title);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 91be0564a26f..1c4b03498144 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -56,10 +56,10 @@ cc_library_static {
"com_android_server_vibrator_VibratorController.cpp",
"com_android_server_VibratorManagerService.cpp",
"com_android_server_PersistentDataBlockService.cpp",
- "com_android_server_am_CachedAppOptimizer.cpp",
"com_android_server_am_LowMemDetector.cpp",
"com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
"onload.cpp",
+ ":lib_cachedAppOptimizer_native",
":lib_networkStatsFactory_native",
],
@@ -193,3 +193,10 @@ filegroup {
"com_android_server_net_NetworkStatsFactory.cpp",
],
}
+
+filegroup {
+ name: "lib_cachedAppOptimizer_native",
+ srcs: [
+ "com_android_server_am_CachedAppOptimizer.cpp",
+ ],
+}
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 31cc295406a5..4551d49d9e58 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -278,6 +278,11 @@ static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv
return retVal;
}
+static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIEnv* env,
+ jobject clazz) {
+ return env->NewStringUTF(CGROUP_FREEZE_PATH);
+}
+
static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
@@ -286,7 +291,9 @@ static const JNINativeMethod sMethods[] = {
(void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
{"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
{"getBinderFreezeInfo", "(I)I",
- (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}};
+ (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo},
+ {"getFreezerCheckPath", "()Ljava/lang/String;",
+ (void*)com_android_server_am_CachedAppOptimizer_getFreezerCheckPath}};
int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
{
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index d863194d6889..c2e0b776fc60 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -18,6 +18,7 @@ package com.android.server.pm.test.verify.domain
import android.content.Context
import android.content.Intent
+import android.content.pm.PackageManager
import android.content.pm.PackageUserState
import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.parsing.component.ParsedActivity
@@ -25,7 +26,6 @@ import android.content.pm.parsing.component.ParsedIntentInfo
import android.os.Build
import android.os.Process
import android.util.ArraySet
-import android.util.Singleton
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.pm.PackageSetting
@@ -46,14 +46,11 @@ import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
import org.mockito.Mockito.eq
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.testng.Assert.assertThrows
import java.io.File
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
-private typealias Enforcer = DomainVerificationEnforcer
-
@RunWith(Parameterized::class)
class DomainVerificationEnforcerTest {
@@ -64,64 +61,27 @@ class DomainVerificationEnforcerTest {
private const val VERIFIER_UID = Process.FIRST_APPLICATION_UID + 1
private const val NON_VERIFIER_UID = Process.FIRST_APPLICATION_UID + 2
- private const val TEST_PKG = "com.test"
+ private const val VISIBLE_PKG = "com.test.visible"
+ private val VISIBLE_UUID = UUID.fromString("8db01272-270d-4606-a3db-bb35228ff9a2")
+ private const val INVISIBLE_PKG = "com.test.invisible"
+ private val INVISIBLE_UUID = UUID.fromString("16dcb029-d96c-4a19-833a-4c9d72e2ebc3")
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun parameters(): Array<Any> {
- val makeEnforcer: (Context) -> DomainVerificationEnforcer = {
- DomainVerificationEnforcer(it)
- }
+ val visiblePkg = mockPkg(VISIBLE_PKG)
+ val visiblePkgSetting = mockPkgSetting(VISIBLE_PKG, VISIBLE_UUID)
+ val invisiblePkg = mockPkg(INVISIBLE_PKG)
+ val invisiblePkgSetting = mockPkgSetting(INVISIBLE_PKG, INVISIBLE_UUID)
- val mockPkg = mockThrowOnUnmocked<AndroidPackage> {
- whenever(packageName) { TEST_PKG }
- whenever(targetSdkVersion) { Build.VERSION_CODES.S }
- whenever(activities) {
- listOf(
- ParsedActivity().apply {
- addIntent(
- ParsedIntentInfo().apply {
- autoVerify = true
- addAction(Intent.ACTION_VIEW)
- addCategory(Intent.CATEGORY_BROWSABLE)
- addCategory(Intent.CATEGORY_DEFAULT)
- addDataScheme("https")
- addDataAuthority("example.com", null)
- }
- )
+ val makeEnforcer: (Context) -> DomainVerificationEnforcer = {
+ DomainVerificationEnforcer(it).apply {
+ setCallback(mockThrowOnUnmocked {
+ whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
+ whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
+ true
}
- )
- }
- }
-
- val uuid = UUID.randomUUID()
-
- // TODO: PackageSetting field encapsulation to move to whenever(name)
- val mockPkgSetting = spyThrowOnUnmocked(
- PackageSetting(
- TEST_PKG,
- TEST_PKG,
- File("/test"),
- null,
- null,
- null,
- null,
- 1,
- 0,
- 0,
- 0,
- null,
- null,
- null,
- uuid
- )
- ) {
- whenever(getPkg()) { mockPkg }
- whenever(domainSetId) { uuid }
- whenever(userState) {
- SparseArray<PackageUserState>().apply {
- this[0] = PackageUserState()
- }
+ })
}
}
@@ -129,142 +89,160 @@ class DomainVerificationEnforcerTest {
{
val callingUidInt = AtomicInteger(-1)
val callingUserIdInt = AtomicInteger(-1)
- Triple(
- callingUidInt, callingUserIdInt, DomainVerificationService(
- it,
- mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
- mockThrowOnUnmocked {
- whenever(
- isChangeEnabled(
- anyLong(),
- any()
- )
- ) { true }
- }).apply {
- setConnection(mockThrowOnUnmocked {
- whenever(callingUid) { callingUidInt.get() }
- whenever(callingUserId) { callingUserIdInt.get() }
- whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting }
- whenever(getPackageLocked(TEST_PKG)) { mockPkg }
- whenever(schedule(anyInt(), any()))
- whenever(scheduleWriteSettings())
- })
+
+ val connection: DomainVerificationManagerInternal.Connection =
+ mockThrowOnUnmocked {
+ whenever(callingUid) { callingUidInt.get() }
+ whenever(callingUserId) { callingUserIdInt.get() }
+ whenever(getPackageSettingLocked(VISIBLE_PKG)) { visiblePkgSetting }
+ whenever(getPackageLocked(VISIBLE_PKG)) { visiblePkg }
+ whenever(getPackageSettingLocked(INVISIBLE_PKG)) { invisiblePkgSetting }
+ whenever(getPackageLocked(INVISIBLE_PKG)) { invisiblePkg }
+ whenever(schedule(anyInt(), any()))
+ whenever(scheduleWriteSettings())
+ whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
+ whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
+ true
+ }
}
- )
+ val service = DomainVerificationService(
+ it,
+ mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
+ mockThrowOnUnmocked {
+ whenever(
+ isChangeEnabled(
+ anyLong(),
+ any()
+ )
+ ) { true }
+ }).apply {
+ setConnection(connection)
+ }
+
+ Triple(callingUidInt, callingUserIdInt, service)
}
fun enforcer(
type: Type,
name: String,
- block: DomainVerificationEnforcer.(
- callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy
- ) -> Unit
- ) = Params(
- type,
- makeEnforcer,
- name
- ) { enforcer, callingUid, callingUserId, userId, proxy ->
- enforcer.block(callingUid, callingUserId, userId, proxy)
+ block: DomainVerificationEnforcer.(Params.Input<DomainVerificationEnforcer>) -> Any?
+ ) = Params(type, makeEnforcer, name) {
+ it.target.block(it)
}
fun service(
type: Type,
name: String,
- block: DomainVerificationService.(
- callingUid: Int, callingUserId: Int, userId: Int
- ) -> Unit
- ) = Params(
- type,
- makeService,
- name
- ) { uidAndUserIdAndService, callingUid, callingUserId, userId, proxy ->
- val (callingUidInt, callingUserIdInt, service) = uidAndUserIdAndService
- callingUidInt.set(callingUid)
- callingUserIdInt.set(callingUserId)
- service.setProxy(proxy)
- service.addPackage(mockPkgSetting)
- service.block(callingUid, callingUserId, userId)
+ block: DomainVerificationService.(Params.Input<Triple<AtomicInteger, AtomicInteger, DomainVerificationService>>) -> Any?
+ ) = Params(type, makeService, name) {
+ val (callingUidInt, callingUserIdInt, service) = it.target
+ callingUidInt.set(it.callingUid)
+ callingUserIdInt.set(it.callingUserId)
+ service.proxy = it.proxy
+ service.addPackage(visiblePkgSetting)
+ service.addPackage(invisiblePkgSetting)
+ service.block(it)
}
return arrayOf(
- enforcer(Type.INTERNAL, "internal") { callingUid, _, _, _ ->
- assertInternal(callingUid)
+ enforcer(Type.INTERNAL, "internal") {
+ assertInternal(it.callingUid)
},
- enforcer(Type.QUERENT, "approvedQuerent") { callingUid, _, _, proxy ->
- assertApprovedQuerent(callingUid, proxy)
+ enforcer(Type.QUERENT, "approvedQuerent") {
+ assertApprovedQuerent(it.callingUid, it.proxy)
},
- enforcer(Type.VERIFIER, "approvedVerifier") { callingUid, _, _, proxy ->
- assertApprovedVerifier(callingUid, proxy)
+ enforcer(Type.VERIFIER, "approvedVerifier") {
+ assertApprovedVerifier(it.callingUid, it.proxy)
},
enforcer(
Type.SELECTOR,
"approvedUserSelector"
- ) { callingUid, callingUserId, userId, _ ->
- assertApprovedUserSelector(callingUid, callingUserId, userId)
+ ) {
+ assertApprovedUserSelector(
+ it.callingUid, it.callingUserId,
+ it.targetPackageName, it.userId
+ )
},
-
- service(Type.INTERNAL, "setStatusInternalPackageName") { _, _, _ ->
+ service(Type.INTERNAL, "setStatusInternalPackageName") {
setDomainVerificationStatusInternal(
- TEST_PKG,
+ it.targetPackageName,
DomainVerificationManager.STATE_SUCCESS,
ArraySet(setOf("example.com"))
)
},
- service(Type.INTERNAL, "setUserSelectionInternal") { _, _, userId ->
+ service(Type.INTERNAL, "setUserSelectionInternal") {
setDomainVerificationUserSelectionInternal(
- userId,
- TEST_PKG,
+ it.userId,
+ it.targetPackageName,
false,
ArraySet(setOf("example.com"))
)
},
- service(Type.INTERNAL, "verifyPackages") { _, _, _ ->
- verifyPackages(listOf(TEST_PKG), true)
+ service(Type.INTERNAL, "verifyPackages") {
+ verifyPackages(listOf(it.targetPackageName), true)
},
- service(Type.INTERNAL, "clearState") { _, _, _ ->
- clearDomainVerificationState(listOf(TEST_PKG))
+ service(Type.INTERNAL, "clearState") {
+ clearDomainVerificationState(listOf(it.targetPackageName))
},
- service(Type.INTERNAL, "clearUserSelections") { _, _, userId ->
- clearUserSelections(listOf(TEST_PKG), userId)
+ service(Type.INTERNAL, "clearUserSelections") {
+ clearUserSelections(listOf(it.targetPackageName), it.userId)
},
- service(Type.VERIFIER, "getPackageNames") { _, _, _ ->
+ service(Type.VERIFIER, "getPackageNames") {
validVerificationPackageNames
},
- service(Type.QUERENT, "getInfo") { _, _, _ ->
- getDomainVerificationInfo(TEST_PKG)
+ service(Type.QUERENT, "getInfo") {
+ getDomainVerificationInfo(it.targetPackageName)
},
- service(Type.VERIFIER, "setStatus") { _, _, _ ->
+ service(Type.VERIFIER, "setStatus") {
setDomainVerificationStatus(
- uuid,
+ it.targetDomainSetId,
setOf("example.com"),
DomainVerificationManager.STATE_SUCCESS
)
},
- service(Type.VERIFIER, "setStatusInternalUid") { callingUid, _, _ ->
+ service(Type.VERIFIER, "setStatusInternalUid") {
setDomainVerificationStatusInternal(
- callingUid,
- uuid,
+ it.callingUid,
+ it.targetDomainSetId,
setOf("example.com"),
DomainVerificationManager.STATE_SUCCESS
)
},
- service(Type.SELECTOR, "setLinkHandlingAllowed") { _, _, _ ->
- setDomainVerificationLinkHandlingAllowed(TEST_PKG, true)
+ service(Type.SELECTOR, "setLinkHandlingAllowed") {
+ setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true)
},
- service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") { _, _, userId ->
- setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, userId)
+ service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") {
+ setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true, it.userId)
},
- service(Type.SELECTOR, "getUserSelection") { _, _, _ ->
- getDomainVerificationUserSelection(TEST_PKG)
+ service(Type.SELECTOR, "getUserSelection") {
+ getDomainVerificationUserSelection(it.targetPackageName)
},
- service(Type.SELECTOR_USER, "getUserSelectionUserId") { _, _, userId ->
- getDomainVerificationUserSelection(TEST_PKG, userId)
+ service(Type.SELECTOR_USER, "getUserSelectionUserId") {
+ getDomainVerificationUserSelection(it.targetPackageName, it.userId)
},
- service(Type.SELECTOR, "setUserSelection") { _, _, _ ->
- setDomainVerificationUserSelection(uuid, setOf("example.com"), true)
+ service(Type.SELECTOR, "setUserSelection") {
+ setDomainVerificationUserSelection(
+ it.targetDomainSetId,
+ setOf("example.com"),
+ true
+ )
+ },
+ service(Type.SELECTOR_USER, "setUserSelectionUserId") {
+ setDomainVerificationUserSelection(
+ it.targetDomainSetId,
+ setOf("example.com"),
+ true,
+ it.userId
+ )
},
- service(Type.SELECTOR_USER, "setUserSelectionUserId") { _, _, userId ->
- setDomainVerificationUserSelection(uuid, setOf("example.com"), true, userId)
+ service(Type.LEGACY_SELECTOR, "setLegacyUserState") {
+ setLegacyUserState(
+ it.targetPackageName, it.userId,
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER
+ )
+ },
+ service(Type.LEGACY_QUERENT, "getLegacyUserState") {
+ getLegacyState(it.targetPackageName, it.userId)
},
)
}
@@ -273,9 +251,7 @@ class DomainVerificationEnforcerTest {
val type: Type,
val construct: (context: Context) -> T,
val name: String,
- private val method: (
- T, callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy
- ) -> Unit
+ private val method: (Input<T>) -> Any?
) {
override fun toString() = "${type}_$name"
@@ -284,10 +260,79 @@ class DomainVerificationEnforcerTest {
callingUid: Int,
callingUserId: Int,
userId: Int,
+ targetPackageName: String,
+ targetDomainSetId: UUID,
proxy: DomainVerificationProxy
- ) {
- @Suppress("UNCHECKED_CAST")
- method(target as T, callingUid, callingUserId, userId, proxy)
+ ): Any? = method(
+ Input(
+ @Suppress("UNCHECKED_CAST")
+ target as T,
+ callingUid,
+ callingUserId,
+ userId,
+ targetPackageName,
+ targetDomainSetId,
+ proxy
+ )
+ )
+
+ data class Input<T>(
+ val target: T,
+ val callingUid: Int,
+ val callingUserId: Int,
+ val userId: Int,
+ val targetPackageName: String,
+ val targetDomainSetId: UUID,
+ val proxy: DomainVerificationProxy
+ )
+ }
+
+ fun mockPkg(packageName: String) = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(this.packageName) { packageName }
+ whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+ whenever(activities) {
+ listOf(
+ ParsedActivity().apply {
+ addIntent(
+ ParsedIntentInfo().apply {
+ autoVerify = true
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("https")
+ addDataAuthority("example.com", null)
+ }
+ )
+ }
+ )
+ }
+ }
+
+ fun mockPkgSetting(packageName: String, domainSetId: UUID) = spyThrowOnUnmocked(
+ PackageSetting(
+ packageName,
+ packageName,
+ File("/test"),
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ domainSetId
+ )
+ ) {
+ whenever(getPkg()) { mockPkg(packageName) }
+ whenever(this.domainSetId) { domainSetId }
+ whenever(userState) {
+ SparseArray<PackageUserState>().apply {
+ this[0] = PackageUserState()
+ }
}
}
}
@@ -309,6 +354,8 @@ class DomainVerificationEnforcerTest {
Type.VERIFIER -> approvedVerifier()
Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false)
Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true)
+ Type.LEGACY_QUERENT -> legacyQuerent()
+ Type.LEGACY_SELECTOR -> legacyUserSelector()
}.run { /*exhaust*/ }
}
@@ -316,24 +363,30 @@ class DomainVerificationEnforcerTest {
val context: Context = mockThrowOnUnmocked()
val target = params.construct(context)
- INTERNAL_UIDS.forEach { runMethod(target, it) }
- assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) }
- assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ // Internal doesn't care about visibility
+ listOf(true, false).forEach { visible ->
+ INTERNAL_UIDS.forEach { runMethod(target, it, visible) }
+ assertFails { runMethod(target, VERIFIER_UID, visible) }
+ assertFails {
+ runMethod(target, NON_VERIFIER_UID, visible)
+ }
+ }
}
fun approvedQuerent() {
val allowUserSelection = AtomicBoolean(false)
+ val allowPreferredApps = AtomicBoolean(false)
+ val allowQueryAll = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (!allowUserSelection.get()) {
- throw SecurityException()
- }
- }
+ initPermission(
+ allowUserSelection,
+ android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+ )
+ initPermission(
+ allowPreferredApps,
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS
+ )
+ initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES)
}
val target = params.construct(context)
@@ -341,27 +394,41 @@ class DomainVerificationEnforcerTest {
verifyNoMoreInteractions(context)
+ assertFails { runMethod(target, VERIFIER_UID) }
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ // Check that the verifier only needs QUERY_ALL to pass
+ allowQueryAll.set(true)
runMethod(target, VERIFIER_UID)
- assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ allowQueryAll.set(false)
+
+ allowPreferredApps.set(true)
+
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
allowUserSelection.set(true)
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ allowQueryAll.set(true)
+
runMethod(target, NON_VERIFIER_UID)
}
fun approvedVerifier() {
- val shouldThrow = AtomicBoolean(false)
+ val allowDomainVerificationAgent = AtomicBoolean(false)
+ val allowIntentVerificationAgent = AtomicBoolean(false)
+ val allowQueryAll = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (shouldThrow.get()) {
- throw SecurityException()
- }
- }
+ initPermission(
+ allowDomainVerificationAgent,
+ android.Manifest.permission.DOMAIN_VERIFICATION_AGENT
+ )
+ initPermission(
+ allowIntentVerificationAgent,
+ android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT
+ )
+ initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES)
}
val target = params.construct(context)
@@ -369,94 +436,241 @@ class DomainVerificationEnforcerTest {
verifyNoMoreInteractions(context)
+ assertFails { runMethod(target, VERIFIER_UID) }
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ allowDomainVerificationAgent.set(true)
+
+ assertFails { runMethod(target, VERIFIER_UID) }
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ allowQueryAll.set(true)
+
runMethod(target, VERIFIER_UID)
- assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
- shouldThrow.set(true)
+ // Check that v1 verifiers are also allowed through
+ allowDomainVerificationAgent.set(false)
+ allowIntentVerificationAgent.set(true)
- assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) }
- assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ runMethod(target, VERIFIER_UID)
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
}
fun approvedUserSelector(verifyCrossUser: Boolean) {
- val allowUserSelection = AtomicBoolean(true)
- val allowInteractAcrossUsers = AtomicBoolean(true)
+ val allowUserSelection = AtomicBoolean(false)
+ val allowInteractAcrossUsers = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (!allowUserSelection.get()) {
- throw SecurityException()
+ initPermission(
+ allowUserSelection,
+ android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+ )
+ initPermission(
+ allowInteractAcrossUsers,
+ android.Manifest.permission.INTERACT_ACROSS_USERS
+ )
+ }
+ val target = params.construct(context)
+
+ fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+ // User selector makes no distinction by UID
+ val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+ if (throws) {
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
}
- }
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.INTERACT_ACROSS_USERS),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (!allowInteractAcrossUsers.get()) {
- throw SecurityException()
+ } else {
+ allUids.forEach {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
}
}
- }
- val target = params.construct(context)
- fun runEachTestCaseWrapped(
- callingUserId: Int,
- targetUserId: Int,
- block: (testCase: () -> Unit) -> Unit = { it.invoke() }
- ) {
- block { runMethod(target, VERIFIER_UID, callingUserId, targetUserId) }
- block { runMethod(target, NON_VERIFIER_UID, callingUserId, targetUserId) }
+ // User selector doesn't use QUERY_ALL, so the invisible package should always fail
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = false, callingUserId, targetUserId)
+ }
+ }
}
val callingUserId = 0
val notCallingUserId = 1
- runEachTestCaseWrapped(callingUserId, callingUserId)
+ runTestCases(callingUserId, callingUserId, throws = true)
if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
}
- allowInteractAcrossUsers.set(false)
+ allowUserSelection.set(true)
+
+ runTestCases(callingUserId, callingUserId, throws = false)
+ if (verifyCrossUser) {
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+ }
- runEachTestCaseWrapped(callingUserId, callingUserId)
+ allowInteractAcrossUsers.set(true)
+ runTestCases(callingUserId, callingUserId, throws = false)
if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId) {
- assertThrows(SecurityException::class.java, it)
+ runTestCases(callingUserId, notCallingUserId, throws = false)
+ }
+ }
+
+ private fun legacyUserSelector() {
+ val allowInteractAcrossUsers = AtomicBoolean(false)
+ val allowPreferredApps = AtomicBoolean(false)
+ val context: Context = mockThrowOnUnmocked {
+ initPermission(
+ allowInteractAcrossUsers,
+ android.Manifest.permission.INTERACT_ACROSS_USERS
+ )
+ initPermission(
+ allowPreferredApps,
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS
+ )
+ }
+ val target = params.construct(context)
+
+ fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+ // Legacy makes no distinction by UID
+ val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+ if (throws) {
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
+ }
+ } else {
+ allUids.forEach {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
+ }
+
+ // Legacy doesn't use QUERY_ALL, so the invisible package should always fail
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = false, callingUserId, targetUserId)
+ }
}
}
- allowUserSelection.set(false)
+ val callingUserId = 0
+ val notCallingUserId = 1
+
+ runTestCases(callingUserId, callingUserId, throws = true)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ allowPreferredApps.set(true)
- runEachTestCaseWrapped(callingUserId, callingUserId) {
- assertThrows(SecurityException::class.java, it)
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ allowInteractAcrossUsers.set(true)
+
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = false)
+ }
+
+ private fun legacyQuerent() {
+ val allowInteractAcrossUsers = AtomicBoolean(false)
+ val allowInteractAcrossUsersFull = AtomicBoolean(false)
+ val context: Context = mockThrowOnUnmocked {
+ initPermission(
+ allowInteractAcrossUsers,
+ android.Manifest.permission.INTERACT_ACROSS_USERS
+ )
+ initPermission(
+ allowInteractAcrossUsersFull,
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ )
}
- if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId) {
- assertThrows(SecurityException::class.java, it)
+ val target = params.construct(context)
+
+ fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+ // Legacy makes no distinction by UID
+ val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+ if (throws) {
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
+ }
+ } else {
+ allUids.forEach {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
+ }
+
+ // Legacy doesn't use QUERY_ALL, so the invisible package should always fail
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = false, callingUserId, targetUserId)
+ }
}
}
+ val callingUserId = 0
+ val notCallingUserId = 1
+
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ // Legacy requires the _FULL permission, so this should continue to fail
allowInteractAcrossUsers.set(true)
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ allowInteractAcrossUsersFull.set(true)
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = false)
+ }
- runEachTestCaseWrapped(callingUserId, callingUserId) {
- assertThrows(SecurityException::class.java, it)
+ private fun Context.initPermission(boolean: AtomicBoolean, permission: String) {
+ whenever(enforcePermission(eq(permission), anyInt(), anyInt(), anyString())) {
+ if (!boolean.get()) {
+ throw SecurityException()
+ }
}
- if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId) {
- assertThrows(SecurityException::class.java, it)
+ whenever(checkPermission(eq(permission), anyInt(), anyInt())) {
+ if (boolean.get()) {
+ PackageManager.PERMISSION_GRANTED
+ } else {
+ PackageManager.PERMISSION_DENIED
}
}
}
- private fun runMethod(target: Any, callingUid: Int, callingUserId: Int = 0, userId: Int = 0) {
- params.runMethod(target, callingUid, callingUserId, userId, proxy)
+ private fun runMethod(
+ target: Any,
+ callingUid: Int,
+ visible: Boolean = true,
+ callingUserId: Int = 0,
+ userId: Int = 0
+ ): Any? {
+ val packageName = if (visible) VISIBLE_PKG else INVISIBLE_PKG
+ val uuid = if (visible) VISIBLE_UUID else INVISIBLE_UUID
+ return params.runMethod(target, callingUid, callingUserId, userId, packageName, uuid, proxy)
+ }
+
+ private fun assertFails(block: () -> Any?) {
+ try {
+ val value = block()
+ // Some methods return false rather than throwing, so check that as well
+ if ((value as? Boolean) != false) {
+ // Can also return default value if it's a legacy call
+ if ((value as? Int)
+ != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
+ ) {
+ throw AssertionError("Expected call to return false, was $value")
+ }
+ }
+ } catch (e: SecurityException) {
+ } catch (e: PackageManager.NameNotFoundException) {
+ } catch (e: DomainVerificationManager.InvalidDomainSetException) {
+ // Any of these 3 exceptions are considered failures, which is expected
+ }
}
enum class Type {
@@ -473,6 +687,12 @@ class DomainVerificationEnforcerTest {
SELECTOR,
// Holding the user setting permission, but targeting cross user
- SELECTOR_USER
+ SELECTOR_USER,
+
+ // Legacy required no permissions except when cross-user
+ LEGACY_QUERENT,
+
+ // Holding the legacy preferred apps permission
+ LEGACY_SELECTOR
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 5792e02e37a2..5629d1c1107d 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -267,5 +267,8 @@ class DomainVerificationSettingsMutationTest {
whenever(getPackageLocked(TEST_PKG)) { mockPkg() }
whenever(schedule(anyInt(), any()))
whenever(scheduleWriteSettings())
+
+ // This doesn't check for visibility; that's done in the enforcer test
+ whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
}
}
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 7c935d532f22..e7d56a070182 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -11,9 +11,16 @@
// 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.
+java_defaults {
+ name: "FrameworkMockingServicesTests-jni-defaults",
+ jni_libs: [
+ "libactivitymanagermockingservicestestjni",
+ ],
+}
android_test {
name: "FrameworksMockingServicesTests",
+ defaults: ["FrameworkMockingServicesTests-jni-defaults"],
srcs: ["src/**/*.java", "src/**/*.kt"],
@@ -72,4 +79,4 @@ java_library {
libs: [
"android.test.runner",
],
-} \ No newline at end of file
+}
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
new file mode 100644
index 000000000000..928065a7ebd9
--- /dev/null
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -0,0 +1,33 @@
+cc_library_shared {
+ name: "libactivitymanagermockingservicestestjni",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wthread-safety",
+ ],
+
+ srcs: [
+ ":lib_cachedAppOptimizer_native",
+ "onload.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/base/libs",
+ "frameworks/native/services",
+ "system/memory/libmeminfo/include",
+ ],
+
+ shared_libs: [
+ "libandroid",
+ "libandroid_runtime",
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libmeminfo",
+ "libnativehelper",
+ "libprocessgroup",
+ "libutils",
+ ],
+}
diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp
new file mode 100644
index 000000000000..147cc479be18
--- /dev/null
+++ b/services/tests/mockingservicestests/jni/onload.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+/*
+ * this is a mini native libaray for cached app optimizer tests to run properly. It
+ * loads all the native methods necessary.
+ */
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+namespace android {
+int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
+};
+
+using namespace android;
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGE("GetEnv failed!");
+ return result;
+ }
+ ALOG_ASSERT(env, "Could not retrieve the env!");
+ register_android_server_am_CachedAppOptimizer(env);
+ return JNI_VERSION_1_4;
+}
+
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index d86032622e43..56d30ccdf59f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -97,6 +97,7 @@ public final class CachedAppOptimizerTest {
@Before
public void setUp() {
+ System.loadLibrary("activitymanagermockingservicestestjni");
mHandlerThread = new HandlerThread("");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
index 0311920477a1..059744388197 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
@@ -19,6 +19,8 @@ package com.android.server.location.injector;
import android.os.IPowerManager;
import android.os.PowerManager.LocationPowerSaveMode;
+import com.android.server.location.eventlog.LocationEventLog;
+
/**
* Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no
* change".
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
index c39a77eac23f..6156ba9359cd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
@@ -43,6 +43,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
import org.junit.After;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index 8e5b16e1ee3a..2822d5c69091 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -16,9 +16,10 @@
package com.android.server.location.injector;
+import com.android.server.location.eventlog.LocationEventLog;
+
public class TestInjector implements Injector {
- private final LocationEventLog mLocationEventLog;
private final FakeUserInfoHelper mUserInfoHelper;
private final FakeAlarmHelper mAlarmHelper;
private final FakeAppOpsHelper mAppOpsHelper;
@@ -32,14 +33,17 @@ public class TestInjector implements Injector {
private final LocationUsageLogger mLocationUsageLogger;
public TestInjector() {
- mLocationEventLog = new LocationEventLog();
+ this(new LocationEventLog());
+ }
+
+ public TestInjector(LocationEventLog eventLog) {
mUserInfoHelper = new FakeUserInfoHelper();
mAlarmHelper = new FakeAlarmHelper();
mAppOpsHelper = new FakeAppOpsHelper();
mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
mSettingsHelper = new FakeSettingsHelper();
mAppForegroundHelper = new FakeAppForegroundHelper();
- mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(mLocationEventLog);
+ mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(eventLog);
mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
mEmergencyHelper = new FakeEmergencyHelper();
@@ -100,9 +104,4 @@ public class TestInjector implements Injector {
public LocationUsageLogger getLocationUsageLogger() {
return mLocationUsageLogger;
}
-
- @Override
- public LocationEventLog getLocationEventLog() {
- return mLocationEventLog;
- }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 54fa89a1e24b..66b037d70a40 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -83,6 +83,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.FakeUserInfoHelper;
import com.android.server.location.injector.TestInjector;
@@ -159,17 +160,19 @@ public class LocationProviderManagerTest {
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
- mInjector = new TestInjector();
+ LocationEventLog eventLog = new LocationEventLog();
+
+ mInjector = new TestInjector(eventLog);
mInjector.getUserInfoHelper().startUser(OTHER_USER);
- mPassive = new PassiveLocationProviderManager(mContext, mInjector);
+ mPassive = new PassiveLocationProviderManager(mContext, mInjector, eventLog);
mPassive.startManager();
mPassive.setRealProvider(new PassiveLocationProvider(mContext));
mProvider = new TestProvider(PROPERTIES, IDENTITY);
mProvider.setProviderAllowed(true);
- mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive);
+ mManager = new LocationProviderManager(mContext, mInjector, eventLog, NAME, mPassive);
mManager.startManager();
mManager.setRealProvider(mProvider);
}
diff --git a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
index 50e7a0395a2a..58d6dae1637a 100644
--- a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
+++ b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
@@ -34,7 +34,7 @@ public class EntropyMixerTest extends AndroidTestCase {
assertEquals(0, FileUtils.readTextFile(file, 0, null).length());
// The constructor has the side effect of writing to file
- new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath(), "/dev/null");
+ new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath());
assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0);
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0cb1255c6830..9e419d4c3f3b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1526,15 +1526,19 @@ public class UsageStatsService extends SystemService implements
@Override
public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime,
- long endTime, String callingPackage) {
+ long endTime, String callingPackage, int userId) {
if (!hasPermission(callingPackage)) {
return null;
}
+ final int callingUid = Binder.getCallingUid();
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
+ userId, false, true, "queryUsageStats", callingPackage);
+
+ // Check the caller's userId for obfuscation decision, not the user being queried
final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
- Binder.getCallingUid(), UserHandle.getCallingUserId());
+ callingUid, UserHandle.getCallingUserId());
- final int userId = UserHandle.getCallingUserId();
final long token = Binder.clearCallingIdentity();
try {
final List<UsageStats> results = UsageStatsService.this.queryUsageStats(
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index be36f82cce7d..f687e4bb9567 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -581,17 +581,24 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
- VoiceInteractionServiceInfo findAvailInteractor(int userHandle, String packageName) {
- List<ResolveInfo> available =
- mContext.getPackageManager().queryIntentServicesAsUser(
- new Intent(VoiceInteractionService.SERVICE_INTERFACE)
- .setPackage(packageName),
- PackageManager.GET_META_DATA
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
+ private List<ResolveInfo> queryInteractorServices(
+ @UserIdInt int user,
+ @Nullable String packageName) {
+ return mContext.getPackageManager().queryIntentServicesAsUser(
+ new Intent(VoiceInteractionService.SERVICE_INTERFACE).setPackage(packageName),
+ PackageManager.GET_META_DATA
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ user);
+ }
+
+ VoiceInteractionServiceInfo findAvailInteractor(
+ @UserIdInt int user,
+ @Nullable String packageName) {
+ List<ResolveInfo> available = queryInteractorServices(user, packageName);
int numAvailable = available.size();
if (numAvailable == 0) {
- Slog.w(TAG, "no available voice interaction services found for user " + userHandle);
+ Slog.w(TAG, "no available voice interaction services found for user " + user);
return null;
}
// Find first system package. We never want to allow third party services to
@@ -1643,13 +1650,7 @@ public class VoiceInteractionManagerService extends SystemService {
String pkg = roleHolders.get(0);
// Try to set role holder as VoiceInteractionService
- List<ResolveInfo> services = mPm.queryIntentServicesAsUser(
- new Intent(VoiceInteractionService.SERVICE_INTERFACE).setPackage(pkg),
- PackageManager.GET_META_DATA
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
-
- for (ResolveInfo resolveInfo : services) {
+ for (ResolveInfo resolveInfo : queryInteractorServices(userId, pkg)) {
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
VoiceInteractionServiceInfo voiceInteractionServiceInfo =
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ac584c175c59..21cf3e57115d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4748,6 +4748,21 @@ public class CarrierConfigManager {
public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL =
"network_temp_not_metered_supported_bool";
+ /*
+ * Boolean indicating whether the SIM PIN can be stored and verified
+ * seamlessly after an unattended reboot.
+ *
+ * The device configuration value {@code config_allow_pin_storage_for_unattended_reboot}
+ * ultimately controls whether this carrier configuration option is used. Where
+ * {@code config_allow_pin_storage_for_unattended_reboot} is false, the value of the
+ * {@link #KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL} carrier configuration option is
+ * ignored.
+ *
+ * @hide
+ */
+ public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL =
+ "store_sim_pin_for_unattended_reboot_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -5304,6 +5319,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true);
sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0);
+ sDefaults.putBoolean(KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, true);
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 65b8de2b2216..16ffd9e4a44e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -11255,16 +11255,21 @@ public class TelephonyManager {
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
- * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+ * Additionally, depending on the level of location permissions the caller holds (i.e. no
+ * location permissions, {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, or
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}), location-sensitive fields will
+ * be cleared from the return value.
+ *
+ * <p>Note also that if the caller holds any sort of location permission, a usage event for the
+ * {@link android.app.AppOpsManager#OPSTR_FINE_LOCATION} or
+ * {@link android.app.AppOpsManager#OPSTR_FINE_LOCATION}
+ * will be logged against the caller when calling this method.
*
* May return {@code null} when the subscription is inactive or when there was an error
* communicating with the phone process.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
- @RequiresPermission(allOf = {
- Manifest.permission.READ_PHONE_STATE,
- Manifest.permission.ACCESS_COARSE_LOCATION
- })
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
public @Nullable ServiceState getServiceState() {
return getServiceStateForSubscriber(getSubId());
}
@@ -14994,6 +14999,7 @@ public class TelephonyManager {
}
return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR;
}
+
/**
* Registers a listener object to receive notification of changes
* in specified telephony states.
@@ -15350,4 +15356,66 @@ public class TelephonyManager {
return PhoneCapability.DEFAULT_SSSS_CAPABILITY;
}
}
+
+ /**
+ * The unattended reboot was prepared successfully.
+ * @hide
+ */
+ @SystemApi
+ public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0;
+
+ /**
+ * The unattended reboot was prepared, but the user will need to manually
+ * enter the PIN code of at least one SIM card present in the device.
+ * @hide
+ */
+ @SystemApi
+ public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1;
+
+ /**
+ * The unattended reboot was not prepared due to generic error.
+ * @hide
+ */
+ @SystemApi
+ public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"PREPARE_UNATTENDED_REBOOT_"},
+ value = {
+ PREPARE_UNATTENDED_REBOOT_SUCCESS,
+ PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED,
+ PREPARE_UNATTENDED_REBOOT_ERROR
+ })
+ public @interface PrepareUnattendedRebootResult {}
+
+ /**
+ * Prepare TelephonyManager for an unattended reboot. The reboot is required to be done
+ * shortly (e.g. within 15 seconds) after the API is invoked.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#REBOOT}
+ *
+ * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success.
+ * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains
+ * at least one SIM card for which the user needs to manually enter the PIN
+ * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case
+ * of error.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.REBOOT)
+ @PrepareUnattendedRebootResult
+ public int prepareForUnattendedReboot() {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.prepareForUnattendedReboot();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Telephony#prepareForUnattendedReboot RemoteException", e);
+ e.rethrowFromSystemServer();
+ }
+ return PREPARE_UNATTENDED_REBOOT_ERROR;
+ }
}
diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl
new file mode 100644
index 000000000000..0830ff2ff050
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2021 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.ims;
+
+parcelable ImsRegistrationAttributes;
diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
new file mode 100644
index 000000000000..ccb3231526dd
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2021 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Contains the attributes associated with the current IMS registration.
+ */
+public final class ImsRegistrationAttributes implements Parcelable {
+
+ /**
+ * Attribute to specify if an EPDG tunnel is setup over the cellular internet APN.
+ * <p>
+ * If IMS is registered through an EPDG tunnel is setup over the cellular internet APN then this
+ * bit will be set. If IMS is registered through the IMS APN, then this bit will not be set.
+ *
+ */
+ public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1 << 0;
+
+ /** @hide */
+ // Defines the underlying radio technology type that we have registered for IMS over.
+ @IntDef(prefix = "ATTR_",
+ value = {
+ ATTR_EPDG_OVER_CELL_INTERNET,
+ },
+ flag = true)
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsAttributeFlag {}
+
+ /**
+ * Builder for creating {@link ImsRegistrationAttributes} instances.
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ private final int mRegistrationTech;
+ private Set<String> mFeatureTags = Collections.emptySet();
+
+ /**
+ * Build a new instance of {@link ImsRegistrationAttributes}.
+ *
+ * @param registrationTech The Radio Access Technology that IMS is registered on.
+ */
+ public Builder(@ImsRegistrationImplBase.ImsRegistrationTech int registrationTech) {
+ mRegistrationTech = registrationTech;
+ }
+
+ /**
+ * Optional IMS feature tags included in this IMS registration.
+ * @param tags A set of Strings containing the MMTEL and RCS feature tags associated with
+ * the IMS registration. This information is used for services such as the UCE
+ * service to ascertain the complete IMS registration state to ensure the SIP
+ * PUBLISH is accurate. The format of the set of feature tags must be one feature
+ * tag key and value per entry. Each feature tag will contain the feature tag name
+ * and string value (if applicable), even if they have the same feature tag name.
+ * For example,
+ * {@code +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg,
+ * urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", +g.gsma.callcomposer} must
+ * be split into three feature tag entries:
+ * {@code {+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg",
+ * +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session",
+ * +g.gsma.callcomposer}}.
+ */
+ public @NonNull Builder setFeatureTags(@NonNull Set<String> tags) {
+ if (tags == null) {
+ throw new IllegalArgumentException("feature tag set must not be null");
+ }
+ mFeatureTags = new ArraySet<>(tags);
+ return this;
+ }
+
+ /**
+ * @return A new instance created from this builder.
+ */
+ public @NonNull ImsRegistrationAttributes build() {
+ return new ImsRegistrationAttributes(mRegistrationTech,
+ RegistrationManager.getAccessType(mRegistrationTech),
+ getAttributeFlags(mRegistrationTech),
+ mFeatureTags);
+ }
+
+ /**
+ * @return attribute flags from the registration technology.
+ */
+ private static int getAttributeFlags(int imsRadioTech) {
+ int attributes = 0;
+ if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
+ attributes |= ATTR_EPDG_OVER_CELL_INTERNET;
+ }
+ return attributes;
+ }
+ }
+
+ private final int mRegistrationTech;
+ private final int mTransportType;
+ private final int mImsAttributeFlags;
+ private final ArrayList<String> mFeatureTags;
+
+ /**
+ * Create a new {@link ImsRegistrationAttributes} instance.
+ *
+ * @param registrationTech The technology that IMS has been registered on.
+ * @param transportType The transport type that IMS has been registered on.
+ * @param imsAttributeFlags The attributes associated with the IMS registration.
+ * @param featureTags The feature tags included in the IMS registration.
+ * @see Builder
+ * @hide
+ */
+ public ImsRegistrationAttributes(
+ @ImsRegistrationImplBase.ImsRegistrationTech int registrationTech,
+ @AccessNetworkConstants.TransportType int transportType,
+ @ImsAttributeFlag int imsAttributeFlags,
+ @Nullable Set<String> featureTags) {
+ mRegistrationTech = registrationTech;
+ mTransportType = transportType;
+ mImsAttributeFlags = imsAttributeFlags;
+ mFeatureTags = new ArrayList<>(featureTags);
+ }
+
+ /**@hide*/
+ public ImsRegistrationAttributes(Parcel source) {
+ mRegistrationTech = source.readInt();
+ mTransportType = source.readInt();
+ mImsAttributeFlags = source.readInt();
+ mFeatureTags = new ArrayList<>();
+ source.readList(mFeatureTags, null /*classloader*/);
+ }
+
+ /**
+ * @return The Radio Access Technology that the IMS registration has been registered over.
+ * @hide
+ */
+ @SystemApi
+ public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTechnology() {
+ return mRegistrationTech;
+ }
+
+ /**
+ * @return The access network transport type that IMS has been registered over.
+ */
+ public @AccessNetworkConstants.TransportType int getTransportType() {
+ return mTransportType;
+ }
+
+ /**
+ * @return A bit-mask containing attributes associated with the IMS registration.
+ */
+ public @ImsAttributeFlag int getAttributeFlags() {
+ return mImsAttributeFlags;
+ }
+
+ /**
+ * Gets the Set of feature tags associated with the current IMS registration, if the IMS
+ * service supports supplying this information.
+ * <p>
+ * The format of the set of feature tags will be one feature tag key and value per entry and
+ * will potentially contain MMTEL and RCS feature tags, depending the configuration of the IMS
+ * service associated with the registration indications. Each feature tag will contain the
+ * feature tag name and string value (if applicable), even if they have the same feature tag
+ * name. For example, {@code +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg,
+ * urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", +g.gsma.callcomposer} will be split
+ * into three feature tag entries:
+ * {@code {+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg",
+ * +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session",
+ * +g.gsma.callcomposer}}.
+ * @return The Set of feature tags associated with the current IMS registration.
+ */
+ public @NonNull Set<String> getFeatureTags() {
+ if (mFeatureTags == null) {
+ return Collections.emptySet();
+ }
+ return new ArraySet<>(mFeatureTags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mRegistrationTech);
+ dest.writeInt(mTransportType);
+ dest.writeInt(mImsAttributeFlags);
+ dest.writeList(mFeatureTags);
+ }
+
+ public static final @NonNull Creator<ImsRegistrationAttributes> CREATOR =
+ new Creator<ImsRegistrationAttributes>() {
+ @Override
+ public ImsRegistrationAttributes createFromParcel(Parcel source) {
+ return new ImsRegistrationAttributes(source);
+ }
+
+ @Override
+ public ImsRegistrationAttributes[] newArray(int size) {
+ return new ImsRegistrationAttributes[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ImsRegistrationAttributes that = (ImsRegistrationAttributes) o;
+ return mRegistrationTech == that.mRegistrationTech
+ && mTransportType == that.mTransportType
+ && mImsAttributeFlags == that.mImsAttributeFlags
+ && Objects.equals(mFeatureTags, that.mFeatureTags);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRegistrationTech, mTransportType, mImsAttributeFlags, mFeatureTags);
+ }
+
+ @Override
+ public String toString() {
+ return "ImsRegistrationAttributes { transportType= " + mTransportType + ", attributeFlags="
+ + mImsAttributeFlags + ", featureTags=[" + mFeatureTags + "]}";
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index b430befbc024..c682afe8178a 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -72,30 +72,6 @@ public interface RegistrationManager {
*/
int REGISTRATION_STATE_REGISTERED = 2;
- /**
- * @hide
- */
- // Defines the underlying radio technology type that we have registered for IMS over.
- @IntDef(prefix = "ATTR_",
- value = {
- ATTR_EPDG_OVER_CELL_INTERNET,
- },
- flag = true)
- @Retention(RetentionPolicy.SOURCE)
- public @interface ImsAttributes {}
-
- /**
- * Attribute to specify if EPDG tunnel is setup over cellular internet.
- * if EPDG tunnel is setup over cellular internet then this bit will be set else the same will
- * not be set.
- */
- int ATTR_EPDG_OVER_CELL_INTERNET = 0x00000001;
-
- //******************************************************************************************
- // Next attribute value: 0x00000002
- //******************************************************************************************
-
-
/**@hide*/
// Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
// and WWAN are more accurate constants.
@@ -103,7 +79,8 @@ public interface RegistrationManager {
new HashMap<Integer, Integer>() {{
// Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
// case, since it is defined.
- put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1);
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE,
+ AccessNetworkConstants.TRANSPORT_TYPE_INVALID);
put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
@@ -132,6 +109,20 @@ public interface RegistrationManager {
}
/**
+ * @param regtech The registration technology.
+ * @return The Access Network type from registration technology.
+ * @hide
+ */
+ static int getAccessType(int regtech) {
+ if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regtech)) {
+ Log.w("RegistrationManager", "getAccessType - invalid regType returned: "
+ + regtech);
+ return AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+ }
+ return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regtech);
+ }
+
+ /**
* Callback class for receiving IMS network Registration callback events.
* @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
* @see #unregisterImsRegistrationCallback(RegistrationCallback)
@@ -149,45 +140,24 @@ public interface RegistrationManager {
}
@Override
- public void onRegistered(int imsRadioTech) {
+ public void onRegistered(ImsRegistrationAttributes attr) {
if (mLocalCallback == null) return;
final long callingIdentity = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() -> {
- mLocalCallback.onRegistered(getAccessType(imsRadioTech));
- });
- int attributes = 0;
- if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
- attributes = changeBitmask(attributes, ATTR_EPDG_OVER_CELL_INTERNET,
- true);
- }
- final int finalattributes = attributes;
- mExecutor.execute(() ->
- mLocalCallback.onRegistered(getAccessType(imsRadioTech),
- finalattributes));
+ mExecutor.execute(() -> mLocalCallback.onRegistered(attr));
} finally {
restoreCallingIdentity(callingIdentity);
}
}
@Override
- public void onRegistering(int imsRadioTech) {
+ public void onRegistering(ImsRegistrationAttributes attr) {
if (mLocalCallback == null) return;
final long callingIdentity = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() ->
- mLocalCallback.onRegistering(getAccessType(imsRadioTech)));
- int attributes = 0;
- if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
- attributes = changeBitmask(attributes, ATTR_EPDG_OVER_CELL_INTERNET,
- true);
- }
- final int finalattributes = attributes;
- mExecutor.execute(() ->
- mLocalCallback.onRegistering(getAccessType(imsRadioTech),
- finalattributes));
+ mExecutor.execute(() -> mLocalCallback.onRegistering(attr));
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -232,31 +202,6 @@ public interface RegistrationManager {
private void setExecutor(Executor executor) {
mExecutor = executor;
}
-
- private static int getAccessType(int regType) {
- if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) {
- Log.w("RegistrationManager", "RegistrationBinder - invalid regType returned: "
- + regType);
- return -1;
- }
- return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
- }
-
- /**
- * Changes a attribute bit-mask to add or remove an attribute.
- *
- * @param bitmask The bit-mask.
- * @param bitfield The bit-field to change.
- * @param enabled Whether the bit-field should be set or removed.
- * @return The bit-mask with the bit-field changed.
- */
- private int changeBitmask(int bitmask, int bitfield, boolean enabled) {
- if (enabled) {
- return bitmask | bitfield;
- } else {
- return bitmask & ~bitfield;
- }
- }
}
private final RegistrationBinder mBinder = new RegistrationBinder(this);
@@ -265,7 +210,7 @@ public interface RegistrationManager {
* Notifies the framework when the IMS Provider is registered to the IMS network.
*
* @param imsTransportType the radio access technology.
- * @deprecated Use {@link #onRegistered(int, int)} instead.
+ * @deprecated Use {@link #onRegistered(ImsRegistrationAttributes)} instead.
*/
@Deprecated
public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
@@ -273,25 +218,20 @@ public interface RegistrationManager {
/**
* Notifies the framework when the IMS Provider is registered to the IMS network
- * with corresponding attributes
- *
- * @param imsTransportType the radio access technology.
- * @param registrationAttributes IMS registration attributes as a bitmap of attributes.
- * Possible attributes are following
- * <ul>
- * <li>{@link #ATTR_EPDG_OVER_CELL_INTERNET}</li>
- * </ul>
+ * with corresponding attributes.
*
+ * @param attributes The attributes associated with this IMS registration.
*/
- public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType,
- @ImsAttributes int registrationAttributes) {
+ public void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
+ // Default impl to keep backwards compatibility with old implementations
+ onRegistered(attributes.getTransportType());
}
/**
* Notifies the framework when the IMS Provider is trying to register the IMS network.
*
* @param imsTransportType the radio access technology.
- * @deprecated Use {@link #onRegistering(int, int)} instead.
+ * @deprecated Use {@link #onRegistering(ImsRegistrationAttributes)} instead.
*/
public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
}
@@ -299,12 +239,11 @@ public interface RegistrationManager {
/**
* Notifies the framework when the IMS Provider is trying to register the IMS network.
*
- * @param imsTransportType the radio access technology.
- * @param registrationAttributes IMS registration attributes as a bitmap of attributes.
- * Possible attributes are following
+ * @param attributes The attributes associated with this IMS registration.
*/
- public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType,
- @ImsAttributes int registrationAttributes) {
+ public void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
+ // Default impl to keep backwards compatibility with old implementations
+ onRegistering(attributes.getTransportType());
}
/**
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
index 749b1916962e..179407c983e5 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
@@ -21,6 +21,7 @@ import android.net.Uri;
import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
/**
* See {@link ImsManager#RegistrationCallback} for more information.
@@ -28,8 +29,8 @@ import android.telephony.ims.ImsReasonInfo;
* {@hide}
*/
oneway interface IImsRegistrationCallback {
- void onRegistered(int imsRadioTech);
- void onRegistering(int imsRadioTech);
+ void onRegistered(in ImsRegistrationAttributes attr);
+ void onRegistering(in ImsRegistrationAttributes attr);
void onDeregistered(in ImsReasonInfo info);
void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info);
void onSubscriberAssociatedUriChanged(in Uri[] uris);
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 4f753c308f7e..39994be34865 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -18,17 +18,18 @@ package android.telephony.ims.stub;
import android.annotation.IntDef;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.Uri;
import android.os.RemoteException;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.RemoteCallbackListExt;
import com.android.internal.util.ArrayUtils;
@@ -89,7 +90,10 @@ public class ImsRegistrationImplBase {
@Override
public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
- return getConnectionType();
+ synchronized (mLock) {
+ return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE
+ : mRegistrationAttributes.getRegistrationTechnology();
+ }
}
@Override
@@ -122,8 +126,7 @@ public class ImsRegistrationImplBase {
new RemoteCallbackListExt<>();
private final Object mLock = new Object();
// Locked on mLock
- private @ImsRegistrationTech
- int mConnectionType = REGISTRATION_TECH_NONE;
+ private ImsRegistrationAttributes mRegistrationAttributes;
// Locked on mLock
private int mRegistrationState = REGISTRATION_STATE_UNKNOWN;
// Locked on mLock, create unspecified disconnect cause.
@@ -201,18 +204,24 @@ public class ImsRegistrationImplBase {
/**
* Notify the framework that the device is connected to the IMS network.
*
- * @param imsRadioTech the radio access technology. Valid values are defined as
- * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
- * {@link #REGISTRATION_TECH_CROSS_SIM}.
+ * @param imsRadioTech the radio access technology.
*/
public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
- updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED);
+ onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
+ }
+
+ /**
+ * Notify the framework that the device is connected to the IMS network.
+ *
+ * @param attributes The attributes associated with the IMS registration.
+ */
+ public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
+ updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED);
mCallbacks.broadcastAction((c) -> {
try {
- c.onRegistered(imsRadioTech);
+ c.onRegistered(attributes);
} catch (RemoteException e) {
- Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " +
- "callback.");
+ Log.w(LOG_TAG, e + "onRegistered(int, Set) - Skipping callback.");
}
});
}
@@ -220,18 +229,24 @@ public class ImsRegistrationImplBase {
/**
* Notify the framework that the device is trying to connect the IMS network.
*
- * @param imsRadioTech the radio access technology. Valid values are defined as
- * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
- * {@link #REGISTRATION_TECH_CROSS_SIM}.
+ * @param imsRadioTech the radio access technology.
*/
public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
- updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING);
+ onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
+ }
+
+ /**
+ * Notify the framework that the device is trying to connect the IMS network.
+ *
+ * @param attributes The attributes associated with the IMS registration.
+ */
+ public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
+ updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING);
mCallbacks.broadcastAction((c) -> {
try {
- c.onRegistering(imsRadioTech);
+ c.onRegistering(attributes);
} catch (RemoteException e) {
- Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " +
- "callback.");
+ Log.w(LOG_TAG, e + "onRegistering(int, Set) - Skipping callback.");
}
});
}
@@ -260,8 +275,7 @@ public class ImsRegistrationImplBase {
try {
c.onDeregistered(reasonInfo);
} catch (RemoteException e) {
- Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " +
- "callback.");
+ Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback.");
}
});
}
@@ -281,8 +295,7 @@ public class ImsRegistrationImplBase {
try {
c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
} catch (RemoteException e) {
- Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " +
- "callback.");
+ Log.w(LOG_TAG, e + "onTechnologyChangeFailed() - Skipping callback.");
}
});
}
@@ -306,14 +319,13 @@ public class ImsRegistrationImplBase {
try {
callback.onSubscriberAssociatedUriChanged(uris);
} catch (RemoteException e) {
- Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping "
- + "callback.");
+ Log.w(LOG_TAG, e + "onSubscriberAssociatedUriChanged() - Skipping callback.");
}
}
- private void updateToState(@ImsRegistrationTech int connType, int newState) {
+ private void updateToState(ImsRegistrationAttributes attributes, int newState) {
synchronized (mLock) {
- mConnectionType = connType;
+ mRegistrationAttributes = attributes;
mRegistrationState = newState;
mLastDisconnectCause = null;
}
@@ -325,7 +337,7 @@ public class ImsRegistrationImplBase {
mUrisSet = false;
mUris = null;
- updateToState(REGISTRATION_TECH_NONE,
+ updateToState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE).build(),
RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
if (info != null) {
mLastDisconnectCause = info;
@@ -337,30 +349,19 @@ public class ImsRegistrationImplBase {
}
/**
- * @return the current registration connection type. Valid values are
- * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
- * {@link #REGISTRATION_TECH_CROSS_SIM}.
- * @hide
- */
- @VisibleForTesting
- public final @ImsRegistrationTech int getConnectionType() {
- synchronized (mLock) {
- return mConnectionType;
- }
- }
-
- /**
* @param c the newly registered callback that will be updated with the current registration
* state.
*/
private void updateNewCallbackWithState(IImsRegistrationCallback c)
throws RemoteException {
int state;
+ ImsRegistrationAttributes attributes;
ImsReasonInfo disconnectInfo;
boolean urisSet;
Uri[] uris;
synchronized (mLock) {
state = mRegistrationState;
+ attributes = mRegistrationAttributes;
disconnectInfo = mLastDisconnectCause;
urisSet = mUrisSet;
uris = mUris;
@@ -371,11 +372,11 @@ public class ImsRegistrationImplBase {
break;
}
case RegistrationManager.REGISTRATION_STATE_REGISTERING: {
- c.onRegistering(getConnectionType());
+ c.onRegistering(attributes);
break;
}
case RegistrationManager.REGISTRATION_STATE_REGISTERED: {
- c.onRegistered(getConnectionType());
+ c.onRegistered(attributes);
break;
}
case REGISTRATION_STATE_UNKNOWN: {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0cd17da3c0c5..1d049530ba77 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2387,4 +2387,18 @@ interface ITelephony {
* Gets the current phone capability.
*/
PhoneCapability getPhoneCapability();
+
+ /**
+ * Prepare TelephonyManager for an unattended reboot. The reboot is
+ * required to be done shortly after the API is invoked.
+ *
+ * Requires system privileges.
+ *
+ * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success.
+ * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains
+ * at least one SIM card for which the user needs to manually enter the PIN
+ * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case
+ * of error.
+ */
+ int prepareForUnattendedReboot();
}
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index 39dc9c286d5f..e2d2ecaf1684 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -16,7 +16,10 @@ java_test_host {
name: "ApkVerityTest",
srcs: ["src/**/*.java"],
libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
- static_libs: ["frameworks-base-hostutils"],
+ static_libs: [
+ "block_device_writer_jar",
+ "frameworks-base-hostutils",
+ ],
test_suites: ["general-tests", "vts"],
target_required: [
"block_device_writer_module",
diff --git a/tests/ApkVerityTest/OWNERS b/tests/ApkVerityTest/OWNERS
new file mode 100644
index 000000000000..d67285ede44a
--- /dev/null
+++ b/tests/ApkVerityTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 36824
+
+victorhsieh@google.com
diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp
index 37fbc29470f6..8f2d4bc70fa0 100644
--- a/tests/ApkVerityTest/block_device_writer/Android.bp
+++ b/tests/ApkVerityTest/block_device_writer/Android.bp
@@ -51,3 +51,9 @@ cc_test {
test_suites: ["general-tests", "pts", "vts"],
gtest: false,
}
+
+java_library_host {
+ name: "block_device_writer_jar",
+ srcs: ["src/**/*.java"],
+ libs: ["tradefed", "junit"],
+}
diff --git a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
new file mode 100644
index 000000000000..5c2c15b22bb0
--- /dev/null
+++ b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 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.blockdevicewriter;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import java.util.ArrayList;
+
+/**
+ * Wrapper for block_device_writer command.
+ *
+ * <p>To use this class, please push block_device_writer binary to /data/local/tmp.
+ * 1. In Android.bp, add:
+ * <pre>
+ * target_required: ["block_device_writer_module"],
+ * </pre>
+ * 2. In AndroidText.xml, add:
+ * <pre>
+ * <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ * <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
+ * </target_preparer>
+ * </pre>
+ */
+public final class BlockDeviceWriter {
+ private static final String EXECUTABLE = "/data/local/tmp/block_device_writer";
+
+ /**
+ * Modifies a byte of the file directly against the backing block storage.
+ *
+ * The effect can only be observed when the page cache is read from disk again. See
+ * {@link #dropCaches} for details.
+ */
+ public static void damageFileAgainstBlockDevice(ITestDevice device, String path,
+ long offsetOfTargetingByte)
+ throws DeviceNotAvailableException {
+ assertThat(path).startsWith("/data/");
+ ITestDevice.MountPointInfo mountPoint = device.getMountPointInfo("/data");
+ ArrayList<String> args = new ArrayList<>();
+ args.add(EXECUTABLE);
+ if ("f2fs".equals(mountPoint.type)) {
+ args.add("--use-f2fs-pinning");
+ }
+ args.add(mountPoint.filesystem);
+ args.add(path);
+ args.add(Long.toString(offsetOfTargetingByte));
+ CommandResult result = device.executeShellV2Command(String.join(" ", args));
+ assertWithMessage(
+ String.format("stdout=%s\nstderr=%s", result.getStdout(), result.getStderr()))
+ .that(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
+ }
+
+ /**
+ * Drops file caches so that the result of {@link #damageFileAgainstBlockDevice} can be
+ * observed. If a process has an open FD or memory map of the damaged file, cache eviction won't
+ * happen and the damage cannot be observed.
+ */
+ public static void dropCaches(ITestDevice device) throws DeviceNotAvailableException {
+ CommandResult result = device.executeShellV2Command(
+ "sync && echo 1 > /proc/sys/vm/drop_caches");
+ assertThat(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
+ }
+
+ public static void assertFileNotOpen(ITestDevice device, String path)
+ throws DeviceNotAvailableException {
+ CommandResult result = device.executeShellV2Command("lsof " + path);
+ assertThat(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
+ assertThat(result.getStdout()).isEmpty();
+ }
+
+ /**
+ * Checks if the give offset of a file can be read.
+ * This method will return false if the file has fs-verity enabled and is damaged at the offset.
+ */
+ public static boolean canReadByte(ITestDevice device, String filePath, long offset)
+ throws DeviceNotAvailableException {
+ CommandResult result = device.executeShellV2Command(
+ "dd if=" + filePath + " bs=1 count=1 skip=" + Long.toString(offset));
+ return result.getStatus() == CommandStatus.SUCCESS;
+ }
+}
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index d0eb9befbdee..ab3572ba2173 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.fail;
import android.platform.test.annotations.RootPermissionTest;
+import com.android.blockdevicewriter.BlockDeviceWriter;
import com.android.fsverity.AddFsVerityCertRule;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
@@ -334,22 +335,23 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
long offsetFirstByte = 0;
// The first two pages should be both readable at first.
- assertTrue(canReadByte(apkPath, offsetFirstByte));
+ assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte));
if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
- assertTrue(canReadByte(apkPath, offsetFirstByte + FSVERITY_PAGE_SIZE));
+ assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
+ offsetFirstByte + FSVERITY_PAGE_SIZE));
}
// Damage the file directly against the block device.
damageFileAgainstBlockDevice(apkPath, offsetFirstByte);
// Expect actual read from disk to fail but only at damaged page.
- dropCaches();
- assertFalse(canReadByte(apkPath, offsetFirstByte));
+ BlockDeviceWriter.dropCaches(mDevice);
+ assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte));
if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
long lastByteOfTheSamePage =
offsetFirstByte % FSVERITY_PAGE_SIZE + FSVERITY_PAGE_SIZE - 1;
- assertFalse(canReadByte(apkPath, lastByteOfTheSamePage));
- assertTrue(canReadByte(apkPath, lastByteOfTheSamePage + 1));
+ assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage));
+ assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage + 1));
}
}
@@ -362,21 +364,22 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
long offsetOfLastByte = apkSize - 1;
// The first two pages should be both readable at first.
- assertTrue(canReadByte(apkPath, offsetOfLastByte));
+ assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte));
if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
- assertTrue(canReadByte(apkPath, offsetOfLastByte - FSVERITY_PAGE_SIZE));
+ assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
+ offsetOfLastByte - FSVERITY_PAGE_SIZE));
}
// Damage the file directly against the block device.
damageFileAgainstBlockDevice(apkPath, offsetOfLastByte);
// Expect actual read from disk to fail but only at damaged page.
- dropCaches();
- assertFalse(canReadByte(apkPath, offsetOfLastByte));
+ BlockDeviceWriter.dropCaches(mDevice);
+ assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte));
if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
long firstByteOfTheSamePage = offsetOfLastByte - offsetOfLastByte % FSVERITY_PAGE_SIZE;
- assertFalse(canReadByte(apkPath, firstByteOfTheSamePage));
- assertTrue(canReadByte(apkPath, firstByteOfTheSamePage - 1));
+ assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage));
+ assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage - 1));
}
}
@@ -395,8 +398,8 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
// from filesystem cache. Forcing GC workarounds the problem.
int retry = 5;
for (; retry > 0; retry--) {
- dropCaches();
- if (!canReadByte(path, kTargetOffset)) {
+ BlockDeviceWriter.dropCaches(mDevice);
+ if (!BlockDeviceWriter.canReadByte(mDevice, path, kTargetOffset)) {
break;
}
try {
@@ -451,16 +454,6 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
return Long.parseLong(expectRemoteCommandToSucceed("stat -c '%s' " + packageName).trim());
}
- private void dropCaches() throws DeviceNotAvailableException {
- expectRemoteCommandToSucceed("sync && echo 1 > /proc/sys/vm/drop_caches");
- }
-
- private boolean canReadByte(String filePath, long offset) throws DeviceNotAvailableException {
- CommandResult result = mDevice.executeShellV2Command(
- "dd if=" + filePath + " bs=1 count=1 skip=" + Long.toString(offset));
- return result.getStatus() == CommandStatus.SUCCESS;
- }
-
private String expectRemoteCommandToSucceed(String cmd) throws DeviceNotAvailableException {
CommandResult result = mDevice.executeShellV2Command(cmd);
assertEquals("`" + cmd + "` failed: " + result.getStderr(), CommandStatus.SUCCESS,
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index d809fe8dad56..43a5078c3c24 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -16,8 +16,14 @@ java_test_host {
name: "UpdatableSystemFontTest",
srcs: ["src/**/*.java"],
libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
- static_libs: ["frameworks-base-hostutils"],
+ static_libs: [
+ "block_device_writer_jar",
+ "frameworks-base-hostutils",
+ ],
test_suites: ["general-tests", "vts"],
+ target_required: [
+ "block_device_writer_module",
+ ],
data: [
":NotoColorEmojiTtf",
":UpdatableSystemFontTestCertDer",
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index efe5d703880c..7b919bd4b114 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -21,6 +21,7 @@
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
+ <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
<option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
<option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
<option name="push" value="UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig" />
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 6d161a5d7b3a..e249f8a99b0c 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -21,7 +21,9 @@ import static com.google.common.truth.Truth.assertWithMessage;
import android.platform.test.annotations.RootPermissionTest;
+import com.android.blockdevicewriter.BlockDeviceWriter;
import com.android.fsverity.AddFsVerityCertRule;
+import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -34,6 +36,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -126,6 +130,44 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
}
+ @Test
+ public void reboot() throws Exception {
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+ String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertThat(fontPath).startsWith("/data/fonts/files/");
+
+ expectRemoteCommandToSucceed("stop");
+ expectRemoteCommandToSucceed("start");
+ waitUntilFontCommandIsReady();
+ String fontPathAfterReboot = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertThat(fontPathAfterReboot).isEqualTo(fontPath);
+ }
+
+ @Test
+ public void reboot_clearDamagedFiles() throws Exception {
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+ String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertThat(fontPath).startsWith("/data/fonts/files/");
+ assertThat(BlockDeviceWriter.canReadByte(getDevice(), fontPath, 0)).isTrue();
+
+ BlockDeviceWriter.damageFileAgainstBlockDevice(getDevice(), fontPath, 0);
+ expectRemoteCommandToSucceed("stop");
+ // We have to make sure system_server is gone before dropping caches, because system_server
+ // process holds font memory maps and prevents cache eviction.
+ waitUntilSystemServerIsGone();
+ BlockDeviceWriter.assertFileNotOpen(getDevice(), fontPath);
+ BlockDeviceWriter.dropCaches(getDevice());
+ assertThat(BlockDeviceWriter.canReadByte(getDevice(), fontPath, 0)).isFalse();
+
+ expectRemoteCommandToSucceed("start");
+ waitUntilFontCommandIsReady();
+ String fontPathAfterReboot = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertWithMessage("Damaged file should be deleted")
+ .that(fontPathAfterReboot).startsWith("/system");
+ }
+
private String getFontPath(String fontFileName) throws Exception {
// TODO: add a dedicated command for testing.
String lines = expectRemoteCommandToSucceed("cmd font dump");
@@ -153,4 +195,39 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
.that(result.getStatus())
.isNotEqualTo(CommandStatus.SUCCESS);
}
+
+ private void waitUntilFontCommandIsReady() {
+ waitUntil(TimeUnit.SECONDS.toMillis(30), () -> {
+ try {
+ return getDevice().executeShellV2Command("cmd font status").getStatus()
+ == CommandStatus.SUCCESS;
+ } catch (DeviceNotAvailableException e) {
+ return false;
+ }
+ });
+ }
+
+ private void waitUntilSystemServerIsGone() {
+ waitUntil(TimeUnit.SECONDS.toMillis(30), () -> {
+ try {
+ return getDevice().executeShellV2Command("pid system_server").getStatus()
+ == CommandStatus.FAILED;
+ } catch (DeviceNotAvailableException e) {
+ return false;
+ }
+ });
+ }
+
+ private void waitUntil(long timeoutMillis, Supplier<Boolean> func) {
+ long untilMillis = System.currentTimeMillis() + timeoutMillis;
+ do {
+ if (func.get()) return;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ throw new AssertionError("Interrupted", e);
+ }
+ } while (System.currentTimeMillis() < untilMillis);
+ throw new AssertionError("Timed out");
+ }
}
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index f9db408462b7..7dada9d1b6d4 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -65,7 +65,7 @@ public class VcnManagerTest {
ArgumentCaptor.forClass(IVcnUnderlyingNetworkPolicyListener.class);
verify(mMockVcnManagementService).addVcnUnderlyingNetworkPolicyListener(captor.capture());
- assertTrue(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+ assertTrue(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener));
IVcnUnderlyingNetworkPolicyListener listenerWrapper = captor.getValue();
listenerWrapper.onPolicyChanged();
@@ -78,7 +78,7 @@ public class VcnManagerTest {
mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
- assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+ assertFalse(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener));
verify(mMockVcnManagementService)
.addVcnUnderlyingNetworkPolicyListener(
any(IVcnUnderlyingNetworkPolicyListener.class));
@@ -88,7 +88,7 @@ public class VcnManagerTest {
public void testRemoveVcnUnderlyingNetworkPolicyListenerUnknownListener() throws Exception {
mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
- assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+ assertFalse(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener));
verify(mMockVcnManagementService, never())
.addVcnUnderlyingNetworkPolicyListener(
any(IVcnUnderlyingNetworkPolicyListener.class));