summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java4
-rw-r--r--apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl2
-rw-r--r--apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java11
-rw-r--r--apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java359
-rw-r--r--core/api/current.txt10
-rw-r--r--core/api/system-current.txt58
-rw-r--r--core/api/test-current.txt12
-rw-r--r--core/java/android/app/AppOpsManager.java36
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java18
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--core/java/android/appwidget/AppWidgetProviderInfo.java2
-rw-r--r--core/java/android/content/Context.java20
-rw-r--r--core/java/android/content/Intent.java23
-rw-r--r--core/java/android/content/pm/IDataLoaderStatusListener.aidl18
-rw-r--r--core/java/android/net/vcn/VcnManager.java65
-rw-r--r--core/java/android/os/BatteryManagerInternal.java25
-rw-r--r--core/java/android/os/BatteryStats.java18
-rw-r--r--core/java/android/os/BatteryStatsManager.java73
-rw-r--r--core/java/android/os/Vibrator.java22
-rw-r--r--core/java/android/os/VibratorInfo.java39
-rw-r--r--core/java/android/permission/PermissionUsageHelper.java9
-rw-r--r--core/java/android/provider/Telephony.java7
-rw-r--r--core/java/android/telephony/PhoneStateListener.java3
-rw-r--r--core/java/android/telephony/TelephonyCallback.java40
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java16
-rw-r--r--core/java/android/util/Slog.java100
-rw-r--r--core/java/android/view/KeyCharacterMap.java29
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl11
-rw-r--r--core/java/com/android/internal/compat/AndroidBuildClassifier.java10
-rw-r--r--core/java/com/android/internal/compat/OverrideAllowedState.java14
-rw-r--r--core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java2
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java10
-rw-r--r--core/java/com/android/internal/os/CpuPowerCalculator.java98
-rw-r--r--core/java/com/android/internal/telephony/IPhoneStateListener.aidl2
-rw-r--r--core/java/com/android/internal/telephony/ITelephonyRegistry.aidl2
-rw-r--r--core/jni/AndroidRuntime.cpp22
-rw-r--r--core/jni/android_view_KeyCharacterMap.cpp55
-rw-r--r--core/proto/OWNERS3
-rw-r--r--core/res/res/values/attrs.xml2
-rw-r--r--core/tests/coretests/src/android/os/VibratorInfoTest.java181
-rw-r--r--core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java86
-rw-r--r--data/etc/platform.xml1
-rw-r--r--graphics/java/android/graphics/drawable/RippleShader.java57
-rw-r--r--keystore/java/android/security/AndroidKeyStoreMaintenance.java18
-rw-r--r--keystore/java/android/security/KeyStore.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java51
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt34
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt23
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt50
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt29
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt38
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt15
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt26
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt42
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt43
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt35
-rw-r--r--libs/androidfw/AssetManager2.cpp3
-rw-r--r--media/jni/soundpool/Sound.cpp12
-rw-r--r--media/jni/soundpool/Stream.cpp4
-rw-r--r--media/jni/soundpool/StreamManager.cpp2
-rw-r--r--media/jni/soundpool/android_media_SoundPool.cpp3
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java4
-rw-r--r--packages/SystemUI/res/drawable/qs_tile_background.xml23
-rw-r--r--packages/SystemUI/res/drawable/qs_tile_background_shape.xml21
-rw-r--r--packages/SystemUI/res/layout/udfps_bp_view.xml (renamed from packages/SystemUI/res/layout/udfps_animation_view_bp.xml)4
-rw-r--r--packages/SystemUI/res/layout/udfps_enroll_view.xml (renamed from packages/SystemUI/res/layout/udfps_animation_view_enroll.xml)11
-rw-r--r--packages/SystemUI/res/layout/udfps_fpm_other_view.xml (renamed from packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml)10
-rw-r--r--packages/SystemUI/res/layout/udfps_keyguard_view.xml (renamed from packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml)12
-rw-r--r--packages/SystemUI/res/layout/udfps_view.xml5
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java135
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java167
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java96
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java)19
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java112
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java)45
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java)51
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java)32
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java)29
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java)27
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java)59
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java170
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java138
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt30
-rw-r--r--services/core/java/com/android/server/BatteryService.java132
-rw-r--r--services/core/java/com/android/server/OWNERS1
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java45
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java4
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java44
-rw-r--r--services/core/java/com/android/server/app/GameManagerSettings.java5
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java36
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java2
-rw-r--r--services/core/java/com/android/server/compat/CompatChange.java13
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java8
-rw-r--r--services/core/java/com/android/server/compat/OverrideValidatorImpl.java4
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java11
-rw-r--r--services/core/java/com/android/server/location/gnss/hal/GnssNative.java194
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java47
-rw-r--r--services/core/java/com/android/server/pm/DataLoaderManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java29
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java8
-rw-r--r--services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java344
-rw-r--r--services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java49
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java4
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java6
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java11
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java22
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java4
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java9
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java14
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java42
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorController.java121
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java1
-rw-r--r--services/core/jni/com_android_server_vibrator_VibratorController.cpp69
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java47
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java24
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java31
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java34
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java28
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java41
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java11
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java12
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java7
-rw-r--r--services/incremental/IncrementalService.cpp90
-rw-r--r--services/incremental/IncrementalService.h7
-rw-r--r--services/incremental/ServiceWrappers.cpp12
-rw-r--r--services/incremental/ServiceWrappers.h8
-rw-r--r--services/incremental/test/IncrementalServiceTest.cpp162
-rw-r--r--services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java117
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java57
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java100
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java105
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java4
-rw-r--r--telecomm/java/android/telecom/CallDiagnosticService.java112
-rw-r--r--telecomm/java/android/telecom/DiagnosticCall.java162
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java4
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java67
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java65
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java12
-rw-r--r--telephony/java/android/telephony/data/DataService.java34
-rw-r--r--telephony/java/android/telephony/ims/ImsCallProfile.java8
-rw-r--r--telephony/java/android/telephony/ims/ProvisioningManager.java26
-rw-r--r--telephony/java/android/telephony/ims/RcsContactPresenceTuple.java25
-rw-r--r--telephony/java/android/telephony/ims/RcsUceAdapter.java90
-rw-r--r--telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl1
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java25
-rw-r--r--telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java35
-rwxr-xr-xtelephony/java/com/android/internal/telephony/ISub.aidl2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt128
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt128
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt157
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt130
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt101
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt170
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt121
-rw-r--r--tests/Input/Android.bp15
-rw-r--r--tests/Input/src/com/android/test/input/InputDeviceTest.java98
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java10
206 files changed, 5357 insertions, 2839 deletions
diff --git a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
index 752c36e53bf9..6cdf5853339a 100644
--- a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
+++ b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
@@ -65,7 +65,7 @@ public class DeviceIdleManager {
* @return package names the system has white-listed to opt out of power save restrictions,
* except for device idle mode.
*
- * @hide Should be migrated to PowerWhitelistManager
+ * @hide Should be migrated to PowerExemptionManager
*/
@TestApi
public @NonNull String[] getSystemPowerWhitelistExceptIdle() {
@@ -80,7 +80,7 @@ public class DeviceIdleManager {
* @return package names the system has white-listed to opt out of power save restrictions for
* all modes.
*
- * @hide Should be migrated to PowerWhitelistManager
+ * @hide Should be migrated to PowerExemptionManager
*/
@TestApi
public @NonNull String[] getSystemPowerWhitelist() {
diff --git a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
index 43d4873a3540..9d18dfe98a34 100644
--- a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
+++ b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
@@ -42,7 +42,7 @@ interface IDeviceIdleController {
boolean isPowerSaveWhitelistExceptIdleApp(String name);
boolean isPowerSaveWhitelistApp(String name);
@UnsupportedAppUsage(maxTargetSdk = 30,
- publicAlternatives = "Use SystemApi {@code PowerWhitelistManager#whitelistAppTemporarily(String, int, String)}.")
+ publicAlternatives = "Use SystemApi {@code PowerExemptionManager#addToTemporaryAllowList(String, int, int, String)}.")
void addPowerSaveTempWhitelistApp(String name, long duration, int userId, int reasonCode, String reason);
long addPowerSaveTempWhitelistAppForMms(String name, int userId, int reasonCode, String reason);
long addPowerSaveTempWhitelistAppForSms(String name, int userId, int reasonCode, String reason);
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 8445335b568e..d9a49aa52365 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -170,7 +170,7 @@ public class PowerExemptionManager {
/** @hide */
public static final int REASON_EXEMPTED_PACKAGE = 64;
/** @hide */
- public static final int REASON_ALLOWLISTED_PACKAGE = 65;
+ public static final int REASON_ALLOWLISTED_PACKAGE = 65;
/** @hide */
public static final int REASON_APPOP = 66;
@@ -193,6 +193,10 @@ public class PowerExemptionManager {
* Set temp-allow-list for activity recognition.
*/
public static final int REASON_ACTIVITY_RECOGNITION = 103;
+ /**
+ * Set temp-allow-list for transferring accounts between users.
+ */
+ public static final int REASON_ACCOUNT_TRANSFER = 104;
/* Reason code range 200-299 are reserved for broadcast actions */
/**
@@ -216,7 +220,7 @@ public class PowerExemptionManager {
* Device idle system allow list, including EXCEPT-IDLE
* @hide
*/
- public static final int REASON_SYSTEM_ALLOW_LISTED = 300;
+ public static final int REASON_SYSTEM_ALLOW_LISTED = 300;
/** @hide */
public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 301;
/**
@@ -329,6 +333,7 @@ public class PowerExemptionManager {
REASON_PUSH_MESSAGING,
REASON_PUSH_MESSAGING_OVER_QUOTA,
REASON_ACTIVITY_RECOGNITION,
+ REASON_ACCOUNT_TRANSFER,
REASON_BOOT_COMPLETED,
REASON_PRE_BOOT_COMPLETED,
REASON_LOCKED_BOOT_COMPLETED,
@@ -579,6 +584,8 @@ public class PowerExemptionManager {
return "PUSH_MESSAGING_OVER_QUOTA";
case REASON_ACTIVITY_RECOGNITION:
return "ACTIVITY_RECOGNITION";
+ case REASON_ACCOUNT_TRANSFER:
+ return "REASON_ACCOUNT_TRANSFER";
case REASON_BOOT_COMPLETED:
return "BOOT_COMPLETED";
case REASON_PRE_BOOT_COMPLETED:
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index b1b733a599c6..eba39c7573be 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -16,13 +16,6 @@
package android.os;
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
-import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
-import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
-import static android.app.ActivityManager.PROCESS_STATE_TOP;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -33,7 +26,6 @@ import android.content.Context;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
import java.util.List;
/**
@@ -43,9 +35,11 @@ import java.util.List;
* placed on the temporary whitelist are removed from that whitelist after a predetermined amount of
* time.
*
+ * @deprecated Use {@link PowerExemptionManager} instead
* @hide
*/
@SystemApi
+@Deprecated
@SystemService(Context.POWER_WHITELIST_MANAGER)
public class PowerWhitelistManager {
private final Context mContext;
@@ -53,21 +47,23 @@ public class PowerWhitelistManager {
// TODO: migrate to PowerWhitelistController
private final IDeviceIdleController mService;
+ private final PowerExemptionManager mPowerExemptionManager;
+
/**
* Indicates that an unforeseen event has occurred and the app should be whitelisted to handle
* it.
*/
- public static final int EVENT_UNSPECIFIED = 0;
+ public static final int EVENT_UNSPECIFIED = PowerExemptionManager.EVENT_UNSPECIFIED;
/**
* Indicates that an SMS event has occurred and the app should be whitelisted to handle it.
*/
- public static final int EVENT_SMS = 1;
+ public static final int EVENT_SMS = PowerExemptionManager.EVENT_SMS;
/**
* Indicates that an MMS event has occurred and the app should be whitelisted to handle it.
*/
- public static final int EVENT_MMS = 2;
+ public static final int EVENT_MMS = PowerExemptionManager.EVENT_MMS;
/**
* @hide
@@ -84,12 +80,14 @@ public class PowerWhitelistManager {
/**
* Allow the temp allowlist behavior, plus allow foreground service start from background.
*/
- public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
+ public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED =
+ PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
/**
* Only allow the temp allowlist behavior, not allow foreground service start from
* background.
*/
- public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
+ public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED =
+ PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
/**
* The list of temp allowlist types.
@@ -107,73 +105,83 @@ public class PowerWhitelistManager {
* BG-FGS-launch is denied.
* @hide
*/
- public static final int REASON_DENIED = -1;
+ public static final int REASON_DENIED = PowerExemptionManager.REASON_DENIED;
/* Reason code range 0-9 are reserved for default reasons */
/**
* The default reason code if reason is unknown.
*/
- public static final int REASON_UNKNOWN = 0;
+ public static final int REASON_UNKNOWN = PowerExemptionManager.REASON_UNKNOWN;
/**
* Use REASON_OTHER if there is no better choice.
*/
- public static final int REASON_OTHER = 1;
+ public static final int REASON_OTHER = PowerExemptionManager.REASON_OTHER;
/* Reason code range 10-49 are reserved for BG-FGS-launch allowed proc states */
/** @hide */
- public static final int REASON_PROC_STATE_PERSISTENT = 10;
+ public static final int REASON_PROC_STATE_PERSISTENT =
+ PowerExemptionManager.REASON_PROC_STATE_PERSISTENT;
/** @hide */
- public static final int REASON_PROC_STATE_PERSISTENT_UI = 11;
+ public static final int REASON_PROC_STATE_PERSISTENT_UI =
+ PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI;
/** @hide */
- public static final int REASON_PROC_STATE_TOP = 12;
+ public static final int REASON_PROC_STATE_TOP = PowerExemptionManager.REASON_PROC_STATE_TOP;
/** @hide */
- public static final int REASON_PROC_STATE_BTOP = 13;
+ public static final int REASON_PROC_STATE_BTOP = PowerExemptionManager.REASON_PROC_STATE_BTOP;
/** @hide */
- public static final int REASON_PROC_STATE_FGS = 14;
+ public static final int REASON_PROC_STATE_FGS = PowerExemptionManager.REASON_PROC_STATE_FGS;
/** @hide */
- public static final int REASON_PROC_STATE_BFGS = 15;
+ public static final int REASON_PROC_STATE_BFGS = PowerExemptionManager.REASON_PROC_STATE_BFGS;
/* Reason code range 50-99 are reserved for BG-FGS-launch allowed reasons */
/** @hide */
- public static final int REASON_UID_VISIBLE = 50;
+ public static final int REASON_UID_VISIBLE = PowerExemptionManager.REASON_UID_VISIBLE;
/** @hide */
- public static final int REASON_SYSTEM_UID = 51;
+ public static final int REASON_SYSTEM_UID = PowerExemptionManager.REASON_SYSTEM_UID;
/** @hide */
- public static final int REASON_ACTIVITY_STARTER = 52;
+ public static final int REASON_ACTIVITY_STARTER = PowerExemptionManager.REASON_ACTIVITY_STARTER;
/** @hide */
- public static final int REASON_START_ACTIVITY_FLAG = 53;
+ public static final int REASON_START_ACTIVITY_FLAG =
+ PowerExemptionManager.REASON_START_ACTIVITY_FLAG;
/** @hide */
- public static final int REASON_FGS_BINDING = 54;
+ public static final int REASON_FGS_BINDING = PowerExemptionManager.REASON_FGS_BINDING;
/** @hide */
- public static final int REASON_DEVICE_OWNER = 55;
+ public static final int REASON_DEVICE_OWNER = PowerExemptionManager.REASON_DEVICE_OWNER;
/** @hide */
- public static final int REASON_PROFILE_OWNER = 56;
+ public static final int REASON_PROFILE_OWNER = PowerExemptionManager.REASON_PROFILE_OWNER;
/** @hide */
- public static final int REASON_COMPANION_DEVICE_MANAGER = 57;
+ public static final int REASON_COMPANION_DEVICE_MANAGER =
+ PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
/**
* START_ACTIVITIES_FROM_BACKGROUND permission.
* @hide
*/
- public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION = 58;
+ public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION =
+ PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
/**
* START_FOREGROUND_SERVICES_FROM_BACKGROUND permission.
* @hide
*/
- public static final int REASON_BACKGROUND_FGS_PERMISSION = 59;
+ public static final int REASON_BACKGROUND_FGS_PERMISSION =
+ PowerExemptionManager.REASON_BACKGROUND_FGS_PERMISSION;
/** @hide */
- public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 60;
+ public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION =
+ PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
/** @hide */
- public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION = 61;
+ public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION =
+ PowerExemptionManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION;
/** @hide */
- public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION = 62;
+ public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION =
+ PowerExemptionManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
/** @hide */
- public static final int REASON_DEVICE_DEMO_MODE = 63;
+ public static final int REASON_DEVICE_DEMO_MODE = PowerExemptionManager.REASON_DEVICE_DEMO_MODE;
/** @hide */
- public static final int REASON_EXEMPTED_PACKAGE = 64;
+ public static final int REASON_EXEMPTED_PACKAGE = PowerExemptionManager.REASON_EXEMPTED_PACKAGE;
/** @hide */
- public static final int REASON_ALLOWLISTED_PACKAGE = 65;
+ public static final int REASON_ALLOWLISTED_PACKAGE =
+ PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE;
/** @hide */
- public static final int REASON_APPOP = 66;
+ public static final int REASON_APPOP = PowerExemptionManager.REASON_APPOP;
/* BG-FGS-launch is allowed by temp-allowlist or system-allowlist.
Reason code for temp and system allowlist starts here.
@@ -181,117 +189,128 @@ public class PowerWhitelistManager {
/**
* Set temp-allowlist for location geofence purpose.
*/
- public static final int REASON_GEOFENCING = 100;
+ public static final int REASON_GEOFENCING = PowerExemptionManager.REASON_GEOFENCING;
/**
* Set temp-allowlist for server push messaging.
*/
- public static final int REASON_PUSH_MESSAGING = 101;
+ public static final int REASON_PUSH_MESSAGING = PowerExemptionManager.REASON_PUSH_MESSAGING;
/**
* Set temp-allowlist for server push messaging over the quota.
*/
- public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102;
+ public static final int REASON_PUSH_MESSAGING_OVER_QUOTA =
+ PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA;
/**
* Set temp-allowlist for activity recognition.
*/
- public static final int REASON_ACTIVITY_RECOGNITION = 103;
+ public static final int REASON_ACTIVITY_RECOGNITION =
+ PowerExemptionManager.REASON_ACTIVITY_RECOGNITION;
/* Reason code range 200-299 are reserved for broadcast actions */
/**
* Broadcast ACTION_BOOT_COMPLETED.
* @hide
*/
- public static final int REASON_BOOT_COMPLETED = 200;
+ public static final int REASON_BOOT_COMPLETED = PowerExemptionManager.REASON_BOOT_COMPLETED;
/**
* Broadcast ACTION_PRE_BOOT_COMPLETED.
* @hide
*/
- public static final int REASON_PRE_BOOT_COMPLETED = 201;
+ public static final int REASON_PRE_BOOT_COMPLETED =
+ PowerExemptionManager.REASON_PRE_BOOT_COMPLETED;
/**
* Broadcast ACTION_LOCKED_BOOT_COMPLETED.
* @hide
*/
- public static final int REASON_LOCKED_BOOT_COMPLETED = 202;
+ public static final int REASON_LOCKED_BOOT_COMPLETED =
+ PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
/* Reason code range 300-399 are reserved for other internal reasons */
/**
* Device idle system allowlist, including EXCEPT-IDLE
* @hide
*/
- public static final int REASON_SYSTEM_ALLOW_LISTED = 300;
+ public static final int REASON_SYSTEM_ALLOW_LISTED =
+ PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
/** @hide */
- public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 301;
+ public static final int REASON_ALARM_MANAGER_ALARM_CLOCK =
+ PowerExemptionManager.REASON_ALARM_MANAGER_ALARM_CLOCK;
/**
* AlarmManagerService.
* @hide
*/
- public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 302;
+ public static final int REASON_ALARM_MANAGER_WHILE_IDLE =
+ PowerExemptionManager.REASON_ALARM_MANAGER_WHILE_IDLE;
/**
* ActiveServices.
* @hide
*/
- public static final int REASON_SERVICE_LAUNCH = 303;
+ public static final int REASON_SERVICE_LAUNCH = PowerExemptionManager.REASON_SERVICE_LAUNCH;
/**
* KeyChainSystemService.
* @hide
*/
- public static final int REASON_KEY_CHAIN = 304;
+ public static final int REASON_KEY_CHAIN = PowerExemptionManager.REASON_KEY_CHAIN;
/**
* PackageManagerService.
* @hide
*/
- public static final int REASON_PACKAGE_VERIFIER = 305;
+ public static final int REASON_PACKAGE_VERIFIER = PowerExemptionManager.REASON_PACKAGE_VERIFIER;
/**
* SyncManager.
* @hide
*/
- public static final int REASON_SYNC_MANAGER = 306;
+ public static final int REASON_SYNC_MANAGER = PowerExemptionManager.REASON_SYNC_MANAGER;
/**
* DomainVerificationProxyV1.
* @hide
*/
- public static final int REASON_DOMAIN_VERIFICATION_V1 = 307;
+ public static final int REASON_DOMAIN_VERIFICATION_V1 =
+ PowerExemptionManager.REASON_DOMAIN_VERIFICATION_V1;
/**
* DomainVerificationProxyV2.
* @hide
*/
- public static final int REASON_DOMAIN_VERIFICATION_V2 = 308;
+ public static final int REASON_DOMAIN_VERIFICATION_V2 =
+ PowerExemptionManager.REASON_DOMAIN_VERIFICATION_V2;
/** @hide */
public static final int REASON_VPN = 309;
/**
* NotificationManagerService.
* @hide
*/
- public static final int REASON_NOTIFICATION_SERVICE = 310;
+ public static final int REASON_NOTIFICATION_SERVICE =
+ PowerExemptionManager.REASON_NOTIFICATION_SERVICE;
/**
* Broadcast ACTION_MY_PACKAGE_REPLACED.
* @hide
*/
- public static final int REASON_PACKAGE_REPLACED = 311;
+ public static final int REASON_PACKAGE_REPLACED = PowerExemptionManager.REASON_PACKAGE_REPLACED;
/**
* LocationProviderManager.
* @hide
*/
- public static final int REASON_LOCATION_PROVIDER = 312;
+ public static final int REASON_LOCATION_PROVIDER =
+ PowerExemptionManager.REASON_LOCATION_PROVIDER;
/**
* MediaButtonReceiver.
* @hide
*/
- public static final int REASON_MEDIA_BUTTON = 313;
+ public static final int REASON_MEDIA_BUTTON = PowerExemptionManager.REASON_MEDIA_BUTTON;
/**
* InboundSmsHandler.
* @hide
*/
- public static final int REASON_EVENT_SMS = 314;
+ public static final int REASON_EVENT_SMS = PowerExemptionManager.REASON_EVENT_SMS;
/**
* InboundSmsHandler.
* @hide
*/
- public static final int REASON_EVENT_MMS = 315;
+ public static final int REASON_EVENT_MMS = PowerExemptionManager.REASON_EVENT_MMS;
/**
* Shell app.
* @hide
*/
- public static final int REASON_SHELL = 316;
+ public static final int REASON_SHELL = PowerExemptionManager.REASON_SHELL;
/**
* The list of BG-FGS-Launch and temp-allowlist reason code.
@@ -360,26 +379,29 @@ public class PowerWhitelistManager {
public PowerWhitelistManager(@NonNull Context context) {
mContext = context;
mService = context.getSystemService(DeviceIdleManager.class).getService();
+ mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class);
}
/**
* Add the specified package to the permanent power save whitelist.
+ *
+ * @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(String)} instead
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
public void addToWhitelist(@NonNull String packageName) {
- addToWhitelist(Collections.singletonList(packageName));
+ mPowerExemptionManager.addToPermanentAllowList(packageName);
}
/**
* Add the specified packages to the permanent power save whitelist.
+ *
+ * @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(List)} instead
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
public void addToWhitelist(@NonNull List<String> packageNames) {
- try {
- mService.addPowerSaveWhitelistApps(packageNames);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mPowerExemptionManager.addToPermanentAllowList(packageNames);
}
/**
@@ -388,19 +410,13 @@ public class PowerWhitelistManager {
*
* @param includingIdle Set to true if the app should be whitelisted from device idle as well
* as other power save restrictions
+ * @deprecated Use {@link PowerExemptionManager#getAllowListedAppIds(boolean)} instead
* @hide
*/
+ @Deprecated
@NonNull
public int[] getWhitelistedAppIds(boolean includingIdle) {
- try {
- if (includingIdle) {
- return mService.getAppIdWhitelist();
- } else {
- return mService.getAppIdWhitelistExceptIdle();
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mPowerExemptionManager.getAllowListedAppIds(includingIdle);
}
/**
@@ -409,18 +425,12 @@ public class PowerWhitelistManager {
*
* @param includingIdle Set to true if the app should be whitelisted from device
* idle as well as other power save restrictions
+ * @deprecated Use {@link PowerExemptionManager#isAllowListed(String, boolean)} instead
* @hide
*/
+ @Deprecated
public boolean isWhitelisted(@NonNull String packageName, boolean includingIdle) {
- try {
- if (includingIdle) {
- return mService.isPowerSaveWhitelistApp(packageName);
- } else {
- return mService.isPowerSaveWhitelistExceptIdleApp(packageName);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mPowerExemptionManager.isAllowListed(packageName, includingIdle);
}
/**
@@ -429,14 +439,12 @@ public class PowerWhitelistManager {
* whitelisted by default by the system cannot be removed.
*
* @param packageName The app to remove from the whitelist
+ * @deprecated Use {@link PowerExemptionManager#removeFromAllowList(String)} instead
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
public void removeFromWhitelist(@NonNull String packageName) {
- try {
- mService.removePowerSaveWhitelistApp(packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mPowerExemptionManager.removeFromAllowList(packageName);
}
/**
@@ -446,16 +454,14 @@ public class PowerWhitelistManager {
* @param durationMs How long to keep the app on the temp whitelist for (in milliseconds)
* @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
* @param reason a optional human readable reason string, could be null or empty string.
+ * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
+ * String, long, int, String)} instead
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public void whitelistAppTemporarily(@NonNull String packageName, long durationMs,
@ReasonCode int reasonCode, @Nullable String reason) {
- try {
- mService.addPowerSaveTempWhitelistApp(packageName, durationMs, mContext.getUserId(),
- reasonCode, reason);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mPowerExemptionManager.addToTemporaryAllowList(packageName, durationMs, reasonCode, reason);
}
/**
@@ -463,12 +469,14 @@ public class PowerWhitelistManager {
*
* @param packageName The package to add to the temp whitelist
* @param durationMs How long to keep the app on the temp whitelist for (in milliseconds)
- * @deprecated Use {@link #whitelistAppTemporarily(String, long, int, String)} instead
+ * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
+ * String, long, int, String)} instead
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) {
- whitelistAppTemporarily(packageName, durationMs, REASON_UNKNOWN, packageName);
+ mPowerExemptionManager.addToTemporaryAllowList(
+ packageName, durationMs, REASON_UNKNOWN, packageName);
}
/**
@@ -481,13 +489,15 @@ public class PowerWhitelistManager {
* @param reason A human-readable reason explaining why the app is temp whitelisted. Only
* used for logging purposes. Could be null or empty string.
* @return The duration (in milliseconds) that the app is whitelisted for
- * @deprecated Use {@link #whitelistAppTemporarilyForEvent(String, int, int, String)} instead
+ * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
+ * String, int, int, String)} instead
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
@WhitelistEvent int event, @Nullable String reason) {
- return whitelistAppTemporarilyForEvent(packageName, event, REASON_UNKNOWN, reason);
+ return mPowerExemptionManager.addToTemporaryAllowListForEvent(
+ packageName, event, REASON_UNKNOWN, reason);
}
/**
@@ -501,47 +511,25 @@ public class PowerWhitelistManager {
* @param reason A human-readable reason explaining why the app is temp whitelisted. Only
* used for logging purposes. Could be null or empty string.
* @return The duration (in milliseconds) that the app is whitelisted for
+ * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
+ * String, int, int, String)} instead
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
@WhitelistEvent int event, @ReasonCode int reasonCode, @Nullable String reason) {
- try {
- switch (event) {
- case EVENT_MMS:
- return mService.addPowerSaveTempWhitelistAppForMms(
- packageName, mContext.getUserId(), reasonCode, reason);
- case EVENT_SMS:
- return mService.addPowerSaveTempWhitelistAppForSms(
- packageName, mContext.getUserId(), reasonCode, reason);
- case EVENT_UNSPECIFIED:
- default:
- return mService.whitelistAppTemporarily(
- packageName, mContext.getUserId(), reasonCode, reason);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mPowerExemptionManager.addToTemporaryAllowListForEvent(
+ packageName, event, reasonCode, reason);
}
/**
* @hide
+ *
+ * @deprecated Use {@link PowerExemptionManager#getReasonCodeFromProcState(int)} instead
*/
+ @Deprecated
public static @ReasonCode int getReasonCodeFromProcState(int procState) {
- if (procState <= PROCESS_STATE_PERSISTENT) {
- return REASON_PROC_STATE_PERSISTENT;
- } else if (procState <= PROCESS_STATE_PERSISTENT_UI) {
- return REASON_PROC_STATE_PERSISTENT_UI;
- } else if (procState <= PROCESS_STATE_TOP) {
- return REASON_PROC_STATE_TOP;
- } else if (procState <= PROCESS_STATE_BOUND_TOP) {
- return REASON_PROC_STATE_BTOP;
- } else if (procState <= PROCESS_STATE_FOREGROUND_SERVICE) {
- return REASON_PROC_STATE_FGS;
- } else if (procState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
- return REASON_PROC_STATE_BFGS;
- } else {
- return REASON_DENIED;
- }
+ return PowerExemptionManager.getReasonCodeFromProcState(procState);
}
/**
@@ -549,111 +537,10 @@ public class PowerWhitelistManager {
* @hide
* @param reasonCode
* @return string name of the reason code.
+ * @deprecated Use {@link PowerExemptionManager#reasonCodeToString(int)} instead
*/
+ @Deprecated
public static String reasonCodeToString(@ReasonCode int reasonCode) {
- switch (reasonCode) {
- case REASON_DENIED:
- return "DENIED";
- case REASON_UNKNOWN:
- return "UNKNOWN";
- case REASON_OTHER:
- return "OTHER";
- case REASON_PROC_STATE_PERSISTENT:
- return "PROC_STATE_PERSISTENT";
- case REASON_PROC_STATE_PERSISTENT_UI:
- return "PROC_STATE_PERSISTENT_UI";
- case REASON_PROC_STATE_TOP:
- return "PROC_STATE_TOP";
- case REASON_PROC_STATE_BTOP:
- return "PROC_STATE_BTOP";
- case REASON_PROC_STATE_FGS:
- return "PROC_STATE_FGS";
- case REASON_PROC_STATE_BFGS:
- return "PROC_STATE_BFGS";
- case REASON_UID_VISIBLE:
- return "UID_VISIBLE";
- case REASON_SYSTEM_UID:
- return "SYSTEM_UID";
- case REASON_ACTIVITY_STARTER:
- return "ACTIVITY_STARTER";
- case REASON_START_ACTIVITY_FLAG:
- return "START_ACTIVITY_FLAG";
- case REASON_FGS_BINDING:
- return "FGS_BINDING";
- case REASON_DEVICE_OWNER:
- return "DEVICE_OWNER";
- case REASON_PROFILE_OWNER:
- return "PROFILE_OWNER";
- case REASON_COMPANION_DEVICE_MANAGER:
- return "COMPANION_DEVICE_MANAGER";
- case REASON_BACKGROUND_ACTIVITY_PERMISSION:
- return "BACKGROUND_ACTIVITY_PERMISSION";
- case REASON_BACKGROUND_FGS_PERMISSION:
- return "BACKGROUND_FGS_PERMISSION";
- case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION:
- return "INSTR_BACKGROUND_ACTIVITY_PERMISSION";
- case REASON_INSTR_BACKGROUND_FGS_PERMISSION:
- return "INSTR_BACKGROUND_FGS_PERMISSION";
- case REASON_SYSTEM_ALERT_WINDOW_PERMISSION:
- return "SYSTEM_ALERT_WINDOW_PERMISSION";
- case REASON_DEVICE_DEMO_MODE:
- return "DEVICE_DEMO_MODE";
- case REASON_EXEMPTED_PACKAGE:
- return "EXEMPTED_PACKAGE";
- case REASON_ALLOWLISTED_PACKAGE:
- return "ALLOWLISTED_PACKAGE";
- case REASON_APPOP:
- return "APPOP";
- case REASON_GEOFENCING:
- return "GEOFENCING";
- case REASON_PUSH_MESSAGING:
- return "PUSH_MESSAGING";
- case REASON_PUSH_MESSAGING_OVER_QUOTA:
- return "PUSH_MESSAGING_OVER_QUOTA";
- case REASON_ACTIVITY_RECOGNITION:
- return "ACTIVITY_RECOGNITION";
- case REASON_BOOT_COMPLETED:
- return "BOOT_COMPLETED";
- case REASON_PRE_BOOT_COMPLETED:
- return "PRE_BOOT_COMPLETED";
- case REASON_LOCKED_BOOT_COMPLETED:
- return "LOCKED_BOOT_COMPLETED";
- case REASON_SYSTEM_ALLOW_LISTED:
- return "SYSTEM_ALLOW_LISTED";
- case REASON_ALARM_MANAGER_ALARM_CLOCK:
- return "ALARM_MANAGER_ALARM_CLOCK";
- case REASON_ALARM_MANAGER_WHILE_IDLE:
- return "ALARM_MANAGER_WHILE_IDLE";
- case REASON_SERVICE_LAUNCH:
- return "SERVICE_LAUNCH";
- case REASON_KEY_CHAIN:
- return "KEY_CHAIN";
- case REASON_PACKAGE_VERIFIER:
- return "PACKAGE_VERIFIER";
- case REASON_SYNC_MANAGER:
- return "SYNC_MANAGER";
- case REASON_DOMAIN_VERIFICATION_V1:
- return "DOMAIN_VERIFICATION_V1";
- case REASON_DOMAIN_VERIFICATION_V2:
- return "DOMAIN_VERIFICATION_V2";
- case REASON_VPN:
- return "VPN";
- case REASON_NOTIFICATION_SERVICE:
- return "NOTIFICATION_SERVICE";
- case REASON_PACKAGE_REPLACED:
- return "PACKAGE_REPLACED";
- case REASON_LOCATION_PROVIDER:
- return "LOCATION_PROVIDER";
- case REASON_MEDIA_BUTTON:
- return "MEDIA_BUTTON";
- case REASON_EVENT_SMS:
- return "EVENT_SMS";
- case REASON_EVENT_MMS:
- return "EVENT_MMS";
- case REASON_SHELL:
- return "SHELL";
- default:
- return "(unknown:" + reasonCode + ")";
- }
+ return PowerExemptionManager.reasonCodeToString(reasonCode);
}
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 829bb3fcf328..4a1c325ebff7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8491,7 +8491,7 @@ package android.appwidget {
field public static final int WIDGET_CATEGORY_HOME_SCREEN = 1; // 0x1
field public static final int WIDGET_CATEGORY_KEYGUARD = 2; // 0x2
field public static final int WIDGET_CATEGORY_SEARCHBOX = 4; // 0x4
- field public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 3; // 0x3
+ field public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 4; // 0x4
field public static final int WIDGET_FEATURE_HIDE_FROM_PICKER = 2; // 0x2
field public static final int WIDGET_FEATURE_RECONFIGURABLE = 1; // 0x1
field public int autoAdvanceViewId;
@@ -10501,6 +10501,7 @@ package android.content {
field public static final String DEVICE_POLICY_SERVICE = "device_policy";
field public static final String DISPLAY_HASH_SERVICE = "display_hash";
field public static final String DISPLAY_SERVICE = "display";
+ field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
field public static final String DOWNLOAD_SERVICE = "download";
field public static final String DROPBOX_SERVICE = "dropbox";
field public static final String EUICC_SERVICE = "euicc";
@@ -42015,6 +42016,7 @@ package android.telephony {
method public static int getDefaultSmsSubscriptionId();
method public static int getDefaultSubscriptionId();
method public static int getDefaultVoiceSubscriptionId();
+ method public int getDeviceToDeviceStatusSharing(int);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
method public static int getSlotIndex(int);
method @Nullable public int[] getSubscriptionIds(int);
@@ -42027,6 +42029,7 @@ package android.telephony {
method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharing(int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int);
method public void setSubscriptionOverrideCongested(int, boolean, long);
method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long);
@@ -42038,6 +42041,11 @@ package android.telephony {
field public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
field public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
field public static final String ACTION_REFRESH_SUBSCRIPTION_PLANS = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
+ field public static final int D2D_SHARING_ALL = 3; // 0x3
+ field public static final int D2D_SHARING_ALL_CONTACTS = 1; // 0x1
+ field public static final int D2D_SHARING_DISABLED = 0; // 0x0
+ field public static final int D2D_SHARING_STARRED_CONTACTS = 2; // 0x2
+ field public static final String D2D_STATUS_SHARING = "d2d_sharing_status";
field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
field public static final int DEFAULT_SUBSCRIPTION_ID = 2147483647; // 0x7fffffff
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6019ab56dea7..bfc205b9f9a6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2162,7 +2162,6 @@ package android.content {
field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
- field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
field public static final String ETHERNET_SERVICE = "ethernet";
field public static final String EUICC_CARD_SERVICE = "euicc_card";
field public static final String FONT_SERVICE = "font";
@@ -2174,7 +2173,6 @@ package android.content {
field public static final String OEM_LOCK_SERVICE = "oem_lock";
field public static final String PERMISSION_SERVICE = "permission";
field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
- field public static final String POWER_EXEMPTION_SERVICE = "power_exemption";
field public static final String REBOOT_READINESS_SERVICE = "reboot_readiness";
field public static final String ROLLBACK_SERVICE = "rollback";
field public static final String SEARCH_UI_SERVICE = "search_ui";
@@ -2241,6 +2239,7 @@ package android.content {
field @RequiresPermission(android.Manifest.permission.REVIEW_ACCESSIBILITY_SERVICES) public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES";
field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_ONGOING_PERMISSION_USAGE = "android.intent.action.REVIEW_ONGOING_PERMISSION_USAGE";
field public static final String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
+ field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_PERMISSION_HISTORY = "android.intent.action.REVIEW_PERMISSION_HISTORY";
field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
field public static final String ACTION_ROLLBACK_COMMITTED = "android.intent.action.ROLLBACK_COMMITTED";
field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
@@ -7521,12 +7520,12 @@ package android.net.util {
package android.net.vcn {
public class VcnManager {
- method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.vcn.VcnNetworkPolicyResult applyVcnNetworkPolicy(@NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties);
- method public void removeVcnNetworkPolicyListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void removeVcnNetworkPolicyChangeListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
}
- public static interface VcnManager.VcnNetworkPolicyListener {
+ public static interface VcnManager.VcnNetworkPolicyChangeListener {
method public void onPolicyChanged();
}
@@ -8184,6 +8183,7 @@ package android.os {
field public static final int EVENT_MMS = 2; // 0x2
field public static final int EVENT_SMS = 1; // 0x1
field public static final int EVENT_UNSPECIFIED = 0; // 0x0
+ field public static final int REASON_ACCOUNT_TRANSFER = 104; // 0x68
field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
field public static final int REASON_GEOFENCING = 100; // 0x64
field public static final int REASON_OTHER = 1; // 0x1
@@ -8224,25 +8224,25 @@ package android.os {
field public static final int USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS = 1; // 0x1
}
- public class PowerWhitelistManager {
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String);
+ @Deprecated public class PowerWhitelistManager {
+ method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String);
method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long);
method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @Nullable String);
- method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String);
- field public static final int EVENT_MMS = 2; // 0x2
- field public static final int EVENT_SMS = 1; // 0x1
- field public static final int EVENT_UNSPECIFIED = 0; // 0x0
- field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
- field public static final int REASON_GEOFENCING = 100; // 0x64
- field public static final int REASON_OTHER = 1; // 0x1
- field public static final int REASON_PUSH_MESSAGING = 101; // 0x65
- field public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
- field public static final int REASON_UNKNOWN = 0; // 0x0
- field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
- field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
+ method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String);
+ field @Deprecated public static final int EVENT_MMS = 2; // 0x2
+ field @Deprecated public static final int EVENT_SMS = 1; // 0x1
+ field @Deprecated public static final int EVENT_UNSPECIFIED = 0; // 0x0
+ field @Deprecated public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
+ field @Deprecated public static final int REASON_GEOFENCING = 100; // 0x64
+ field @Deprecated public static final int REASON_OTHER = 1; // 0x1
+ field @Deprecated public static final int REASON_PUSH_MESSAGING = 101; // 0x65
+ field @Deprecated public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
+ field @Deprecated public static final int REASON_UNKNOWN = 0; // 0x0
+ field @Deprecated public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
+ field @Deprecated public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
}
public class RecoverySystem {
@@ -10363,6 +10363,7 @@ package android.telecom {
public abstract class CallDiagnosticService extends android.app.Service {
ctor public CallDiagnosticService();
+ method @NonNull public java.util.concurrent.Executor getExecutor();
method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onBluetoothCallQualityReportReceived(@NonNull android.telecom.BluetoothCallQualityReport);
method public abstract void onCallAudioStateChanged(@NonNull android.telecom.CallAudioState);
@@ -10435,16 +10436,12 @@ package android.telecom {
ctor public DiagnosticCall();
method public final void clearDiagnosticMessage(int);
method public final void displayDiagnosticMessage(int, @NonNull CharSequence);
- method @NonNull public android.telecom.Call.Details getCallDetails();
method public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details);
method @Nullable public abstract CharSequence onCallDisconnected(int, int);
method @Nullable public abstract CharSequence onCallDisconnected(@NonNull android.telephony.ims.ImsReasonInfo);
method public abstract void onCallQualityReceived(@NonNull android.telephony.CallQuality);
method public abstract void onReceiveDeviceToDeviceMessage(int, int);
method public final void sendDeviceToDeviceMessage(int, int);
- field public static final int AUDIO_CODEC_AMR_NB = 3; // 0x3
- field public static final int AUDIO_CODEC_AMR_WB = 2; // 0x2
- field public static final int AUDIO_CODEC_EVS = 1; // 0x1
field public static final int BATTERY_STATE_CHARGING = 3; // 0x3
field public static final int BATTERY_STATE_GOOD = 2; // 0x2
field public static final int BATTERY_STATE_LOW = 1; // 0x1
@@ -10454,9 +10451,6 @@ package android.telecom {
field public static final int MESSAGE_CALL_NETWORK_TYPE = 1; // 0x1
field public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; // 0x3
field public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; // 0x4
- field public static final int NETWORK_TYPE_IWLAN = 2; // 0x2
- field public static final int NETWORK_TYPE_LTE = 1; // 0x1
- field public static final int NETWORK_TYPE_NR = 3; // 0x3
}
public abstract class InCallService extends android.app.Service {
@@ -11478,7 +11472,7 @@ package android.telephony {
}
public static interface TelephonyCallback.AllowedNetworkTypesListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(@NonNull java.util.Map<java.lang.Integer,java.lang.Long>);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(int, long);
}
public static interface TelephonyCallback.CallAttributesListener {
@@ -11962,7 +11956,7 @@ package android.telephony.data {
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(@IntRange(from=android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET, to=15) int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long);
method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo);
@@ -13039,6 +13033,7 @@ package android.telephony.ims {
method public void onAutoConfigurationErrorReceived(int, @NonNull String);
method public void onConfigurationChanged(@NonNull byte[]);
method public void onConfigurationReset();
+ method public void onPreProvisioningReceived(@NonNull byte[]);
method public void onRemoved();
}
@@ -13508,6 +13503,7 @@ package android.telephony.ims.stub {
method public int getConfigInt(int);
method public String getConfigString(int);
method public final void notifyAutoConfigurationErrorReceived(int, @NonNull String);
+ method public final void notifyPreProvisioningReceived(@NonNull byte[]);
method public final void notifyProvisionedValueChanged(int, int);
method public final void notifyProvisionedValueChanged(int, String);
method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e832571ba499..d95140575151 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -414,6 +414,7 @@ package android.app.admin {
method public long getLastNetworkLogRetrievalTime();
method public long getLastSecurityLogRetrievalTime();
method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
+ method @NonNull @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public java.util.Set<java.lang.String> getPolicyExemptApps();
method public boolean isCurrentInputMethodSetByOwner();
method public boolean isFactoryResetProtectionPolicySupported();
method @RequiresPermission(anyOf={"android.permission.MARK_DEVICE_ORGANIZATION_OWNED", "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
@@ -698,7 +699,8 @@ package android.content {
field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
field public static final String DREAM_SERVICE = "dream";
field public static final String FONT_SERVICE = "font";
- field public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
+ field public static final String POWER_EXEMPTION_SERVICE = "power_exemption";
+ field @Deprecated public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
field public static final String TEST_NETWORK_SERVICE = "test_network";
}
@@ -1495,6 +1497,14 @@ package android.net {
package android.os {
+ public final class BatteryStatsManager {
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void resetBattery(boolean);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryLevel(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setChargerAcOnline(boolean, boolean);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void suspendBatteryInput();
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void unplugBattery(boolean);
+ }
+
public class Build {
method public static boolean is64BitAbi(String);
field public static final boolean IS_EMULATOR;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index dd1bc7c61547..d310e8f0ef5c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -56,6 +56,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -200,9 +201,12 @@ public class AppOpsManager {
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
public static final long SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE = 151105954L;
+ private static final String FULL_LOG = "privacy_attribution_tag_full_log_enabled";
private static final int MAX_UNFORWARDED_OPS = 10;
+ private static Boolean sFullLog = null;
+
final Context mContext;
@UnsupportedAppUsage
@@ -6972,6 +6976,26 @@ public class AppOpsManager {
AppOpsManager(Context context, IAppOpsService service) {
mContext = context;
mService = service;
+
+ if (mContext != null) {
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ if (pm != null && pm.checkPermission(Manifest.permission.READ_DEVICE_CONFIG,
+ mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
+ mContext.getMainExecutor(), properties -> {
+ if (properties.getKeyset().contains(FULL_LOG)) {
+ sFullLog = properties.getBoolean(FULL_LOG, false);
+ }
+ });
+ return;
+ }
+ } catch (Exception e) {
+ // This manager was made before DeviceConfig is ready, so it's a low-level
+ // system app. We likely don't care about its logs.
+ }
+ }
+ sFullLog = false;
}
/**
@@ -9110,10 +9134,20 @@ public class AppOpsManager {
StringBuilder sb = new StringBuilder();
for (int i = firstInteresting; i <= lastInteresting; i++) {
+ if (sFullLog == null) {
+ try {
+ sFullLog = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ FULL_LOG, false);
+ } catch (SecurityException e) {
+ // This should not happen, but it may, in rare cases
+ sFullLog = false;
+ }
+ }
+
if (i != firstInteresting) {
sb.append('\n');
}
- if (sb.length() + trace[i].toString().length() > 600) {
+ if (!sFullLog && sb.length() + trace[i].toString().length() > 600) {
break;
}
sb.append(trace[i]);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 30fb858b4bbc..930717b97555 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -13726,4 +13726,22 @@ public class DevicePolicyManager {
throw re.rethrowFromSystemServer();
}
}
+
+ /**
+ * Lists apps that are exempt from policies (such as
+ * {@link #setPackagesSuspended(ComponentName, String[], boolean)}).
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(value = android.Manifest.permission.MANAGE_DEVICE_ADMINS)
+ public @NonNull Set<String> getPolicyExemptApps() {
+ if (mService == null) return Collections.emptySet();
+
+ try {
+ return new HashSet<>(mService.listPolicyExemptApps());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 25ca59963d4b..e98720c0d96c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -177,6 +177,7 @@ interface IDevicePolicyManager {
String[] setPackagesSuspended(in ComponentName admin, in String callerPackage, in String[] packageNames, boolean suspended);
boolean isPackageSuspended(in ComponentName admin, in String callerPackage, String packageName);
+ List<String> listPolicyExemptApps();
boolean installCaCert(in ComponentName admin, String callerPackage, in byte[] certBuffer);
void uninstallCaCerts(in ComponentName admin, String callerPackage, in String[] aliases);
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 6ac1c1ae61ec..1cbb2fb3a8a3 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -121,7 +121,7 @@ public class AppWidgetProviderInfo implements Parcelable {
*
* @see #widgetFeatures
*/
- public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 3;
+ public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 4;
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0509e3f77c1f..25234592f4c4 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3713,6 +3713,9 @@ public abstract class Context {
* usage statistics.
* <dt> {@link #HARDWARE_PROPERTIES_SERVICE} ("hardware_properties")
* <dd> A {@link android.os.HardwarePropertiesManager} for accessing hardware properties.
+ * <dt> {@link #DOMAIN_VERIFICATION_SERVICE} ("domain_verification")
+ * <dd> A {@link android.content.pm.verify.domain.DomainVerificationManager} for accessing
+ * web domain approval state.
* </dl>
*
* <p>Note: System services obtained via this API may be closely associated with
@@ -3794,6 +3797,8 @@ public abstract class Context {
* @see android.app.usage.NetworkStatsManager
* @see android.os.HardwarePropertiesManager
* @see #HARDWARE_PROPERTIES_SERVICE
+ * @see #DOMAIN_VERIFICATION_SERVICE
+ * @see android.content.pm.verify.domain.DomainVerificationManager
*/
public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
@@ -3813,7 +3818,8 @@ public abstract class Context {
* {@link android.view.inputmethod.InputMethodManager},
* {@link android.app.UiModeManager}, {@link android.app.DownloadManager},
* {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler},
- * {@link android.app.usage.NetworkStatsManager}.
+ * {@link android.app.usage.NetworkStatsManager},
+ * {@link android.content.pm.verify.domain.DomainVerificationManager}.
* </p>
*
* <p>
@@ -4833,7 +4839,8 @@ public abstract class Context {
* @hide
*/
@TestApi
- @SuppressLint("ServiceName") // TODO: This should be renamed to POWER_WHITELIST_SERVICE
+ @Deprecated
+ @SuppressLint("ServiceName")
public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
/**
@@ -4842,7 +4849,7 @@ public abstract class Context {
* @see #getSystemService(String)
* @hide
*/
- @SystemApi
+ @TestApi
public static final String POWER_EXEMPTION_SERVICE = "power_exemption";
/**
@@ -5544,12 +5551,13 @@ public abstract class Context {
public static final String GAME_SERVICE = "game";
/**
- * Use with {@link #getSystemService(String)} to access domain verification service.
+ * Use with {@link #getSystemService(String)} to access
+ * {@link android.content.pm.verify.domain.DomainVerificationManager} to retrieve approval and
+ * user state for declared web domains.
*
* @see #getSystemService(String)
- * @hide
+ * @see android.content.pm.verify.domain.DomainVerificationManager
*/
- @SystemApi
public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 04f93ca6991b..c601aabb582b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2172,6 +2172,29 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.REVIEW_PERMISSION_USAGE";
/**
+ * Activity action: Launch UI to review the timeline history of permissions.
+ * <p>
+ * Input: {@link #EXTRA_PERMISSION_GROUP_NAME} specifies the permission group name
+ * that will be displayed by the launched UI.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ * <p class="note">
+ * This requires {@link android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS} permission.
+ * </p>
+ *
+ * @see #EXTRA_PERMISSION_GROUP_NAME
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_REVIEW_PERMISSION_HISTORY =
+ "android.intent.action.REVIEW_PERMISSION_HISTORY";
+
+ /**
* Activity action: Launch UI to review ongoing app uses of permissions.
* <p>
* Input: {@link #EXTRA_DURATION_MILLIS} specifies the minimum number of milliseconds of recent
diff --git a/core/java/android/content/pm/IDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
index 745c39b460fa..79b70f2bd5ee 100644
--- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl
+++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
@@ -23,32 +23,34 @@ package android.content.pm;
oneway interface IDataLoaderStatusListener {
/** The DataLoader process died, binder disconnected or class destroyed. */
const int DATA_LOADER_DESTROYED = 0;
+ /** The system is in process of binding to the DataLoader. */
+ const int DATA_LOADER_BINDING = 1;
/** DataLoader process is running and bound to. */
- const int DATA_LOADER_BOUND = 1;
+ const int DATA_LOADER_BOUND = 2;
/** DataLoader has handled onCreate(). */
- const int DATA_LOADER_CREATED = 2;
+ const int DATA_LOADER_CREATED = 3;
/** DataLoader can receive missing pages and read pages notifications,
* and ready to provide data. */
- const int DATA_LOADER_STARTED = 3;
+ const int DATA_LOADER_STARTED = 4;
/** DataLoader no longer ready to provide data and is not receiving
* any notifications from IncFS. */
- const int DATA_LOADER_STOPPED = 4;
+ const int DATA_LOADER_STOPPED = 5;
/** DataLoader streamed everything necessary to continue installation. */
- const int DATA_LOADER_IMAGE_READY = 5;
+ const int DATA_LOADER_IMAGE_READY = 6;
/** Installation can't continue as DataLoader failed to stream necessary data. */
- const int DATA_LOADER_IMAGE_NOT_READY = 6;
+ const int DATA_LOADER_IMAGE_NOT_READY = 7;
/** DataLoader instance can't run at the moment, but might recover later.
* It's up to system to decide if the app is still usable. */
- const int DATA_LOADER_UNAVAILABLE = 7;
+ const int DATA_LOADER_UNAVAILABLE = 8;
/** DataLoader reports that this instance is invalid and can never be restored.
* Warning: this is a terminal status that data loader should use carefully and
* the system should almost never use - e.g. only if all recovery attempts
* fail and all retry limits are exceeded. */
- const int DATA_LOADER_UNRECOVERABLE = 8;
+ const int DATA_LOADER_UNRECOVERABLE = 9;
/** There are no known issues with the data stream. */
const int STREAM_HEALTHY = 0;
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 8ebf757760c3..062438c6e5db 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -73,7 +73,8 @@ import java.util.concurrent.Executor;
public class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
- private static final Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+ private static final Map<
+ VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
@NonNull private final Context mContext;
@@ -93,13 +94,13 @@ public class VcnManager {
}
/**
- * Get all currently registered VcnNetworkPolicyListeners for testing purposes.
+ * Get all currently registered VcnNetworkPolicyChangeListeners for testing purposes.
*
* @hide
*/
@VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull
- public static Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+ public static Map<VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
getAllPolicyListeners() {
return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS);
}
@@ -162,14 +163,14 @@ public class VcnManager {
}
// TODO(b/180537630): remove all VcnUnderlyingNetworkPolicyListener refs once Telephony is using
- // the new VcnNetworkPolicyListener API
+ // the new VcnNetworkPolicyChangeListener API
/**
* VcnUnderlyingNetworkPolicyListener is the interface through which internal system components
* can register to receive updates for VCN-underlying Network policies from the System Server.
*
* @hide
*/
- public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyListener {}
+ public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyChangeListener {}
/**
* Add a listener for VCN-underlying network policy updates.
@@ -185,7 +186,7 @@ public class VcnManager {
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
public void addVcnUnderlyingNetworkPolicyListener(
@NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) {
- addVcnNetworkPolicyListener(executor, listener);
+ addVcnNetworkPolicyChangeListener(executor, listener);
}
/**
@@ -198,7 +199,7 @@ public class VcnManager {
*/
public void removeVcnUnderlyingNetworkPolicyListener(
@NonNull VcnUnderlyingNetworkPolicyListener listener) {
- removeVcnNetworkPolicyListener(listener);
+ removeVcnNetworkPolicyChangeListener(listener);
}
/**
@@ -233,20 +234,20 @@ public class VcnManager {
}
/**
- * VcnNetworkPolicyListener is the interface through which internal system components (e.g.
- * Network Factories) can register to receive updates for VCN-underlying Network policies from
- * the System Server.
+ * VcnNetworkPolicyChangeListener is the interface through which internal system components
+ * (e.g. Network Factories) can register to receive updates for VCN-underlying Network policies
+ * from the System Server.
*
* <p>Any Network Factory that brings up Networks capable of being VCN-underlying Networks
- * should register a VcnNetworkPolicyListener. VcnManager will then use this listener to notify
- * the registrant when VCN Network policies change. Upon receiving this signal, the listener
- * must check {@link VcnManager} for the current Network policy result for each of its Networks
- * via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}.
+ * should register a VcnNetworkPolicyChangeListener. VcnManager will then use this listener to
+ * notify the registrant when VCN Network policies change. Upon receiving this signal, the
+ * listener must check {@link VcnManager} for the current Network policy result for each of its
+ * Networks via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}.
*
* @hide
*/
@SystemApi
- public interface VcnNetworkPolicyListener {
+ public interface VcnNetworkPolicyChangeListener {
/**
* Notifies the implementation that the VCN's underlying Network policy has changed.
*
@@ -260,20 +261,21 @@ public class VcnManager {
/**
* Add a listener for VCN-underlying Network policy updates.
*
- * <p>A {@link VcnNetworkPolicyListener} is eligible to begin receiving callbacks once it is
- * registered. No callbacks are guaranteed upon registration.
+ * <p>A {@link VcnNetworkPolicyChangeListener} is eligible to begin receiving callbacks once it
+ * is registered. No callbacks are guaranteed upon registration.
*
* @param executor the Executor that will be used for invoking all calls to the specified
* Listener
- * @param listener the VcnNetworkPolicyListener to be added
+ * @param listener the VcnNetworkPolicyChangeListener to be added
* @throws SecurityException if the caller does not have permission NETWORK_FACTORY
- * @throws IllegalStateException if the specified VcnNetworkPolicyListener is already registered
+ * @throws IllegalStateException if the specified VcnNetworkPolicyChangeListener is already
+ * registered
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
- public void addVcnNetworkPolicyListener(
- @NonNull Executor executor, @NonNull VcnNetworkPolicyListener listener) {
+ public void addVcnNetworkPolicyChangeListener(
+ @NonNull Executor executor, @NonNull VcnNetworkPolicyChangeListener listener) {
requireNonNull(executor, "executor must not be null");
requireNonNull(listener, "listener must not be null");
@@ -292,15 +294,18 @@ public class VcnManager {
}
/**
- * Remove the specified VcnNetworkPolicyListener from VcnManager.
+ * Remove the specified VcnNetworkPolicyChangeListener from VcnManager.
*
* <p>If the specified listener is not currently registered, this is a no-op.
*
- * @param listener the VcnNetworkPolicyListener that will be removed
+ * @param listener the VcnNetworkPolicyChangeListener that will be removed
+ * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
* @hide
*/
@SystemApi
- public void removeVcnNetworkPolicyListener(@NonNull VcnNetworkPolicyListener listener) {
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public void removeVcnNetworkPolicyChangeListener(
+ @NonNull VcnNetworkPolicyChangeListener listener) {
requireNonNull(listener, "listener must not be null");
VcnUnderlyingNetworkPolicyListenerBinder binder =
@@ -320,8 +325,9 @@ public class VcnManager {
* Applies the network policy for a {@link android.net.Network} with the given parameters.
*
* <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy
- * may have changed via {@link VcnNetworkPolicyListener#onPolicyChanged()}, a Network Provider
- * MUST poll for the updated Network policy based on that Network's capabilities and properties.
+ * may have changed via {@link VcnNetworkPolicyChangeListener#onPolicyChanged()}, a Network
+ * Provider MUST poll for the updated Network policy based on that Network's capabilities and
+ * properties.
*
* @param networkCapabilities the NetworkCapabilities to be used in determining the Network
* policy result for this Network.
@@ -532,17 +538,18 @@ public class VcnManager {
}
/**
- * Binder wrapper for added VcnNetworkPolicyListeners to receive signals from System Server.
+ * Binder wrapper for added VcnNetworkPolicyChangeListeners to receive signals from System
+ * Server.
*
* @hide
*/
private static class VcnUnderlyingNetworkPolicyListenerBinder
extends IVcnUnderlyingNetworkPolicyListener.Stub {
@NonNull private final Executor mExecutor;
- @NonNull private final VcnNetworkPolicyListener mListener;
+ @NonNull private final VcnNetworkPolicyChangeListener mListener;
private VcnUnderlyingNetworkPolicyListenerBinder(
- Executor executor, VcnNetworkPolicyListener listener) {
+ Executor executor, VcnNetworkPolicyChangeListener listener) {
mExecutor = executor;
mListener = listener;
}
diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java
index a86237dd271f..97ec5940ccb0 100644
--- a/core/java/android/os/BatteryManagerInternal.java
+++ b/core/java/android/os/BatteryManagerInternal.java
@@ -83,4 +83,29 @@ public abstract class BatteryManagerInternal {
* wait on the battery service lock.
*/
public abstract int getInvalidCharger();
+
+ /**
+ * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+ */
+ public abstract void setChargerAcOnline(boolean online, boolean forceUpdate);
+
+ /**
+ * Sets battery level, and freezes the battery state.
+ */
+ public abstract void setBatteryLevel(int level, boolean forceUpdate);
+
+ /**
+ * Unplugs battery, and freezes the battery state.
+ */
+ public abstract void unplugBattery(boolean forceUpdate);
+
+ /**
+ * Unfreezes battery state, returning to current hardware values.
+ */
+ public abstract void resetBattery(boolean forceUpdate);
+
+ /**
+ * Suspend charging even if plugged in.
+ */
+ public abstract void suspendBatteryInput();
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 66f7bd9d8dee..4c26e2f33fb2 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -995,6 +995,15 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getScreenOnMeasuredBatteryConsumptionUC();
/**
+ * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from
+ * on device power measurement data.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getCpuMeasuredBatteryConsumptionUC();
+
+ /**
* Returns the battery consumption (in microcoulombs) used by this uid for each
* {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
* type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
@@ -2521,6 +2530,15 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getScreenDozeMeasuredBatteryConsumptionUC();
/**
+ * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power
+ * measurement data.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getCpuMeasuredBatteryConsumptionUC();
+
+ /**
* Returns the battery consumption (in microcoulombs) that each
* {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
* type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}) consumed.
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 1905d708d6d3..e47478abf439 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.net.NetworkStack;
import android.os.connectivity.CellularBatteryStats;
@@ -487,4 +488,74 @@ public final class BatteryStatsManager {
return isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
: DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
}
-}
+
+ /**
+ * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+ try {
+ mBatteryStats.setChargerAcOnline(online, forceUpdate);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets battery level, and freezes the battery state.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void setBatteryLevel(int level, boolean forceUpdate) {
+ try {
+ mBatteryStats.setBatteryLevel(level, forceUpdate);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unplugs battery, and freezes the battery state.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void unplugBattery(boolean forceUpdate) {
+ try {
+ mBatteryStats.unplugBattery(forceUpdate);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unfreezes battery state, returning to current hardware values.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void resetBattery(boolean forceUpdate) {
+ try {
+ mBatteryStats.resetBattery(forceUpdate);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Suspend charging even if plugged in.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public void suspendBatteryInput() {
+ try {
+ mBatteryStats.suspendBatteryInput();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+} \ No newline at end of file
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index b003d238c268..b90d438ffb93 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -208,6 +208,28 @@ public abstract class Vibrator {
public abstract boolean hasAmplitudeControl();
/**
+ * Gets the resonant frequency of the vibrator.
+ *
+ * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+ * this vibrator is a composite of multiple physical devices.
+ * @hide
+ */
+ public float getResonantFrequency() {
+ return Float.NaN;
+ }
+
+ /**
+ * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
+ *
+ * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+ * this vibrator is a composite of multiple physical devices.
+ * @hide
+ */
+ public float getQFactor() {
+ return Float.NaN;
+ }
+
+ /**
* Configure an always-on haptics effect.
*
* @param alwaysOnId The board-specific always-on ID to configure.
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 50d2de3da965..3121b952281e 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -42,21 +42,27 @@ public final class VibratorInfo implements Parcelable {
private final SparseBooleanArray mSupportedEffects;
@Nullable
private final SparseBooleanArray mSupportedPrimitives;
+ private final float mResonantFrequency;
+ private final float mQFactor;
VibratorInfo(Parcel in) {
mId = in.readInt();
mCapabilities = in.readLong();
mSupportedEffects = in.readSparseBooleanArray();
mSupportedPrimitives = in.readSparseBooleanArray();
+ mResonantFrequency = in.readFloat();
+ mQFactor = in.readFloat();
}
/** @hide */
public VibratorInfo(int id, long capabilities, int[] supportedEffects,
- int[] supportedPrimitives) {
+ int[] supportedPrimitives, float resonantFrequency, float qFactor) {
mId = id;
mCapabilities = capabilities;
mSupportedEffects = toSparseBooleanArray(supportedEffects);
mSupportedPrimitives = toSparseBooleanArray(supportedPrimitives);
+ mResonantFrequency = resonantFrequency;
+ mQFactor = qFactor;
}
@Override
@@ -65,6 +71,8 @@ public final class VibratorInfo implements Parcelable {
dest.writeLong(mCapabilities);
dest.writeSparseBooleanArray(mSupportedEffects);
dest.writeSparseBooleanArray(mSupportedPrimitives);
+ dest.writeFloat(mResonantFrequency);
+ dest.writeFloat(mQFactor);
}
@Override
@@ -83,12 +91,15 @@ public final class VibratorInfo implements Parcelable {
VibratorInfo that = (VibratorInfo) o;
return mId == that.mId && mCapabilities == that.mCapabilities
&& Objects.equals(mSupportedEffects, that.mSupportedEffects)
- && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives);
+ && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives)
+ && Objects.equals(mResonantFrequency, that.mResonantFrequency)
+ && Objects.equals(mQFactor, that.mQFactor);
}
@Override
public int hashCode() {
- return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives);
+ return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives,
+ mResonantFrequency, mQFactor);
}
@Override
@@ -99,6 +110,8 @@ public final class VibratorInfo implements Parcelable {
+ ", mCapabilities flags=" + Long.toBinaryString(mCapabilities)
+ ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
+ ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
+ + ", mResonantFrequency=" + mResonantFrequency
+ + ", mQFactor=" + mQFactor
+ '}';
}
@@ -156,6 +169,26 @@ public final class VibratorInfo implements Parcelable {
return (mCapabilities & capability) == capability;
}
+ /**
+ * Gets the resonant frequency of the vibrator.
+ *
+ * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+ * this vibrator is a composite of multiple physical devices.
+ */
+ public float getResonantFrequency() {
+ return mResonantFrequency;
+ }
+
+ /**
+ * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
+ *
+ * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+ * this vibrator is a composite of multiple physical devices.
+ */
+ public float getQFactor() {
+ return mQFactor;
+ }
+
private String[] getCapabilitiesNames() {
List<String> names = new ArrayList<>();
if (hasCapability(IVibrator.CAP_ON_CALLBACK)) {
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 4c9e77c35135..7e3a0f30e75c 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -111,8 +111,7 @@ public class PermissionUsageHelper {
private static boolean shouldShowLocationIndicator() {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_LOCATION_INDICATORS_ENABLED, false)
- || shouldShowPermissionsHub();
+ PROPERTY_LOCATION_INDICATORS_ENABLED, false);
}
private static long getRecentThreshold(Long now) {
@@ -326,10 +325,10 @@ public class PermissionUsageHelper {
}
if (packageName.equals(SYSTEM_PKG)
- || (!isUserSensitive(packageName, user, op)
+ || (!shouldShowPermissionsHub()
+ && !isUserSensitive(packageName, user, op)
&& !isLocationProvider(packageName, user)
- && !isAppPredictor(packageName, user))
- && !isSpeechRecognizerUsage(op, packageName)) {
+ && !isSpeechRecognizerUsage(op, packageName))) {
continue;
}
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 8a4812a42c8a..374de9ccb2f4 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5310,5 +5310,12 @@ public final class Telephony {
* @hide
*/
public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status";
+
+ /**
+ * TelephonyProvider column name for device to device sharing status.
+ *
+ * @hide
+ */
+ public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status";
}
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index bbe887f500a9..e9a79e70fd74 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -20,7 +20,6 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
-import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Binder;
import android.os.Build;
@@ -1577,7 +1576,7 @@ public class PhoneStateListener {
// default implementation empty
}
- public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
+ public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
// default implementation empty
}
}
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 2cadda25a9d3..e3d3dec60151 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -546,9 +546,6 @@ public class TelephonyCallback {
/**
* Event for changes to allowed network list based on all active subscriptions.
*
- * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
- * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
* @hide
* @see AllowedNetworkTypesListener#onAllowedNetworkTypesChanged
*/
@@ -1265,30 +1262,34 @@ public class TelephonyCallback {
public interface AllowedNetworkTypesListener {
/**
* Callback invoked when the current allowed network type list has changed on the
- * registered subscription.
+ * registered subscription for a specified reason.
* Note, the registered subscription is associated with {@link TelephonyManager} object
- * on which
- * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
+ * on which {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
* was called.
* If this TelephonyManager object was created with
* {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
* given subscription ID. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
- * @param allowedNetworkTypesList Map associating all allowed network type reasons
- * ({@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER},
- * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER},
- * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}, and
- * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}) with reason's allowed
- * network type values.
+ * @param reason an allowed network type reasons.
+ * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER
+ * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER
+ * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER
+ * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G
+ *
+ * @param allowedNetworkType an allowed network type bitmask value. (for example,
+ * the long bitmask value is {{@link TelephonyManager#NETWORK_TYPE_BITMASK_NR}|
+ * {@link TelephonyManager#NETWORK_TYPE_BITMASK_LTE}})
+ *
* For example:
- * map{{TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER, long type value},
- * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER, long type value},
- * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER, long type value},
- * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, long type value}}
+ * If the latest allowed network type is changed by user, then the system
+ * notifies the {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER} and
+ * long type value}.
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- void onAllowedNetworkTypesChanged(@NonNull Map<Integer, Long> allowedNetworkTypesList);
+ void onAllowedNetworkTypesChanged(
+ @TelephonyManager.AllowedNetworkTypesReason int reason,
+ @TelephonyManager.NetworkTypeBitMask long allowedNetworkType);
}
/**
@@ -1707,14 +1708,15 @@ public class TelephonyCallback {
enabled, reason)));
}
- public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
+ public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
AllowedNetworkTypesListener listener =
(AllowedNetworkTypesListener) mTelephonyCallbackWeakRef.get();
if (listener == null) return;
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(
- () -> listener.onAllowedNetworkTypesChanged(allowedNetworkTypesList)));
+ () -> listener.onAllowedNetworkTypesChanged(reason,
+ allowedNetworkType)));
}
}
}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 9cda4ae79335..3fa63d8c1a9c 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -825,16 +825,18 @@ public class TelephonyRegistryManager {
}
/**
- * Notify emergency number list changed on certain subscription.
- *
- * @param slotIndex for which emergency number list changed. Can be derived from subId except
- * when subId is invalid.
- * @param subId for which emergency number list changed.
+ * Notify the allowed network types has changed for a specific subscription and the specific
+ * reason.
+ * @param slotIndex for which allowed network types changed.
+ * @param subId for which allowed network types changed.
+ * @param reason an allowed network type reasons.
+ * @param allowedNetworkType an allowed network type bitmask value.
*/
public void notifyAllowedNetworkTypesChanged(int slotIndex, int subId,
- Map<Integer, Long> allowedNetworkTypeList) {
+ int reason, long allowedNetworkType) {
try {
- sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, allowedNetworkTypeList);
+ sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, reason,
+ allowedNetworkType);
} catch (RemoteException ex) {
// system process is dead
}
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index 3880131324fc..f61ab2985163 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -16,14 +16,26 @@
package android.util;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Formatter;
+import java.util.Locale;
+
/**
* @hide
*/
public final class Slog {
+ @GuardedBy("sMessageBuilder")
+ private static final StringBuilder sMessageBuilder = new StringBuilder();
+
+ @GuardedBy("sMessageBuilder")
+ private static final Formatter sFormatter = new Formatter(sMessageBuilder, Locale.ENGLISH);
+
private Slog() {
}
@@ -37,6 +49,15 @@ public final class Slog {
msg + '\n' + Log.getStackTraceString(tr));
}
+ /**
+ * Logs a {@link Log.VERBOSE} message.
+ */
+ public static void v(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.VERBOSE)) return;
+
+ v(tag, getMessage(format, args));
+ }
+
@UnsupportedAppUsage
public static int d(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg);
@@ -48,6 +69,15 @@ public final class Slog {
msg + '\n' + Log.getStackTraceString(tr));
}
+ /**
+ * Logs a {@link Log.DEBUG} message.
+ */
+ public static void d(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.DEBUG)) return;
+
+ d(tag, getMessage(format, args));
+ }
+
@UnsupportedAppUsage
public static int i(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg);
@@ -58,6 +88,15 @@ public final class Slog {
msg + '\n' + Log.getStackTraceString(tr));
}
+ /**
+ * Logs a {@link Log.INFO} message.
+ */
+ public static void i(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.INFO)) return;
+
+ i(tag, getMessage(format, args));
+ }
+
@UnsupportedAppUsage
public static int w(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, msg);
@@ -73,6 +112,24 @@ public final class Slog {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, Log.getStackTraceString(tr));
}
+ /**
+ * Logs a {@link Log.WARN} message.
+ */
+ public static void w(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.WARN)) return;
+
+ w(tag, getMessage(format, args));
+ }
+
+ /**
+ * Logs a {@link Log.WARN} message with an exception
+ */
+ public static void w(String tag, Exception exception, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.WARN)) return;
+
+ w(tag, getMessage(format, args), exception);
+ }
+
@UnsupportedAppUsage
public static int e(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag, msg);
@@ -85,6 +142,24 @@ public final class Slog {
}
/**
+ * Logs a {@link Log.ERROR} message.
+ */
+ public static void e(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.ERROR)) return;
+
+ e(tag, getMessage(format, args));
+ }
+
+ /**
+ * Logs a {@link Log.ERROR} message with an exception
+ */
+ public static void e(String tag, Exception exception, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.ERROR)) return;
+
+ e(tag, getMessage(format, args), exception);
+ }
+
+ /**
* Like {@link Log#wtf(String, String)}, but will never cause the caller to crash, and
* will always be handled asynchronously. Primarily for use by coding running within
* the system process.
@@ -95,6 +170,21 @@ public final class Slog {
}
/**
+ * Logs a {@code wtf} message.
+ */
+ public static void wtf(String tag, String format, @Nullable Object... args) {
+ wtf(tag, getMessage(format, args));
+ }
+
+ /**
+ * Logs a {@code wtf} message with an exception.
+ */
+ public static void wtf(String tag, Exception exception, String format,
+ @Nullable Object... args) {
+ wtf(tag, getMessage(format, args), exception);
+ }
+
+ /**
* Like {@link #wtf(String, String)}, but does not output anything to the log.
*/
public static void wtfQuiet(String tag, String msg) {
@@ -134,5 +224,13 @@ public final class Slog {
public static int println(int priority, String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, priority, tag, msg);
}
-}
+ private static String getMessage(String format, @Nullable Object... args) {
+ synchronized (sMessageBuilder) {
+ sFormatter.format(format, args);
+ String message = sMessageBuilder.toString();
+ sMessageBuilder.setLength(0);
+ return message;
+ }
+ }
+}
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 02a97888cffd..aa1acc1217df 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.input.InputManager;
import android.os.Build;
@@ -25,6 +26,8 @@ import android.text.method.MetaKeyKeyListener;
import android.util.AndroidRuntimeException;
import android.util.SparseIntArray;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.text.Normalizer;
/**
@@ -297,6 +300,8 @@ public class KeyCharacterMap implements Parcelable {
private static native char nativeGetDisplayLabel(long ptr, int keyCode);
private static native int nativeGetKeyboardType(long ptr);
private static native KeyEvent[] nativeGetEvents(long ptr, char[] chars);
+ private static native KeyCharacterMap nativeObtainEmptyKeyCharacterMap(int deviceId);
+ private static native boolean nativeEquals(long ptr1, long ptr2);
private KeyCharacterMap(Parcel in) {
if (in == null) {
@@ -323,6 +328,18 @@ public class KeyCharacterMap implements Parcelable {
}
/**
+ * Obtain empty key character map
+ * @param deviceId The input device ID
+ * @return The KeyCharacterMap object
+ * @hide
+ */
+ @VisibleForTesting
+ @Nullable
+ public static KeyCharacterMap obtainEmptyMap(int deviceId) {
+ return nativeObtainEmptyKeyCharacterMap(deviceId);
+ }
+
+ /**
* Loads the key character maps for the keyboard with the specified device id.
*
* @param deviceId The device id of the keyboard.
@@ -729,6 +746,18 @@ public class KeyCharacterMap implements Parcelable {
return 0;
}
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof KeyCharacterMap)) {
+ return false;
+ }
+ KeyCharacterMap peer = (KeyCharacterMap) obj;
+ if (mPtr == 0 || peer.mPtr == 0) {
+ return mPtr == peer.mPtr;
+ }
+ return nativeEquals(mPtr, peer.mPtr);
+ }
+
/**
* Thrown by {@link KeyCharacterMap#load} when a key character map could not be loaded.
*/
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index c1952c7d52cf..957e416986e0 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -166,4 +166,15 @@ interface IBatteryStats {
/** {@hide} */
boolean setChargingStateUpdateDelayMillis(int delay);
+
+ /** Exposed as a test API. */
+ void setChargerAcOnline(boolean online, boolean forceUpdate);
+ /** Exposed as a test API. */
+ void setBatteryLevel(int level, boolean forceUpdate);
+ /** Exposed as a test API. */
+ void unplugBattery(boolean forceUpdate);
+ /** Exposed as a test API. */
+ void resetBattery(boolean forceUpdate);
+ /** Exposed as a test API. */
+ void suspendBatteryInput();
}
diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
index 0b937fad7df1..364db06976a0 100644
--- a/core/java/com/android/internal/compat/AndroidBuildClassifier.java
+++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
@@ -31,4 +31,14 @@ public class AndroidBuildClassifier {
public boolean isFinalBuild() {
return "REL".equals(Build.VERSION.CODENAME);
}
+
+ /**
+ * The current platform SDK version.
+ */
+ public int platformTargetSdk() {
+ if (isFinalBuild()) {
+ return Build.VERSION.SDK_INT;
+ }
+ return Build.VERSION_CODES.CUR_DEVELOPMENT;
+ }
}
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
index c0bbe5082131..e408be2ab471 100644
--- a/core/java/com/android/internal/compat/OverrideAllowedState.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -34,7 +34,8 @@ public final class OverrideAllowedState implements Parcelable {
DISABLED_NON_TARGET_SDK,
DISABLED_TARGET_SDK_TOO_HIGH,
DEFERRED_VERIFICATION,
- LOGGING_ONLY_CHANGE
+ LOGGING_ONLY_CHANGE,
+ PLATFORM_TOO_OLD
})
@Retention(RetentionPolicy.SOURCE)
public @interface State {
@@ -65,6 +66,10 @@ public final class OverrideAllowedState implements Parcelable {
* Change is marked as logging only, and cannot be toggled.
*/
public static final int LOGGING_ONLY_CHANGE = 5;
+ /**
+ * Change is gated by a target sdk version newer than the current platform sdk version.
+ */
+ public static final int PLATFORM_TOO_OLD = 6;
@State
public final int state;
@@ -123,6 +128,11 @@ public final class OverrideAllowedState implements Parcelable {
throw new SecurityException(String.format(
"Cannot override %1$d because it is marked as a logging-only change.",
changeId));
+ case PLATFORM_TOO_OLD:
+ throw new SecurityException(String.format(
+ "Cannot override %1$d for %2$s because the change's targetSdk threshold "
+ + "(%3$d) is above the platform sdk.",
+ changeId, packageName, changeIdTargetSdk));
}
}
@@ -170,6 +180,8 @@ public final class OverrideAllowedState implements Parcelable {
return "DEFERRED_VERIFICATION";
case LOGGING_ONLY_CHANGE:
return "LOGGING_ONLY_CHANGE";
+ case PLATFORM_TOO_OLD:
+ return "PLATFORM_TOO_OLD";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
index b4f216ba87fa..1d865c2513cf 100644
--- a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
+++ b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
@@ -75,7 +75,7 @@ public class WSMeansQuantizer implements Quantizer {
// Note: they don't _have_ to be ignored, for example, we could instead turn them
// opaque. Traditionally, including outside Android, quantizers ignore transparent
// pixels, so that strategy was chosen.
- int alpha = (pixel >> 24);
+ int alpha = (pixel >> 24) & 0xff;
if (alpha < 255) {
continue;
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 9ecb0ad09bc4..11466f4bc042 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6976,6 +6976,11 @@ public class BatteryStatsImpl extends BatteryStats {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE);
}
+ @Override
+ public long getCpuMeasuredBatteryConsumptionUC() {
+ return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+ }
+
/**
* Returns the consumption (in microcoulombs) that the given standard power bucket consumed.
* Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable
@@ -8482,6 +8487,11 @@ public class BatteryStatsImpl extends BatteryStats {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
}
+ @Override
+ public long getCpuMeasuredBatteryConsumptionUC() {
+ return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+ }
+
void initNetworkActivityLocked() {
detachIfNotNull(mNetworkByteActivityCounters);
mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 97f727ba72c5..b15543a26b4c 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -85,12 +85,14 @@ public class CpuPowerCalculator extends PowerCalculator {
builder.getUidBatteryConsumerBuilders();
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
- calculateApp(app, app.getBatteryStatsUid(), result);
+ calculateApp(app, app.getBatteryStatsUid(), query, result);
}
}
- private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, Result result) {
- calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED, result);
+ private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ BatteryUsageStatsQuery query, Result result) {
+ calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED,
+ query.shouldForceUsePowerProfileModel(), result);
app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah)
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, result.durationMs)
@@ -112,7 +114,7 @@ public class CpuPowerCalculator extends PowerCalculator {
}
private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result) {
- calculatePowerAndDuration(u, statsType, result);
+ calculatePowerAndDuration(u, statsType, false, result);
app.cpuPowerMah = result.powerMah;
app.cpuTimeMs = result.durationMs;
@@ -120,46 +122,16 @@ public class CpuPowerCalculator extends PowerCalculator {
app.packageWithHighestDrain = result.packageWithHighestDrain;
}
- private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType, Result result) {
+ private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType,
+ boolean forceUsePowerProfileModel, Result result) {
long durationMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
- // Constant battery drain when CPU is active
- double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());
-
- // Additional per-cluster battery drain
- long[] cpuClusterTimes = u.getCpuClusterTimes();
- if (cpuClusterTimes != null) {
- if (cpuClusterTimes.length == mNumCpuClusters) {
- for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
- double power = calculatePerCpuClusterPowerMah(cluster,
- cpuClusterTimes[cluster]);
- powerMah += power;
- if (DEBUG) {
- Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
- + " clusterTimeMs=" + cpuClusterTimes[cluster]
- + " power=" + formatCharge(power));
- }
- }
- } else {
- Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
- + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
- }
- }
-
- // Additional per-frequency battery drain
- for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
- final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length;
- for (int speed = 0; speed < speedsForCluster; speed++) {
- final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
- final double power = calculatePerCpuFreqPowerMah(cluster, speed,
- timeUs / 1000);
- if (DEBUG) {
- Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
- + speed + " timeUs=" + timeUs + " power="
- + formatCharge(power));
- }
- powerMah += power;
- }
+ final double powerMah;
+ final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC();
+ if (forceUsePowerProfileModel || consumptionUC == BatteryStats.POWER_DATA_UNAVAILABLE) {
+ powerMah = calculateUidModeledPowerMah(u, statsType);
+ } else {
+ powerMah = uCtoMah(consumptionUC);
}
if (DEBUG && (durationMs != 0 || powerMah != 0)) {
@@ -208,6 +180,48 @@ public class CpuPowerCalculator extends PowerCalculator {
result.packageWithHighestDrain = packageWithHighestDrain;
}
+ private double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
+ // Constant battery drain when CPU is active
+ double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());
+
+ // Additional per-cluster battery drain
+ long[] cpuClusterTimes = u.getCpuClusterTimes();
+ if (cpuClusterTimes != null) {
+ if (cpuClusterTimes.length == mNumCpuClusters) {
+ for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+ double power = calculatePerCpuClusterPowerMah(cluster,
+ cpuClusterTimes[cluster]);
+ powerMah += power;
+ if (DEBUG) {
+ Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
+ + " clusterTimeMs=" + cpuClusterTimes[cluster]
+ + " power=" + formatCharge(power));
+ }
+ }
+ } else {
+ Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
+ + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
+ }
+ }
+
+ // Additional per-frequency battery drain
+ for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+ final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length;
+ for (int speed = 0; speed < speedsForCluster; speed++) {
+ final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
+ final double power = calculatePerCpuFreqPowerMah(cluster, speed,
+ timeUs / 1000);
+ if (DEBUG) {
+ Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
+ + speed + " timeUs=" + timeUs + " power="
+ + formatCharge(power));
+ }
+ powerMah += power;
+ }
+ }
+ return powerMah;
+ }
+
/**
* Calculates active CPU power consumption.
*
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 85114e59cc2d..b57b4b959334 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -72,5 +72,5 @@ oneway interface IPhoneStateListener {
void onBarringInfoChanged(in BarringInfo barringInfo);
void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs);
void onDataEnabledChanged(boolean enabled, int reason);
- void onAllowedNetworkTypesChanged(in Map allowedNetworkTypeList);
+ void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType);
}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 95e0a3b524c5..83691ee64103 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -95,5 +95,5 @@ interface ITelephonyRegistry {
void notifyPhysicalChannelConfigForSubscriber(in int subId,
in List<PhysicalChannelConfig> configs);
void notifyDataEnabled(in int phoneId, int subId, boolean enabled, int reason);
- void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in Map allowedNetworkTypeList);
+ void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType);
}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 3bc0ef4ee3a0..94ac183517a2 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -637,6 +637,12 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
char saveResolvedClassesDelayMsOptsBuf[
sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX];
+ char madviseWillNeedFileSizeVdex[
+ sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX];
+ char madviseWillNeedFileSizeOdex[
+ sizeof("-XMadviseWillNeedOdexFileSize:")-1 + PROPERTY_VALUE_MAX];
+ char madviseWillNeedFileSizeArt[
+ sizeof("-XMadviseWillNeedArtFileSize:")-1 + PROPERTY_VALUE_MAX];
char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
@@ -845,6 +851,22 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
parseRuntimeOption("dalvik.vm.madvise-random", madviseRandomOptsBuf, "-XX:MadviseRandomAccess:");
/*
+ * Use default platform configuration as limits for madvising,
+ * when no properties are specified.
+ */
+ parseRuntimeOption("dalvik.vm.madvise.vdexfile.size",
+ madviseWillNeedFileSizeVdex,
+ "-XMadviseWillNeedVdexFileSize:");
+
+ parseRuntimeOption("dalvik.vm.madvise.odexfile.size",
+ madviseWillNeedFileSizeOdex,
+ "-XMadviseWillNeedOdexFileSize:");
+
+ parseRuntimeOption("dalvik.vm.madvise.artfile.size",
+ madviseWillNeedFileSizeArt,
+ "-XMadviseWillNeedArtFileSize:");
+
+ /*
* Profile related options.
*/
parseRuntimeOption("dalvik.vm.hot-startup-method-samples", hotstartupsamplesOptsBuf,
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index ebc507a8381e..469e577829d8 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -16,9 +16,10 @@
#include <android_runtime/AndroidRuntime.h>
-#include <input/KeyCharacterMap.h>
-#include <input/Input.h>
#include <binder/Parcel.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/KeyCharacterMap.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
@@ -75,6 +76,10 @@ jobject android_view_KeyCharacterMap_create(JNIEnv* env, int32_t deviceId,
reinterpret_cast<jlong>(nativeMap));
}
+static jobject nativeObtainEmptyKeyCharacterMap(JNIEnv* env, jobject /* clazz */, jint deviceId) {
+ return android_view_KeyCharacterMap_create(env, deviceId, nullptr);
+}
+
static jlong nativeReadFromParcel(JNIEnv *env, jobject clazz, jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
if (!parcel) {
@@ -224,33 +229,37 @@ static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jlong ptr,
return result;
}
+static jboolean nativeEquals(JNIEnv* env, jobject clazz, jlong ptr1, jlong ptr2) {
+ const std::shared_ptr<KeyCharacterMap>& map1 =
+ (reinterpret_cast<NativeKeyCharacterMap*>(ptr1))->getMap();
+ const std::shared_ptr<KeyCharacterMap>& map2 =
+ (reinterpret_cast<NativeKeyCharacterMap*>(ptr2))->getMap();
+ if (map1 == nullptr || map2 == nullptr) {
+ return map1 == map2;
+ }
+ return static_cast<jboolean>(*map1 == *map2);
+}
/*
* JNI registration.
*/
static const JNINativeMethod g_methods[] = {
- /* name, signature, funcPtr */
- { "nativeReadFromParcel", "(Landroid/os/Parcel;)J",
- (void*)nativeReadFromParcel },
- { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
- (void*)nativeWriteToParcel },
- { "nativeDispose", "(J)V",
- (void*)nativeDispose },
- { "nativeGetCharacter", "(JII)C",
- (void*)nativeGetCharacter },
- { "nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
- (void*)nativeGetFallbackAction },
- { "nativeGetNumber", "(JI)C",
- (void*)nativeGetNumber },
- { "nativeGetMatch", "(JI[CI)C",
- (void*)nativeGetMatch },
- { "nativeGetDisplayLabel", "(JI)C",
- (void*)nativeGetDisplayLabel },
- { "nativeGetKeyboardType", "(J)I",
- (void*)nativeGetKeyboardType },
- { "nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;",
- (void*)nativeGetEvents },
+ /* name, signature, funcPtr */
+ {"nativeReadFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeReadFromParcel},
+ {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel},
+ {"nativeDispose", "(J)V", (void*)nativeDispose},
+ {"nativeGetCharacter", "(JII)C", (void*)nativeGetCharacter},
+ {"nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
+ (void*)nativeGetFallbackAction},
+ {"nativeGetNumber", "(JI)C", (void*)nativeGetNumber},
+ {"nativeGetMatch", "(JI[CI)C", (void*)nativeGetMatch},
+ {"nativeGetDisplayLabel", "(JI)C", (void*)nativeGetDisplayLabel},
+ {"nativeGetKeyboardType", "(J)I", (void*)nativeGetKeyboardType},
+ {"nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;", (void*)nativeGetEvents},
+ {"nativeObtainEmptyKeyCharacterMap", "(I)Landroid/view/KeyCharacterMap;",
+ (void*)nativeObtainEmptyKeyCharacterMap},
+ {"nativeEquals", "(JJ)Z", (void*)nativeEquals},
};
int register_android_view_KeyCharacterMap(JNIEnv* env)
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index e62b5c102a59..ea5e7f729845 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -14,9 +14,10 @@ per-file settings_enums.proto=tmfang@google.com
# Frameworks
ogunwale@google.com
jjaggi@google.com
+kwekua@google.com
roosa@google.com
per-file package_item_info.proto = toddke@google.com
-per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
+per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/android/app/usage/OWNERS
per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
# Biometrics
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7c446a90b06f..5412db623c85 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8173,7 +8173,7 @@
<flag name="hide_from_picker" value="0x2" />
<!-- The widget provides a default configuration. The host may decide not to launch
the provided configuration activity. -->
- <flag name="configuration_optional" value="0x3" />
+ <flag name="configuration_optional" value="0x4" />
</attr>
<!-- A resource identifier for a string containing a short description of the widget. -->
<attr name="description" />
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index c06405affc1b..09c36dd261bd 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -16,9 +16,10 @@
package android.os;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import android.hardware.vibrator.IVibrator;
import android.platform.test.annotations.Presubmit;
@@ -33,72 +34,128 @@ public class VibratorInfoTest {
@Test
public void testHasAmplitudeControl() {
- assertFalse(createInfo(/* capabilities= */ 0).hasAmplitudeControl());
- assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS
- | IVibrator.CAP_AMPLITUDE_CONTROL).hasAmplitudeControl());
+ VibratorInfo noCapabilities = new InfoBuilder().build();
+ assertFalse(noCapabilities.hasAmplitudeControl());
+ VibratorInfo composeAndAmplitudeControl = new InfoBuilder()
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS
+ | IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build();
+ assertTrue(composeAndAmplitudeControl.hasAmplitudeControl());
}
@Test
public void testHasCapabilities() {
- assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS)
- .hasCapability(IVibrator.CAP_COMPOSE_EFFECTS));
- assertFalse(createInfo(IVibrator.CAP_COMPOSE_EFFECTS)
- .hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL));
+ VibratorInfo info = new InfoBuilder()
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .build();
+ assertTrue(info.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS));
+ assertFalse(info.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL));
}
@Test
public void testIsEffectSupported() {
- VibratorInfo info = new VibratorInfo(/* id= */ 0, /* capabilities= */0,
- new int[]{VibrationEffect.EFFECT_CLICK}, null);
+ VibratorInfo noEffects = new InfoBuilder().build();
+ VibratorInfo canClick = new InfoBuilder()
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .build();
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN,
- createInfo(/* capabilities= */ 0).isEffectSupported(VibrationEffect.EFFECT_CLICK));
+ noEffects.isEffectSupported(VibrationEffect.EFFECT_CLICK));
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
- info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+ canClick.isEffectSupported(VibrationEffect.EFFECT_CLICK));
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
- info.isEffectSupported(VibrationEffect.EFFECT_TICK));
+ canClick.isEffectSupported(VibrationEffect.EFFECT_TICK));
}
@Test
public void testIsPrimitiveSupported() {
- VibratorInfo info = new VibratorInfo(/* id= */ 0, IVibrator.CAP_COMPOSE_EFFECTS,
- null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
+ VibratorInfo info = new InfoBuilder()
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .build();
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
// Returns false when there is no compose capability.
- info = new VibratorInfo(/* id= */ 0, /* capabilities= */ 0,
- null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
+ info = new InfoBuilder()
+ .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .build();
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@Test
public void testEquals() {
- VibratorInfo empty = new VibratorInfo(1, 0, null, null);
- VibratorInfo complete = new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
- new int[]{VibrationEffect.EFFECT_CLICK},
- new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
+ InfoBuilder completeBuilder = new InfoBuilder()
+ .setId(1)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .setQFactor(2f)
+ .setResonantFrequency(150f);
+ VibratorInfo complete = completeBuilder.build();
assertEquals(complete, complete);
- assertEquals(complete, new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
- new int[]{VibrationEffect.EFFECT_CLICK},
- new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}));
-
- assertFalse(empty.equals(new VibratorInfo(1, 0, new int[]{}, new int[]{})));
- assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS,
- new int[]{VibrationEffect.EFFECT_CLICK},
- new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})));
- assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
- new int[]{}, new int[]{})));
- assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
- null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})));
- assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
- new int[]{VibrationEffect.EFFECT_CLICK}, null)));
+ assertEquals(complete, completeBuilder.build());
+
+ VibratorInfo completeWithComposeControl = completeBuilder
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .build();
+ assertNotEquals(complete, completeWithComposeControl);
+
+ VibratorInfo completeWithNoEffects = completeBuilder
+ .setSupportedEffects()
+ .setSupportedPrimitives()
+ .build();
+ assertNotEquals(complete, completeWithNoEffects);
+
+ VibratorInfo completeWithUnknownEffects = completeBuilder
+ .setSupportedEffects(null)
+ .build();
+ assertNotEquals(complete, completeWithNoEffects);
+
+ VibratorInfo completeWithUnknownPrimitives = completeBuilder
+ .setSupportedPrimitives(null)
+ .build();
+ assertNotEquals(complete, completeWithUnknownPrimitives);
+
+ VibratorInfo completeWithDifferentF0 = completeBuilder
+ .setResonantFrequency(complete.getResonantFrequency() + 3f)
+ .build();
+ assertNotEquals(complete, completeWithDifferentF0);
+
+ VibratorInfo completeWithUnknownF0 = completeBuilder
+ .setResonantFrequency(Float.NaN)
+ .build();
+ assertNotEquals(complete, completeWithUnknownF0);
+
+ VibratorInfo completeWithUnknownQFactor = completeBuilder
+ .setQFactor(Float.NaN)
+ .build();
+ assertNotEquals(complete, completeWithUnknownQFactor);
+
+ VibratorInfo completeWithDifferentQFactor = completeBuilder
+ .setQFactor(complete.getQFactor() + 3f)
+ .build();
+ assertNotEquals(complete, completeWithDifferentQFactor);
+
+ VibratorInfo empty = new InfoBuilder().setId(1).build();
+ VibratorInfo emptyWithKnownSupport = new InfoBuilder()
+ .setId(1)
+ .setSupportedEffects()
+ .setSupportedPrimitives()
+ .build();
+ assertNotEquals(empty, emptyWithKnownSupport);
}
@Test
- public void testSerialization() {
- VibratorInfo original = new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS,
- new int[]{VibrationEffect.EFFECT_CLICK}, null);
+ public void testParceling() {
+ VibratorInfo original = new InfoBuilder()
+ .setId(1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .setSupportedPrimitives(null)
+ .setResonantFrequency(1.3f)
+ .setQFactor(Float.NaN)
+ .build();
Parcel parcel = Parcel.obtain();
original.writeToParcel(parcel, 0);
@@ -107,7 +164,47 @@ public class VibratorInfoTest {
assertEquals(original, restored);
}
- private static VibratorInfo createInfo(long capabilities) {
- return new VibratorInfo(/* id= */ 0, capabilities, null, null);
+ private static class InfoBuilder {
+ private int mId = 0;
+ private int mCapabilities = 0;
+ private int[] mSupportedEffects = null;
+ private int[] mSupportedPrimitives = null;
+ private float mResonantFrequency = Float.NaN;
+ private float mQFactor = Float.NaN;
+
+ public InfoBuilder setId(int id) {
+ mId = id;
+ return this;
+ }
+
+ public InfoBuilder setCapabilities(int capabilities) {
+ mCapabilities = capabilities;
+ return this;
+ }
+
+ public InfoBuilder setSupportedEffects(int... supportedEffects) {
+ mSupportedEffects = supportedEffects;
+ return this;
+ }
+
+ public InfoBuilder setSupportedPrimitives(int... supportedPrimitives) {
+ mSupportedPrimitives = supportedPrimitives;
+ return this;
+ }
+
+ public InfoBuilder setResonantFrequency(float resonantFrequency) {
+ mResonantFrequency = resonantFrequency;
+ return this;
+ }
+
+ public InfoBuilder setQFactor(float qFactor) {
+ mQFactor = qFactor;
+ return this;
+ }
+
+ public VibratorInfo build() {
+ return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives,
+ mResonantFrequency, mQFactor);
+ }
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
index 70888905d7c8..10ff3a47a7d9 100644
--- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
@@ -19,18 +19,22 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.power.MeasuredEnergyStats;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -81,6 +85,10 @@ public class CpuPowerCalculatorTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
+ final boolean[] supportedPowerBuckets =
+ new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
+ supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+
mStatsRule.getBatteryStats()
.setUserInfoProvider(mMockUserInfoProvider)
.setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders)
@@ -88,7 +96,8 @@ public class CpuPowerCalculatorTest {
.setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader)
.setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader)
.setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
- .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader);
+ .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader)
+ .initMeasuredEnergyStatsLocked(supportedPowerBuckets, 0);
}
@Test
@@ -103,28 +112,28 @@ public class CpuPowerCalculatorTest {
// User/System CPU time
doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
+ final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
// User/system time in microseconds
callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000});
callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000});
return null;
- }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(any());
+ }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());
// Active CPU time
doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+ final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
callback.onUidCpuTime(APP_UID1, 1111L);
callback.onUidCpuTime(APP_UID2, 3333L);
return null;
- }).when(mMockKerneCpuUidActiveTimeReader).readDelta(any());
+ }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
// Per-cluster CPU time
doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
+ final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
return null;
- }).when(mMockKernelCpuUidClusterTimeReader).readDelta(any());
+ }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());
mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
@@ -134,7 +143,8 @@ public class CpuPowerCalculatorTest {
CpuPowerCalculator calculator =
new CpuPowerCalculator(mStatsRule.getPowerProfile());
- mStatsRule.apply(calculator);
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+ calculator);
UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
@@ -150,4 +160,64 @@ public class CpuPowerCalculatorTest {
.isWithin(PRECISION).of(2.672322);
assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
}
+
+ @Test
+ public void testMeasuredEnergyBasedModel() {
+ when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
+
+ when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000});
+ when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000});
+
+ when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false);
+
+ // User/System CPU time
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
+ // User/system time in microseconds
+ callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000});
+ callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000});
+ return null;
+ }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());
+
+ // Active CPU time
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
+ callback.onUidCpuTime(APP_UID1, 1111L);
+ callback.onUidCpuTime(APP_UID2, 3333L);
+ return null;
+ }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
+
+ // Per-cluster CPU time
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
+ callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
+ callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
+ return null;
+ }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());
+
+ final long[] clusterChargesUC = new long[]{13577531, 24688642};
+ mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, clusterChargesUC);
+
+ mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("foo").addCpuTimeLocked(4321, 1234);
+ mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("bar").addCpuTimeLocked(5432, 2345);
+
+ CpuPowerCalculator calculator =
+ new CpuPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(calculator);
+
+ UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
+ assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
+ .isEqualTo(3333);
+ assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+ .isWithin(PRECISION).of(3.18877);
+ assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar");
+
+ UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
+ assertThat(uidConsumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
+ .isEqualTo(7777);
+ assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+ .isWithin(PRECISION).of(7.44072);
+ assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
+ }
}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 77a38a9bb1a0..b3a180d70fe2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -154,6 +154,7 @@
<assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="media" />
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="media" />
<assign-permission name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" uid="media" />
+ <assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="media" />
<assign-permission name="android.permission.INTERNET" uid="media" />
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index 25492779e6e9..657a32c1ac46 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -61,53 +61,6 @@ final class RippleShader extends RuntimeShader {
+ " return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);\n"
+ "}\n"
+ "\n"
- + "float softRing(vec2 uv, vec2 xy, float radius, float blur) {\n"
- + " float thickness = 0.4;\n"
- + " float circle_outer = softCircle(uv, xy, radius + thickness * 0.5, blur);\n"
- + " float circle_inner = softCircle(uv, xy, radius - thickness * 0.5, blur);\n"
- + " return circle_outer - circle_inner;\n"
- + "}\n"
- + "\n"
- + "struct Viewport {\n"
- + " float aspect;\n"
- + " vec2 uv;\n"
- + " vec2 resolution_pixels;\n"
- + "};\n"
- + "\n"
- + "Viewport getViewport(vec2 frag_coord, vec2 resolution_pixels) {\n"
- + " Viewport v;\n"
- + " v.aspect = resolution_pixels.y / resolution_pixels.x;\n"
- + " v.uv = frag_coord / resolution_pixels;\n"
- + " v.uv.y = (1.0 - v.uv.y) * v.aspect;\n"
- + " v.resolution_pixels = resolution_pixels;\n"
- + " return v;\n"
- + "}\n"
- + "\n"
- + "vec2 getTouch(vec2 touch_position_pixels, Viewport viewport) {\n"
- + " vec2 touch = touch_position_pixels / viewport.resolution_pixels;\n"
- + " touch.y *= viewport.aspect;\n"
- + " return touch;\n"
- + "}\n"
- + "\n"
- + "struct Wave {\n"
- + " float ring;\n"
- + " float circle;\n"
- + "};\n"
- + "\n"
- + "Wave getWave(Viewport viewport, vec2 touch, float progress) {\n"
- + " float fade = pow((clamp(progress, 0.8, 1.0)), 8.);\n"
- + " Wave w;\n"
- + " w.ring = max(softRing(viewport.uv, touch, progress, 0.45) - fade, 0.);\n"
- + " w.circle = softCircle(viewport.uv, touch, 2.0 * progress, 0.2) - progress;\n"
- + " return w;\n"
- + "}\n"
- + "\n"
- + "vec4 getRipple(vec4 color, float loudness, float sparkle, Wave wave) {\n"
- + " float alpha = wave.ring * sparkle * loudness\n"
- + " + wave.circle * color.a;\n"
- + " return vec4(color.rgb, saturate(alpha));\n"
- + "}\n"
- + "\n"
+ "float getRingMask(vec2 frag, vec2 center, float r, float progress) {\n"
+ " float dist = distance(frag, center);\n"
+ " float expansion = r * .6;\n"
@@ -126,19 +79,15 @@ final class RippleShader extends RuntimeShader {
+ " float fadeIn = subProgress(0., 0.175, in_progress);\n"
+ " float fadeOutNoise = subProgress(0.375, 1., in_progress);\n"
+ " float fadeOutRipple = subProgress(0.375, 0.75, in_progress);\n"
- + " Viewport vp = getViewport(p, in_resolution);\n"
- + " vec2 touch = getTouch(in_origin, vp);\n"
- + " Wave w = getWave(vp, touch, in_progress * 0.25);\n"
+ " float ring = getRingMask(p, in_origin, in_maxRadius, fadeIn);\n"
+ " float alpha = min(fadeIn, 1. - fadeOutNoise);\n"
+ " float sparkle = sparkles(p, in_progress * 0.25 + in_secondsOffset)\n"
+ " * ring * alpha;\n"
- + " vec4 r = getRipple(in_color, 1., sparkle, w);\n"
+ " float fade = min(fadeIn, 1.-fadeOutRipple);\n"
- + " vec4 circle = vec4(in_color.rgb, softCircle(p, in_origin, in_maxRadius "
- + " * fadeIn, 0.2) * fade * in_color.a);\n"
+ + " vec4 circle = in_color * (softCircle(p, in_origin, in_maxRadius "
+ + " * fadeIn, 0.2) * fade);\n"
+ " float mask = in_hasMask == 1. ? sample(in_shader).a > 0. ? 1. : 0. : 1.;\n"
- + " return mix(circle, vec4(1.), sparkle * mask);\n"
+ + " return mix(circle, vec4(sparkle), sparkle) * mask;\n"
+ "}";
private static final String SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN;
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 55015696ff47..35b1c169f283 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -121,4 +121,22 @@ public class AndroidKeyStoreMaintenance {
return SYSTEM_ERROR;
}
}
+
+ /**
+ * Queries user state from Keystore 2.0.
+ *
+ * @param userId - Android user id of the user.
+ * @return UserState enum variant as integer if successful or an error
+ */
+ public static int getState(int userId) {
+ try {
+ return getService().getState(userId);
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "getState failed", e);
+ return e.errorCode;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 93658e69eac8..937f01ce3767 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -43,6 +43,7 @@ import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeystoreResponse;
import android.security.keystore.UserNotAuthenticatedException;
+import android.security.maintenance.UserState;
import android.system.keystore2.Domain;
import android.util.Log;
@@ -196,6 +197,19 @@ public class KeyStore {
public State state(int userId) {
final int ret;
try {
+ if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
+ int userState = AndroidKeyStoreMaintenance.getState(userId);
+ switch (userState) {
+ case UserState.UNINITIALIZED:
+ return KeyStore.State.UNINITIALIZED;
+ case UserState.LSKF_UNLOCKED:
+ return KeyStore.State.UNLOCKED;
+ case UserState.LSKF_LOCKED:
+ return KeyStore.State.LOCKED;
+ default:
+ throw new AssertionError(KeyStore.VALUE_CORRUPTED);
+ }
+ }
ret = mBinder.getState(userId);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 46884fefd69c..7d65a08ce798 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -83,6 +83,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private boolean mIsInitialized;
private Listener mListener;
private Executor mListenerExecutor;
+ private Rect mObscuredTouchRect;
private final Rect mTmpRect = new Rect();
private final Rect mTmpRootRect = new Rect();
@@ -161,6 +162,15 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
}
/**
+ * Indicates a region of the view that is not touchable.
+ *
+ * @param obscuredRect the obscured region of the view.
+ */
+ public void setObscuredTouchRect(Rect obscuredRect) {
+ mObscuredTouchRect = obscuredRect;
+ }
+
+ /**
* Call when view position or size has changed. Do not call when animating.
*/
public void onLocationChanged() {
@@ -384,6 +394,10 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
mTmpRect.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + getWidth(), mTmpLocation[1] + getHeight());
inoutInfo.touchableRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+
+ if (mObscuredTouchRect != null) {
+ inoutInfo.touchableRegion.union(mObscuredTouchRect);
+ }
}
@Override
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 64a44ca9e7e9..16ede735660f 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
@@ -41,7 +41,6 @@ import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.Region;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
@@ -1046,6 +1045,7 @@ public class BubbleStackView extends FrameLayout
}
};
+ // TODO: Create ManageMenuView and move setup / animations there
private void setUpManageMenu() {
if (mManageMenu != null) {
removeView(mManageMenu);
@@ -2146,50 +2146,6 @@ public class BubbleStackView extends FrameLayout
}
/**
- * This method is called by {@link android.app.ActivityView} because the BubbleStackView has a
- * higher Z-index than the ActivityView (so that dragged-out bubbles are visible over the AV).
- * ActivityView is asking BubbleStackView to subtract the stack's bounds from the provided
- * touchable region, so that the ActivityView doesn't consume events meant for the stack. Due to
- * the special nature of ActivityView, it does not respect the standard
- * {@link #dispatchTouchEvent} and {@link #onInterceptTouchEvent} methods typically used for
- * this purpose.
- *
- * BubbleStackView is MATCH_PARENT, so that bubbles can be positioned via their translation
- * properties for performance reasons. This means that the default implementation of this method
- * subtracts the entirety of the screen from the ActivityView's touchable region, resulting in
- * it not receiving any touch events. This was previously addressed by returning false in the
- * stack's {@link View#canReceivePointerEvents()} method, but this precluded the use of any
- * touch handlers in the stack or its child views.
- *
- * To support touch handlers, we're overriding this method to leave the ActivityView's touchable
- * region alone. The only touchable part of the stack that can ever overlap the AV is a
- * dragged-out bubble that is animating back into the row of bubbles. It's not worth continually
- * updating the touchable region to allow users to grab a bubble while it completes its ~50ms
- * animation back to the bubble row.
- *
- * NOTE: Any future additions to the stack that obscure the ActivityView region will need their
- * bounds subtracted here in order to receive touch events.
- */
- @Override
- public void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
- // If the notification shade is expanded, or the manage menu is open, or we are showing
- // manage bubbles user education, we shouldn't let the ActivityView steal any touch events
- // from any location.
- if (!mIsExpanded
- || mShowingManage
- || (mManageEduView != null
- && mManageEduView.getVisibility() == VISIBLE)) {
- touchableRegion.setEmpty();
- }
- }
-
- /**
- * If you're here because you're not receiving touch events on a view that is a descendant of
- * BubbleStackView, and you think BSV is intercepting them - it's not! You need to subtract the
- * bounds of the view in question in {@link #subtractObscuredTouchableRegion}. The ActivityView
- * consumes all touch events within its bounds, even for views like the BubbleStackView that are
- * above it. It ignores typical view touch handling methods like this one and
- * dispatchTouchEvent.
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -2539,6 +2495,11 @@ public class BubbleStackView extends FrameLayout
}
mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
+ if (mExpandedBubble.getExpandedView().getTaskView() != null) {
+ mExpandedBubble.getExpandedView().getTaskView().setObscuredTouchRect(mShowingManage
+ ? new Rect(0, 0, getWidth(), getHeight())
+ : null);
+ }
final boolean isLtr =
getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_LTR;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 90e71373b1fd..63968f333ff4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.apppairs
import android.os.SystemClock
import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -59,6 +60,14 @@ class AppPairsTestCannotPairNonResizeableApps(
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
@Presubmit
@Test
fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index dc51b4fb5a9e..63e9a787aa17 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -56,6 +56,14 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
@Presubmit
@Test
fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 5bb9b2f8b8ca..234dda448cc8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -61,6 +61,14 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
@Presubmit
@Test
fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 91e080f65550..128560a0dd21 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -20,19 +20,23 @@ import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.system.helpers.ActivityHelper
import android.util.Log
+import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -134,17 +138,39 @@ abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter)
@Presubmit
@Test
- open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ open fun navBarLayerIsAlwaysVisible() {
+ testSpec.navBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerIsAlwaysVisible() {
+ testSpec.statusBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarWindowIsAlwaysVisible() {
+ testSpec.navBarWindowIsAlwaysVisible()
+ }
@Presubmit
@Test
- open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ open fun statusBarWindowIsAlwaysVisible() {
+ testSpec.statusBarWindowIsAlwaysVisible()
+ }
@Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ open fun navBarLayerRotatesAndScales() {
+ testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ testSpec.config.endRotation)
+ }
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ open fun statusBarLayerRotatesScales() {
+ testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
+ testSpec.config.endRotation)
+ }
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index 5f003ba62b2d..d341bb1e6aa9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -27,8 +27,6 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.wm.shell.flicker.appPairsDividerIsVisible
import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
@@ -75,16 +73,6 @@ class RotateTwoLaunchedAppsInAppPairsMode(
@Test
fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
- testSpec.config.endRotation)
-
- @Presubmit
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
- testSpec.config.endRotation)
-
@FlakyTest(bugId = 172776659)
@Test
fun appPairsPrimaryBoundsIsVisible() =
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index d4792088ac31..3bf0296fee20 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -27,16 +27,13 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.appPairsDividerIsVisible
import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -67,20 +64,10 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
- testSpec.config.endRotation)
-
- @Presubmit
- @Test
fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
- Surface.ROTATION_0, testSpec.config.endRotation)
-
- @Presubmit
- @Test
override fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 75c33c671008..033322786d8f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -23,13 +23,7 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -48,7 +42,6 @@ class EnterExitPipTest(
testSpec: FlickerTestParameter
) : PipTransition(testSpec) {
private val testApp = FixedAppHelper(instrumentation)
- private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true) {
@@ -84,14 +77,6 @@ class EnterExitPipTest(
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
fun showBothAppLayersThenHidePip() {
testSpec.assertLayers {
isVisible(testApp.defaultWindowName)
@@ -118,14 +103,6 @@ class EnterExitPipTest(
}
}
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 2f08db1b7d0a..4847c98f54e9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -24,14 +25,6 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.startRotation
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -57,15 +50,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun pipWindowBecomesVisible() {
+ fun pipAppWindowAlwaysVisible() {
testSpec.assertWm {
this.showsAppWindow(pipApp.defaultWindowName)
}
@@ -73,34 +58,37 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
- @Presubmit
- @Test
fun pipLayerBecomesVisible() {
testSpec.assertLayers {
this.isVisible(pipApp.launcherName)
}
}
+ @Postsubmit
+ @Test
+ fun pipWindowBecomesVisible() {
+ testSpec.assertWm {
+ invoke("pipWindowIsNotVisible") { !it.wmState.hasPipWindow() }
+ .then()
+ .invoke("pipWindowIsVisible") { it.wmState.hasPipWindow() }
+ }
+ }
+
+ @FlakyTest(bugId = 140855415)
+ @Test
+ override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
+
@FlakyTest(bugId = 140855415)
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@FlakyTest(bugId = 140855415)
@Test
- fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
@FlakyTest(bugId = 140855415)
@Test
- fun noUncoveredRegions() =
- testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+ override fun noUncoveredRegions() = super.noUncoveredRegions()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 9011f1a9fb6a..ba88ee5751de 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -18,16 +18,13 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
@@ -83,6 +80,14 @@ class EnterPipToOtherOrientationTest(
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
@Presubmit
@Test
fun pipAppWindowIsAlwaysOnTop() {
@@ -109,14 +114,6 @@ class EnterPipToOtherOrientationTest(
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
fun pipAppLayerHidesTestApp() {
testSpec.assertLayersStart {
coversExactly(startingBounds, pipApp.defaultWindowName)
@@ -132,14 +129,6 @@ class EnterPipToOtherOrientationTest(
}
}
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
index 3e331761f767..eae7e973711c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
@@ -24,14 +24,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.Test
import org.junit.runners.Parameterized
@@ -52,22 +45,6 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio
@Presubmit
@Test
- open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
open fun pipWindowBecomesInvisible() {
testSpec.assertWm {
this.showsAppWindow(PIP_WINDOW_TITLE)
@@ -86,21 +63,6 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio
}
}
- @Presubmit
- @Test
- open fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
- @Presubmit
- @Test
- open fun noUncoveredRegions() =
- testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
-
- @Presubmit
- @Test
- open fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
@FlakyTest(bugId = 151179149)
@Test
open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 97afc65b8b61..3309e10f61a6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -24,17 +24,11 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.removeAllTasksButHome
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.FixMethodOrder
@@ -55,7 +49,6 @@ import org.junit.runners.Parameterized
class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
private val testApp = FixedAppHelper(instrumentation)
- private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -105,11 +98,11 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
@Postsubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
@Postsubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
@Postsubmit
@Test
@@ -130,11 +123,11 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
@Postsubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
@Postsubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
companion object {
const val TEST_REPETITIONS = 2
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
index 4c95da284d9e..d011419150e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
@@ -24,8 +24,6 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.google.common.truth.Truth
import org.junit.FixMethodOrder
import org.junit.Test
@@ -59,11 +57,11 @@ class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec
@Postsubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
@Postsubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
@Postsubmit
@Test
@@ -71,6 +69,14 @@ class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec
@Postsubmit
@Test
+ fun pipLayerInsideDisplay() {
+ testSpec.assertLayersStart {
+ coversAtMost(displayBounds, pipApp.defaultWindowName)
+ }
+ }
+
+ @Postsubmit
+ @Test
fun pipWindowMovesUp() = testSpec.assertWmEnd {
val initialState = this.trace?.first()?.wmState
?: error("Trace should not be empty")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index df835d21e73f..49a1055af4a0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -29,10 +29,6 @@ import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerRotatesScales
@@ -77,34 +73,18 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
testSpec.config.endRotation, allStates = false)
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
@FlakyTest(bugId = 140855415)
@Test
- fun navBarLayerRotatesAndScales() =
+ override fun navBarLayerRotatesAndScales() =
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
testSpec.config.endRotation)
@FlakyTest(bugId = 140855415)
@Test
- fun statusBarLayerRotatesScales() =
+ override fun statusBarLayerRotatesScales() =
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
testSpec.config.endRotation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 1bb1d2861f3f..945a20b28ff0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -26,14 +26,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -68,22 +61,6 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
fun appReplacesPipWindow() {
testSpec.assertWm {
this.showsAppWindow(PIP_WINDOW_TITLE)
@@ -94,11 +71,6 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Presubmit
@Test
- fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
- @Presubmit
- @Test
fun appReplacesPipLayer() {
testSpec.assertLayers {
this.isVisible(PIP_WINDOW_TITLE)
@@ -107,15 +79,13 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
}
}
- @Presubmit
- @Test
- fun noUncoveredRegions() =
- testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
-
- @Presubmit
+ @FlakyTest
@Test
- fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ fun testAppCoversFullScreen() {
+ testSpec.assertLayersStart {
+ coversExactly(displayBounds, pipApp.defaultWindowName)
+ }
+ }
@FlakyTest(bugId = 151179149)
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index b0a9afef9215..7dc7e7d25b79 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -18,27 +18,37 @@ package com.android.wm.shell.flicker.pip
import android.app.Instrumentation
import android.content.Intent
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.helpers.PipAppHelper
import com.android.wm.shell.flicker.removeAllTasksButHome
import com.android.wm.shell.flicker.testapp.Components
+import org.junit.Test
abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected val isRotated = testSpec.config.startRotation.isRotated()
protected val pipApp = PipAppHelper(instrumentation)
+ protected val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
-
// Helper class to process test actions by broadcast.
protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
private fun createIntentWithAction(broadcastAction: String): Intent {
@@ -148,4 +158,35 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
extraSpec(this, configuration)
}
}
+
+ @Presubmit
+ @Test
+ open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ open fun navBarLayerRotatesAndScales() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerRotatesScales() =
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+ @Presubmit
+ @Test
+ open fun noUncoveredRegions() =
+ testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 7916ce59af52..67e1768f3a9f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -25,10 +26,6 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
@@ -83,6 +80,14 @@ class SetRequestedOrientationWhilePinnedTest(
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
@Presubmit
@Test
fun pipWindowInsideDisplay() {
@@ -101,20 +106,18 @@ class SetRequestedOrientationWhilePinnedTest(
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
fun pipLayerInsideDisplay() {
testSpec.assertLayersStart {
coversAtMost(startingBounds, pipApp.defaultWindowName)
}
}
+ @Postsubmit
+ @Test
+ fun pipAlwaysVisible() = testSpec.assertWm {
+ this.showsAppWindow(pipApp.windowName)
+ }
+
@Presubmit
@Test
fun pipAppLayerCoversFullScreen() {
@@ -123,14 +126,6 @@ class SetRequestedOrientationWhilePinnedTest(
}
}
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index c0ef7be8b673..7e45f952d389 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -226,8 +226,6 @@ void AssetManager2::BuildDynamicRefTable() {
}
void AssetManager2::DumpToLog() const {
- base::ScopedLogSeverity _log(base::INFO);
-
LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this);
std::string list;
@@ -1721,7 +1719,6 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) {
}
void Theme::Dump() const {
- base::ScopedLogSeverity _log(base::INFO);
LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_);
for (int p = 0; p < packages_.size(); p++) {
diff --git a/media/jni/soundpool/Sound.cpp b/media/jni/soundpool/Sound.cpp
index f8b4bdb1f4d5..50e0d336f981 100644
--- a/media/jni/soundpool/Sound.cpp
+++ b/media/jni/soundpool/Sound.cpp
@@ -99,8 +99,8 @@ static status_t decode(int fd, int64_t offset, int64_t length,
__func__);
break;
}
- int sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
- ALOGV("%s: read %d", __func__, sampleSize);
+ ssize_t sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
+ ALOGV("%s: read %zd", __func__, sampleSize);
if (sampleSize < 0) {
sampleSize = 0;
sawInputEOS = true;
@@ -124,8 +124,8 @@ static status_t decode(int fd, int64_t offset, int64_t length,
}
AMediaCodecBufferInfo info;
- const int status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
- ALOGV("%s: dequeueoutput returned: %d", __func__, status);
+ const ssize_t status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
+ ALOGV("%s: dequeueoutput returned: %zd", __func__, status);
if (status >= 0) {
if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
ALOGV("%s: output EOS", __func__);
@@ -167,10 +167,10 @@ static status_t decode(int fd, int64_t offset, int64_t length,
} else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
ALOGV("%s: no output buffer right now", __func__);
} else if (status <= AMEDIA_ERROR_BASE) {
- ALOGE("%s: decode error: %d", __func__, status);
+ ALOGE("%s: decode error: %zd", __func__, status);
break;
} else {
- ALOGV("%s: unexpected info code: %d", __func__, status);
+ ALOGV("%s: unexpected info code: %zd", __func__, status);
}
}
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index abb0f1208bc2..95fe000bd2c2 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -320,7 +320,8 @@ void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
// audio track while the new one is being started and avoids processing them with
// wrong audio audio buffer size (mAudioBufferSize)
auto toggle = mToggle ^ 1;
- void* userData = (void*)((uintptr_t)this | toggle);
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
audio_channel_mask_t soundChannelMask = sound->getChannelMask();
// When sound contains a valid channel mask, use it as is.
// Otherwise, use stream count to calculate channel mask.
@@ -391,6 +392,7 @@ exit:
void Stream::staticCallback(int event, void* user, void* info)
{
const auto userAsInt = (uintptr_t)user;
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
auto stream = reinterpret_cast<Stream*>(userAsInt & ~1);
stream->callback(event, info, int(userAsInt & 1), 0 /* tries */);
}
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index 502ee00b583e..8b84bf3eb8d8 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -330,7 +330,7 @@ ssize_t StreamManager::removeFromQueues_l(
// streams on mProcessingStreams are undergoing processing by the StreamManager thread
// and do not participate in normal stream migration.
- return found;
+ return (ssize_t)found;
}
void StreamManager::addToRestartQueue_l(Stream *stream) {
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index 357cc63bd41e..a66d99fbd9f4 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -34,7 +34,8 @@ static struct fields_t {
jclass mSoundPoolClass;
} fields;
static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
- return (SoundPool*)env->GetLongField(thiz, fields.mNativeContext);
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ return reinterpret_cast<SoundPool*>(env->GetLongField(thiz, fields.mNativeContext));
}
static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
struct audio_attributes_fields_t {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index 53f7e44bc25a..ca1320448726 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -50,4 +50,8 @@ public abstract class QSTileView extends LinearLayout {
public abstract void onStateChanged(State state);
public abstract int getDetailY();
+
+ public View getLabelContainer() {
+ return null;
+ }
}
diff --git a/packages/SystemUI/res/drawable/qs_tile_background.xml b/packages/SystemUI/res/drawable/qs_tile_background.xml
new file mode 100644
index 000000000000..265f575fc99c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_tile_background.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask"
+ android:drawable="@drawable/qs_tile_background_shape" />
+ <item android:id="@id/background"
+ android:drawable="@drawable/qs_tile_background_shape"/>
+</ripple> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_tile_background_shape.xml b/packages/SystemUI/res/drawable/qs_tile_background_shape.xml
new file mode 100644
index 000000000000..f6b68347124e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_tile_background_shape.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <corners android:radius="@dimen/qs_corner_radius" />
+ <solid android:color="#FFFFFF" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml b/packages/SystemUI/res/layout/udfps_bp_view.xml
index 0cfbf2e61dd1..f1c55ef16cdc 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml
+++ b/packages/SystemUI/res/layout/udfps_bp_view.xml
@@ -14,9 +14,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.UdfpsAnimationViewBp
+<com.android.systemui.biometrics.UdfpsBpView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/udfps_animation_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewBp>
+</com.android.systemui.biometrics.UdfpsBpView>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml b/packages/SystemUI/res/layout/udfps_enroll_view.xml
index 9b5752d2de59..40353052ca85 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml
+++ b/packages/SystemUI/res/layout/udfps_enroll_view.xml
@@ -14,13 +14,13 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.UdfpsAnimationViewEnroll
+<com.android.systemui.biometrics.UdfpsEnrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/udfps_animation_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <!-- Enrollment progress bar-->
+ <!-- Enrollment progress bar -->
<com.android.systemui.biometrics.UdfpsProgressBar
android:id="@+id/progress_bar"
android:layout_width="match_parent"
@@ -31,4 +31,9 @@
android:layout_gravity="center"
android:visibility="gone"/>
-</com.android.systemui.biometrics.UdfpsAnimationViewEnroll>
+ <!-- Fingerprint -->
+ <ImageView
+ android:id="@+id/udfps_enroll_animation_fp_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</com.android.systemui.biometrics.UdfpsEnrollView>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml b/packages/SystemUI/res/layout/udfps_fpm_other_view.xml
index f32faa0df867..6ecbb473d720 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
+++ b/packages/SystemUI/res/layout/udfps_fpm_other_view.xml
@@ -14,9 +14,15 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.UdfpsAnimationViewFpmOther
+<com.android.systemui.biometrics.UdfpsFpmOtherView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/udfps_animation_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewFpmOther>
+
+ <!-- Fingerprint -->
+ <ImageView
+ android:id="@+id/udfps_fpm_other_fp_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</com.android.systemui.biometrics.UdfpsFpmOtherView>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
index 644d1adac46b..0199ccb04be6 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
+++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
@@ -14,9 +14,17 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.UdfpsAnimationViewKeyguard
+<com.android.systemui.biometrics.UdfpsKeyguardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/udfps_animation_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewKeyguard>
+
+ <!-- TODO: add background protection -->
+
+ <!-- Fingerprint -->
+ <ImageView
+ android:id="@+id/udfps_keyguard_animation_fp_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</com.android.systemui.biometrics.UdfpsKeyguardView>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index e24c9e99a405..50b2f209d871 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -22,6 +22,11 @@
android:layout_height="match_parent"
systemui:sensorTouchAreaCoefficient="0.5">
+ <ViewStub
+ android:id="@+id/animation_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
<com.android.systemui.biometrics.UdfpsSurfaceView
android:id="@+id/hbm_view"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 885cd254a1ea..2062104be2df 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -512,6 +512,7 @@
<!-- The size of the gesture span needed to activate the "pull" notification expansion -->
<dimen name="pull_span_min">25dp</dimen>
+ <dimen name="qs_corner_radius">14dp</dimen>
<dimen name="qs_tile_height">96dp</dimen>
<!--notification_side_paddings + notification_content_margin_start - (qs_quick_tile_size - qs_tile_background_size) / 2 -->
<dimen name="qs_tile_layout_margin_side">18dp</dimen>
@@ -528,6 +529,8 @@
<dimen name="qs_tile_margin_top">0dp</dimen>
<dimen name="qs_tile_icon_background_stroke_width">-1dp</dimen>
<dimen name="qs_tile_background_size">44dp</dimen>
+ <dimen name="qs_icon_size">20dp</dimen>
+ <dimen name="qs_label_container_margin">10dp</dimen>
<dimen name="qs_quick_tile_size">48dp</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 2040347de1b5..e53f5c97bb5c 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -20,7 +20,6 @@ import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
-import android.Manifest;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -43,6 +42,7 @@ import androidx.annotation.WorkerThread;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
@@ -370,13 +370,9 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
}
// TODO ntmyren: remove after teamfood is finished
- private boolean shouldShowAppPredictor(String pkgName) {
- if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled",
- false)) {
- return false;
- }
- return mPackageManager.checkPermission(Manifest.permission.MANAGE_APP_PREDICTIONS, pkgName)
- == PackageManager.PERMISSION_GRANTED;
+ private boolean showSystemApps() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false);
}
/**
@@ -399,8 +395,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
return true;
}
// TODO ntmyren: Replace this with more robust check if this moves beyond teamfood
- if ((appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName))
- || shouldShowAppPredictor(packageName)
+ if (((showSystemApps() && !packageName.equals("android"))
+ || appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName))
|| isSpeechRecognizerUsage(appOpCode, packageName)) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 43ecf6778022..2036150d3679 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -16,57 +16,63 @@
package com.android.systemui.biometrics;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.FrameLayout;
-import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.statusbar.phone.StatusBar;
-
/**
* Base class for views containing UDFPS animations. Note that this is a FrameLayout so that we
- * can support multiple child views drawing on the same region around the sensor location.
+ * can support multiple child views drawing in the same region around the sensor location.
+ *
+ * - hides animation view when pausing auth
+ * - sends illumination events to fingerprint drawable
+ * - sends sensor rect updates to fingerprint drawable
+ * - optionally can override dozeTimeTick to adjust views for burn-in mitigation
*/
-public abstract class UdfpsAnimationView extends FrameLayout implements DozeReceiver,
- StatusBar.ExpansionChangedListener {
-
- private static final String TAG = "UdfpsAnimationView";
+abstract class UdfpsAnimationView extends FrameLayout {
- @Nullable protected abstract UdfpsAnimation getUdfpsAnimation();
-
- @NonNull private UdfpsView mParent;
- @NonNull private RectF mSensorRect;
private int mAlpha;
+ private boolean mPauseAuth;
public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
- mSensorRect = new RectF();
- setWillNotDraw(false);
}
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
+ /**
+ * Fingerprint drawable
+ */
+ abstract UdfpsDrawable getDrawable();
- if (getUdfpsAnimation() != null) {
- final int alpha = mParent.shouldPauseAuth() ? mAlpha : 255;
- getUdfpsAnimation().setAlpha(alpha);
- getUdfpsAnimation().draw(canvas);
- }
+ void onSensorRectUpdated(RectF bounds) {
+ getDrawable().onSensorRectUpdated(bounds);
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
+ void onIlluminationStarting() {
+ getDrawable().setIlluminationShowing(true);
+ getDrawable().invalidateSelf();
+ }
+
+ void onIlluminationStopped() {
+ getDrawable().setIlluminationShowing(false);
+ getDrawable().invalidateSelf();
+ }
- if (getUdfpsAnimation() != null) {
- getUdfpsAnimation().onDestroy();
+ /**
+ * @return true if changed
+ */
+ boolean setPauseAuth(boolean pauseAuth) {
+ if (pauseAuth != mPauseAuth) {
+ mPauseAuth = pauseAuth;
+ updateAlpha();
+ return true;
}
+ return false;
+ }
+
+ private void updateAlpha() {
+ getDrawable().setAlpha(mPauseAuth ? mAlpha : 255);
}
private int expansionToAlpha(float expansion) {
@@ -81,76 +87,15 @@ public abstract class UdfpsAnimationView extends FrameLayout implements DozeRece
return (int) ((1 - percent) * 255);
}
- void onIlluminationStarting() {
- if (getUdfpsAnimation() == null) {
- return;
- }
-
- getUdfpsAnimation().setIlluminationShowing(true);
- postInvalidate();
- }
-
- void onIlluminationStopped() {
- if (getUdfpsAnimation() == null) {
- return;
- }
-
- getUdfpsAnimation().setIlluminationShowing(false);
- postInvalidate();
- }
-
- void setParent(@NonNull UdfpsView parent) {
- mParent = parent;
- }
-
- void onSensorRectUpdated(@NonNull RectF sensorRect) {
- mSensorRect = sensorRect;
- if (getUdfpsAnimation() != null) {
- getUdfpsAnimation().onSensorRectUpdated(mSensorRect);
- }
- }
-
- void updateColor() {
- if (getUdfpsAnimation() != null) {
- getUdfpsAnimation().updateColor();
- }
- postInvalidate();
- }
-
- @Override
- public void dozeTimeTick() {
- if (getUdfpsAnimation() instanceof DozeReceiver) {
- ((DozeReceiver) getUdfpsAnimation()).dozeTimeTick();
- }
- }
-
- @Override
public void onExpansionChanged(float expansion, boolean expanded) {
mAlpha = expansionToAlpha(expansion);
- postInvalidate();
- }
-
- public int getPaddingX() {
- if (getUdfpsAnimation() == null) {
- return 0;
- }
- return getUdfpsAnimation().getPaddingX();
- }
-
- public int getPaddingY() {
- if (getUdfpsAnimation() == null) {
- return 0;
- }
- return getUdfpsAnimation().getPaddingY();
+ updateAlpha();
}
/**
- * @return the amount of translation needed if the view currently requires the user to touch
- * somewhere other than the exact center of the sensor. For example, this can happen
- * during guided enrollment.
+ * @return true if handled
*/
- @NonNull
- PointF getTouchTranslation() {
- return new PointF(0, 0);
+ boolean dozeTimeTick() {
+ return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
new file mode 100644
index 000000000000..b6d80ba14dc0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -0,0 +1,167 @@
+/*
+ * 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.biometrics;
+
+import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
+
+import android.annotation.NonNull;
+import android.graphics.PointF;
+import android.graphics.RectF;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.util.ViewController;
+
+/**
+ * Handles:
+ * 1. registering for listeners when its view is attached and unregistering on view detached
+ * 2. pausing udfps when fingerprintManager may still be running but we temporarily want to hide
+ * the affordance. this allows us to fade the view in and out nicely (see shouldPauseAuth)
+ * 3. sending events to its view including:
+ * - illumination events
+ * - sensor position changes
+ * - doze time event
+ */
+abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
+ extends ViewController<T> {
+ @NonNull final StatusBarStateController mStatusBarStateController;
+ @NonNull final StatusBar mStatusBar;
+
+ private boolean mNotificationShadeExpanded;
+ private int mStatusBarState;
+
+ protected UdfpsAnimationViewController(
+ T view,
+ StatusBarStateController statusBarStateController,
+ StatusBar statusBar) {
+ super(view);
+ mStatusBarStateController = statusBarStateController;
+ mStatusBar = statusBar;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mStatusBarStateController.addCallback(mStateListener);
+ mStateListener.onStateChanged(mStatusBarStateController.getState());
+ mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mStatusBarStateController.removeCallback(mStateListener);
+ mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener);
+ }
+
+ /**
+ * Returns true if the fingerprint manager is running but we want to temporarily pause
+ * authentication.
+ */
+ boolean shouldPauseAuth() {
+ return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD)
+ || mStatusBarState == SHADE_LOCKED
+ || mStatusBarState == FULLSCREEN_USER_SWITCHER;
+ }
+
+ /**
+ * Send pause auth update to our view.
+ */
+ void updatePauseAuth() {
+ if (mView.setPauseAuth(shouldPauseAuth())) {
+ mView.postInvalidate();
+ }
+ }
+
+ /**
+ * Send sensor position change to our view. This rect contains paddingX and paddingY.
+ */
+ void onSensorRectUpdated(RectF sensorRect) {
+ mView.onSensorRectUpdated(sensorRect);
+ }
+
+ /**
+ * Send dozeTimeTick to view in case it wants to handle its burn-in offset.
+ */
+ void dozeTimeTick() {
+ if (mView.dozeTimeTick()) {
+ mView.postInvalidate();
+ }
+ }
+
+ /**
+ * @return the amount of translation needed if the view currently requires the user to touch
+ * somewhere other than the exact center of the sensor. For example, this can happen
+ * during guided enrollment.
+ */
+ PointF getTouchTranslation() {
+ return new PointF(0, 0);
+ }
+
+ /**
+ * X-Padding to add to left and right of the sensor rectangle area to increase the size of our
+ * window to draw within.
+ * @return
+ */
+ int getPaddingX() {
+ return 0;
+ }
+
+ /**
+ * Y-Padding to add to top and bottom of the sensor rectangle area to increase the size of our
+ * window to draw within.
+ */
+ int getPaddingY() {
+ return 0;
+ }
+
+ /**
+ * Udfps has started illuminating and the fingerprint manager is working on authenticating.
+ */
+ void onIlluminationStarting() {
+ mView.onIlluminationStarting();
+ mView.postInvalidate();
+ }
+
+ /**
+ * Udfps has stopped illuminating and the fingerprint manager is no longer attempting to
+ * authenticate.
+ */
+ void onIlluminationStopped() {
+ mView.onIlluminationStopped();
+ mView.postInvalidate();
+ }
+
+ private final StatusBar.ExpansionChangedListener mStatusBarExpansionChangedListener =
+ new StatusBar.ExpansionChangedListener() {
+ @Override
+ public void onExpansionChanged(float expansion, boolean expanded) {
+ mNotificationShadeExpanded = expanded;
+ mView.onExpansionChanged(expansion, expanded);
+ updatePauseAuth();
+ }
+ };
+
+ private final StatusBarStateController.StateListener mStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ mStatusBarState = newState;
+ updatePauseAuth();
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
deleted file mode 100644
index 543df33dd5d7..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.biometrics;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.PointF;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-
-import com.android.systemui.R;
-
-/**
- * Class that coordinates non-HBM animations during enrollment.
- */
-public class UdfpsAnimationViewEnroll extends UdfpsAnimationView
- implements UdfpsEnrollHelper.Listener {
-
- private static final String TAG = "UdfpsAnimationViewEnroll";
-
- @NonNull private UdfpsAnimationEnroll mUdfpsAnimation;
- @NonNull private UdfpsProgressBar mProgressBar;
- @Nullable private UdfpsEnrollHelper mEnrollHelper;
-
- @NonNull
- @Override
- protected UdfpsAnimation getUdfpsAnimation() {
- return mUdfpsAnimation;
- }
-
- public UdfpsAnimationViewEnroll(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- mUdfpsAnimation = new UdfpsAnimationEnroll(context);
- }
-
- public void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
- mEnrollHelper = helper;
- mUdfpsAnimation.setEnrollHelper(helper);
- }
-
- @Override
- protected void onFinishInflate() {
- mProgressBar = findViewById(R.id.progress_bar);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- if (mEnrollHelper == null) {
- Log.e(TAG, "Enroll helper is null");
- return;
- }
-
- if (mEnrollHelper.shouldShowProgressBar()) {
- mProgressBar.setVisibility(View.VISIBLE);
-
- // Only need enrollment updates if the progress bar is showing :)
- mEnrollHelper.setListener(this);
- }
- }
-
- @Override
- public void onEnrollmentProgress(int remaining, int totalSteps) {
- final int interpolatedProgress = mProgressBar.getMax()
- * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1);
-
- mProgressBar.setProgress(interpolatedProgress, true);
- }
-
- @NonNull
- @Override
- PointF getTouchTranslation() {
- if (!mEnrollHelper.isCenterEnrollmentComplete()) {
- return new PointF(0, 0);
- } else {
- return mEnrollHelper.getNextGuidedEnrollmentPoint();
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java
index 515b442b61f6..70be907228c8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java
@@ -24,19 +24,22 @@ import androidx.annotation.Nullable;
/**
* Class that coordinates non-HBM animations during BiometricPrompt.
*
+ * Currently doesn't draw anything.
+ *
* Note that {@link AuthBiometricUdfpsView} also shows UDFPS animations. At some point we should
- * de-dupe this if necessary. This will probably happen once the top-level TODO in UdfpsController
- * is completed (inflate operation-specific views, instead of inflating generic udfps_view and
- * adding operation-specific animations to it).
+ * de-dupe this if necessary.
*/
-public class UdfpsAnimationViewBp extends UdfpsAnimationView {
- public UdfpsAnimationViewBp(Context context, @Nullable AttributeSet attrs) {
+public class UdfpsBpView extends UdfpsAnimationView {
+ private UdfpsFpDrawable mFingerprintDrawable;
+
+ public UdfpsBpView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
+ // Drawable isn't ever added to the view, so we don't currently show anything
+ mFingerprintDrawable = new UdfpsFpDrawable(mContext);
}
- @Nullable
@Override
- protected UdfpsAnimation getUdfpsAnimation() {
- return null;
+ UdfpsDrawable getDrawable() {
+ return mFingerprintDrawable;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
new file mode 100644
index 000000000000..b712c655a6e7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
@@ -0,0 +1,32 @@
+/*
+ * 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.biometrics;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations for biometric prompt.
+ */
+class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> {
+ protected UdfpsBpViewController(
+ UdfpsBpView view,
+ StatusBarStateController statusBarStateController,
+ StatusBar statusBar) {
+ super(view, statusBarStateController, statusBar);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 4b6a8f639cc4..94aeb73c4b42 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -31,9 +31,9 @@ import android.graphics.RectF;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.SystemClock;
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -195,17 +195,6 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
}
}
- @VisibleForTesting final StatusBar.ExpansionChangedListener mStatusBarExpansionListener =
- (expansion, expanded) -> mView.onExpansionChanged(expansion, expanded);
-
- @VisibleForTesting final StatusBarStateController.StateListener mStatusBarStateListener =
- new StatusBarStateController.StateListener() {
- @Override
- public void onStateChanged(int newState) {
- mView.onStateChanged(newState);
- }
- };
-
private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
final float vx = tracker.getXVelocity(pointerId);
final float vy = tracker.getYVelocity(pointerId);
@@ -360,10 +349,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
@Override
public void dozeTimeTick() {
- if (mView == null) {
- return;
+ if (mView != null) {
+ mView.dozeTimeTick();
}
- mView.dozeTimeTick();
}
/**
@@ -387,7 +375,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
}
}
- private WindowManager.LayoutParams computeLayoutParams(@Nullable UdfpsAnimationView animation) {
+ private WindowManager.LayoutParams computeLayoutParams(
+ @Nullable UdfpsAnimationViewController animation) {
final int paddingX = animation != null ? animation.getPaddingX() : 0;
final int paddingY = animation != null ? animation.getPaddingY() : 0;
@@ -438,20 +427,13 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
mFgExecutor.execute(() -> {
if (mView == null) {
try {
- Log.v(TAG, "showUdfpsOverlay | adding window");
- // TODO: Eventually we should refactor the code to inflate an
- // operation-specific view here, instead of inflating a generic udfps_view
- // and adding operation-specific animations to it.
+ Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason);
mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false);
mView.setSensorProperties(mSensorProps);
mView.setHbmCallback(this);
-
- final UdfpsAnimationView animation = getUdfpsAnimationViewForReason(reason);
- mView.setAnimationView(animation);
-
- mStatusBar.addExpansionChangedListener(mStatusBarExpansionListener);
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState());
+ UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason);
+ animation.init();
+ mView.setAnimationViewController(animation);
mWindowManager.addView(mView, computeLayoutParams(animation));
mView.setOnTouchListener(mOnTouchListener);
@@ -464,40 +446,46 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
});
}
- @NonNull
- private UdfpsAnimationView getUdfpsAnimationViewForReason(int reason) {
- Log.d(TAG, "getUdfpsAnimationForReason: " + reason);
-
- final LayoutInflater inflater = LayoutInflater.from(mContext);
-
+ private UdfpsAnimationViewController inflateUdfpsAnimation(int reason) {
switch (reason) {
case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR:
- case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: {
- final UdfpsAnimationViewEnroll view = (UdfpsAnimationViewEnroll)
- inflater.inflate(R.layout.udfps_animation_view_enroll, null, false);
- view.setEnrollHelper(mServerRequest.mEnrollHelper);
- return view;
- }
-
- case IUdfpsOverlayController.REASON_AUTH_BP: {
- final UdfpsAnimationViewBp view = (UdfpsAnimationViewBp)
- inflater.inflate(R.layout.udfps_animation_view_bp, null, false);
- return view;
- }
-
- case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: {
- final UdfpsAnimationViewKeyguard view = (UdfpsAnimationViewKeyguard)
- inflater.inflate(R.layout.udfps_animation_view_keyguard, null, false);
- view.setStatusBarStateController(mStatusBarStateController);
- return view;
- }
-
- case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: {
- final UdfpsAnimationViewFpmOther view = (UdfpsAnimationViewFpmOther)
- inflater.inflate(R.layout.udfps_animation_view_fpm_other, null, false);
- return view;
- }
-
+ case IUdfpsOverlayController.REASON_ENROLL_ENROLLING:
+ UdfpsEnrollView enrollView = (UdfpsEnrollView) mInflater.inflate(
+ R.layout.udfps_enroll_view, null);
+ mView.addView(enrollView);
+ return new UdfpsEnrollViewController(
+ enrollView,
+ mServerRequest.mEnrollHelper,
+ mStatusBarStateController,
+ mStatusBar
+ );
+ case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
+ UdfpsKeyguardView keyguardView = (UdfpsKeyguardView)
+ mInflater.inflate(R.layout.udfps_keyguard_view, null);
+ mView.addView(keyguardView);
+ return new UdfpsKeyguardViewController(
+ keyguardView,
+ mStatusBarStateController,
+ mStatusBar
+ );
+ case IUdfpsOverlayController.REASON_AUTH_BP:
+ // note: empty controller, currently shows no visual affordance
+ UdfpsBpView bpView = (UdfpsBpView) mInflater.inflate(R.layout.udfps_bp_view, null);
+ mView.addView(bpView);
+ return new UdfpsBpViewController(
+ bpView,
+ mStatusBarStateController,
+ mStatusBar
+ );
+ case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER:
+ UdfpsFpmOtherView authOtherView = (UdfpsFpmOtherView)
+ mInflater.inflate(R.layout.udfps_fpm_other_view, null);
+ mView.addView(authOtherView);
+ return new UdfpsFpmOtherViewController(
+ authOtherView,
+ mStatusBarStateController,
+ mStatusBar
+ );
default:
Log.d(TAG, "Animation for reason " + reason + " not supported yet");
return null;
@@ -510,11 +498,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
Log.v(TAG, "hideUdfpsOverlay | removing window");
// Reset the controller back to its starting state.
onFingerUp();
-
- mStatusBar.removeExpansionChangedListener(mStatusBarExpansionListener);
- mStatusBarStateController.removeCallback(mStatusBarStateListener);
-
mWindowManager.removeView(mView);
+ mView.setOnTouchListener(null);
+ mView.setAnimationViewController(null);
mView = null;
} else {
Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java
index a51b6fd16445..13d31cb87fdc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java
@@ -17,10 +17,10 @@
package com.android.systemui.biometrics;
import android.content.Context;
+import android.graphics.ColorFilter;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -28,24 +28,24 @@ import androidx.annotation.Nullable;
import com.android.systemui.R;
/**
- * Abstract base class for animations that should be drawn when the finger is not touching the
+ * Abstract base class for drawable displayed when the finger is not touching the
* sensor area.
*/
-public abstract class UdfpsAnimation extends Drawable {
- protected abstract void updateColor();
- protected abstract void onDestroy();
-
+public abstract class UdfpsDrawable extends Drawable {
@NonNull protected final Context mContext;
@NonNull protected final Drawable mFingerprintDrawable;
- @Nullable private View mView;
private boolean mIlluminationShowing;
- public UdfpsAnimation(@NonNull Context context) {
+ int mAlpha = 255; // 0 - 255
+ public UdfpsDrawable(@NonNull Context context) {
mContext = context;
mFingerprintDrawable = context.getResources().getDrawable(R.drawable.ic_fingerprint, null);
mFingerprintDrawable.mutate();
}
+ /**
+ * @param sensorRect the rect coordinates for the sensor area
+ */
public void onSensorRectUpdated(@NonNull RectF sensorRect) {
final int margin = (int) sensorRect.height() / 8;
@@ -56,17 +56,17 @@ public abstract class UdfpsAnimation extends Drawable {
updateFingerprintIconBounds(bounds);
}
+ /**
+ * Bounds for the fingerprint icon
+ */
protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
mFingerprintDrawable.setBounds(bounds);
}
@Override
public void setAlpha(int alpha) {
- mFingerprintDrawable.setAlpha(alpha);
- }
-
- public void setAnimationView(UdfpsAnimationView view) {
- mView = view;
+ mAlpha = alpha;
+ mFingerprintDrawable.setAlpha(mAlpha);
}
boolean isIlluminationShowing() {
@@ -77,23 +77,12 @@ public abstract class UdfpsAnimation extends Drawable {
mIlluminationShowing = showing;
}
- /**
- * @return The amount of padding that's needed on each side of the sensor, in pixels.
- */
- public int getPaddingX() {
- return 0;
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
}
- /**
- * @return The amount of padding that's needed on each side of the sensor, in pixels.
- */
- public int getPaddingY() {
+ @Override
+ public int getOpacity() {
return 0;
}
-
- protected void postInvalidateView() {
- if (mView != null) {
- mView.postInvalidate();
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 015a598e972b..d80e085bdc70 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -20,7 +20,6 @@ import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -33,23 +32,23 @@ import androidx.annotation.Nullable;
import com.android.systemui.R;
/**
- * UDFPS animations that should be shown when enrolling.
+ * UDFPS fingerprint drawable that is shown when enrolling
*/
-public class UdfpsAnimationEnroll extends UdfpsAnimation {
+public class UdfpsEnrollDrawable extends UdfpsDrawable {
private static final String TAG = "UdfpsAnimationEnroll";
private static final float SHADOW_RADIUS = 5.f;
- private static final float PROGRESS_BAR_RADIUS = 140.f;
+ static final float PROGRESS_BAR_RADIUS = 140.f;
@NonNull private final Drawable mMovingTargetFpIcon;
@NonNull private final Paint mSensorPaint;
@NonNull private final Paint mBlueFill;
- @NonNull private final Paint mBlueStroke;;
+ @NonNull private final Paint mBlueStroke;
@Nullable private RectF mSensorRect;
@Nullable private UdfpsEnrollHelper mEnrollHelper;
- UdfpsAnimationEnroll(@NonNull Context context) {
+ UdfpsEnrollDrawable(@NonNull Context context) {
super(context);
mSensorPaint = new Paint(0 /* flags */);
@@ -72,20 +71,12 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
mMovingTargetFpIcon = context.getResources().getDrawable(R.drawable.ic_fingerprint, null);
mMovingTargetFpIcon.setTint(Color.WHITE);
mMovingTargetFpIcon.mutate();
- }
-
- void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
- mEnrollHelper = helper;
- }
- @Override
- protected void updateColor() {
mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
}
- @Override
- protected void onDestroy() {
-
+ void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
+ mEnrollHelper = helper;
}
@Override
@@ -98,6 +89,7 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
super.updateFingerprintIconBounds(bounds);
mMovingTargetFpIcon.setBounds(bounds);
+ invalidateSelf();
}
@Override
@@ -117,7 +109,7 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
// Draw moving target
if (mEnrollHelper.isCenterEnrollmentComplete()) {
- mFingerprintDrawable.setAlpha(64);
+ mFingerprintDrawable.setAlpha(mAlpha == 255 ? 64 : mAlpha);
canvas.save();
final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint();
@@ -130,33 +122,16 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
mMovingTargetFpIcon.draw(canvas);
canvas.restore();
} else {
- mFingerprintDrawable.setAlpha(255);
+ mFingerprintDrawable.setAlpha(mAlpha);
}
}
@Override
- public int getPaddingX() {
- return (int) Math.ceil(PROGRESS_BAR_RADIUS);
- }
-
- @Override
- public int getPaddingY() {
- return (int) Math.ceil(PROGRESS_BAR_RADIUS);
- }
-
- @Override
public void setAlpha(int alpha) {
super.setAlpha(alpha);
mSensorPaint.setAlpha(alpha);
- }
-
- @Override
- public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
- }
-
- @Override
- public int getOpacity() {
- return 0;
+ mBlueFill.setAlpha(alpha);
+ mBlueStroke.setAlpha(alpha);
+ invalidateSelf();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index 667b7a7cf0a3..98a703f595d2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -21,6 +21,9 @@ import android.annotation.Nullable;
import android.content.Context;
import android.graphics.PointF;
import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Build;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.TypedValue;
import java.util.ArrayList;
@@ -32,6 +35,10 @@ import java.util.List;
public class UdfpsEnrollHelper {
private static final String TAG = "UdfpsEnrollHelper";
+ private static final String SCALE_OVERRIDE =
+ "com.android.systemui.biometrics.UdfpsEnrollHelper.scale";
+ private static final float SCALE = 0.5f;
+
// Enroll with two center touches before going to guided enrollment
private static final int NUM_CENTER_TOUCHES = 2;
@@ -39,9 +46,10 @@ public class UdfpsEnrollHelper {
void onEnrollmentProgress(int remaining, int totalSteps);
}
+ @NonNull private final Context mContext;
// IUdfpsOverlayController reason
private final int mEnrollReason;
- private final List<PointF> mGuidedEnrollmentPoints;
+ @NonNull private final List<PointF> mGuidedEnrollmentPoints;
private int mTotalSteps = -1;
private int mRemainingSteps = -1;
@@ -53,6 +61,7 @@ public class UdfpsEnrollHelper {
@Nullable Listener mListener;
public UdfpsEnrollHelper(@NonNull Context context, int reason) {
+ mContext = context;
mEnrollReason = reason;
mGuidedEnrollmentPoints = new ArrayList<>();
@@ -100,13 +109,13 @@ public class UdfpsEnrollHelper {
}
- void setListener(@NonNull Listener listener) {
+ void setListener(Listener listener) {
mListener = listener;
// Only notify during setListener if enrollment is already in progress, so the progress
// bar can be updated. If enrollment has not started yet, the progress bar will be empty
// anyway.
- if (mTotalSteps != -1) {
+ if (mListener != null && mTotalSteps != -1) {
mListener.onEnrollmentProgress(mRemainingSteps, mTotalSteps);
}
}
@@ -121,9 +130,15 @@ public class UdfpsEnrollHelper {
@NonNull
PointF getNextGuidedEnrollmentPoint() {
+ float scale = SCALE;
+ if (Build.IS_ENG || Build.IS_USERDEBUG) {
+ scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
+ SCALE_OVERRIDE, SCALE,
+ UserHandle.USER_CURRENT);
+ }
final int index = mLocationsEnrolled - NUM_CENTER_TOUCHES;
final PointF originalPoint = mGuidedEnrollmentPoints
.get(index % mGuidedEnrollmentPoints.size());
- return new PointF(originalPoint.x * 0.5f, originalPoint.y * 0.5f);
+ return new PointF(originalPoint.x * scale, originalPoint.y * scale);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
index 7d0b3e59feb1..7985d95c7c61 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -18,32 +18,36 @@ package com.android.systemui.biometrics;
import android.content.Context;
import android.util.AttributeSet;
+import android.widget.ImageView;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.R;
/**
- * Class that coordinates non-HBM animations during keyguard authentication.
+ * View corresponding with udfps_enroll_view.xml
*/
-public class UdfpsAnimationViewKeyguard extends UdfpsAnimationView {
- @Nullable private UdfpsAnimationKeyguard mAnimation;
+public class UdfpsEnrollView extends UdfpsAnimationView {
+ private final UdfpsEnrollDrawable mFingerprintDrawable;
+ private ImageView mFingerprintView;
- public UdfpsAnimationViewKeyguard(Context context, @Nullable AttributeSet attrs) {
+ public UdfpsEnrollView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
+ mFingerprintDrawable = new UdfpsEnrollDrawable(mContext);
}
- void setStatusBarStateController(@NonNull StatusBarStateController statusBarStateController) {
- if (mAnimation == null) {
- mAnimation = new UdfpsAnimationKeyguard(getContext(), statusBarStateController);
- mAnimation.setAnimationView(this);
- }
+ @Override
+ protected void onFinishInflate() {
+ mFingerprintView = findViewById(R.id.udfps_enroll_animation_fp_view);
+ mFingerprintView.setImageDrawable(mFingerprintDrawable);
}
- @Nullable
@Override
- protected UdfpsAnimation getUdfpsAnimation() {
- return mAnimation;
+ public UdfpsDrawable getDrawable() {
+ return mFingerprintDrawable;
+ }
+
+ void setEnrollHelper(UdfpsEnrollHelper enrollHelper) {
+ mFingerprintDrawable.setEnrollHelper(enrollHelper);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
new file mode 100644
index 000000000000..da8d712ebbdc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -0,0 +1,92 @@
+/*
+ * 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.biometrics;
+
+import android.annotation.NonNull;
+import android.graphics.PointF;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations during enrollment.
+ */
+public class UdfpsEnrollViewController extends UdfpsAnimationViewController<UdfpsEnrollView> {
+ @NonNull private final UdfpsProgressBar mProgressBar;
+ @NonNull private final UdfpsEnrollHelper mEnrollHelper;
+
+ protected UdfpsEnrollViewController(
+ UdfpsEnrollView view,
+ @NonNull UdfpsEnrollHelper enrollHelper,
+ StatusBarStateController statusBarStateController,
+ StatusBar statusBar) {
+ super(view, statusBarStateController, statusBar);
+ mEnrollHelper = enrollHelper;
+ mProgressBar = mView.findViewById(R.id.progress_bar);
+ mView.setEnrollHelper(mEnrollHelper);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+ if (mEnrollHelper.shouldShowProgressBar()) {
+ mProgressBar.setVisibility(View.VISIBLE);
+
+ // Only need enrollment updates if the progress bar is showing :)
+ mEnrollHelper.setListener(mEnrollHelperListener);
+ }
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mEnrollHelper.setListener(null);
+ }
+
+ @NonNull
+ @Override
+ public PointF getTouchTranslation() {
+ if (!mEnrollHelper.isCenterEnrollmentComplete()) {
+ return new PointF(0, 0);
+ } else {
+ return mEnrollHelper.getNextGuidedEnrollmentPoint();
+ }
+ }
+
+ @Override
+ public int getPaddingX() {
+ return (int) Math.ceil(UdfpsEnrollDrawable.PROGRESS_BAR_RADIUS);
+ }
+
+ @Override
+ public int getPaddingY() {
+ return (int) Math.ceil(UdfpsEnrollDrawable.PROGRESS_BAR_RADIUS);
+ }
+
+ private final UdfpsEnrollHelper.Listener mEnrollHelperListener =
+ new UdfpsEnrollHelper.Listener() {
+ @Override
+ public void onEnrollmentProgress(int remaining, int totalSteps) {
+ final int interpolatedProgress = mProgressBar.getMax()
+ * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1);
+
+ mProgressBar.setProgress(interpolatedProgress, true);
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java
index ef7a34000841..09b6fabbdd15 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java
@@ -18,32 +18,19 @@ package com.android.systemui.biometrics;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.ColorFilter;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
/**
- * UDFPS animations that should be shown when authenticating via FingerprintManager, excluding
- * keyguard.
+ * Draws udfps fingerprint if sensor isn't illuminating.
*/
-public class UdfpsAnimationFpmOther extends UdfpsAnimation {
+public class UdfpsFpDrawable extends UdfpsDrawable {
- UdfpsAnimationFpmOther(@NonNull Context context) {
+ UdfpsFpDrawable(@NonNull Context context) {
super(context);
}
@Override
- protected void updateColor() {
-
- }
-
- @Override
- protected void onDestroy() {
-
- }
-
- @Override
public void draw(@NonNull Canvas canvas) {
if (isIlluminationShowing()) {
return;
@@ -51,14 +38,4 @@ public class UdfpsAnimationFpmOther extends UdfpsAnimation {
mFingerprintDrawable.draw(canvas);
}
-
- @Override
- public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
- }
-
- @Override
- public int getOpacity() {
- return 0;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java
index 3d2f5a0fe5cf..85f16068188e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java
@@ -18,25 +18,32 @@ package com.android.systemui.biometrics;
import android.content.Context;
import android.util.AttributeSet;
+import android.widget.ImageView;
import androidx.annotation.Nullable;
+import com.android.systemui.R;
+
/**
- * Class that coordinates non-HBM animations during other usage of FingerprintManager (not
- * including Keyguard).
+ * View corresponding with udfps_fpm_other_view.xml
*/
-public class UdfpsAnimationViewFpmOther extends UdfpsAnimationView {
-
- private final UdfpsAnimationFpmOther mAnimation;
+public class UdfpsFpmOtherView extends UdfpsAnimationView {
+ private final UdfpsFpDrawable mFingerprintDrawable;
+ private ImageView mFingerprintView;
- public UdfpsAnimationViewFpmOther(Context context, @Nullable AttributeSet attrs) {
+ public UdfpsFpmOtherView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
- mAnimation = new UdfpsAnimationFpmOther(context);
+ mFingerprintDrawable = new UdfpsFpDrawable(context);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mFingerprintView = findViewById(R.id.udfps_fpm_other_fp_view);
+ mFingerprintView.setImageDrawable(mFingerprintDrawable);
}
- @Nullable
@Override
- protected UdfpsAnimation getUdfpsAnimation() {
- return mAnimation;
+ UdfpsDrawable getDrawable() {
+ return mFingerprintDrawable;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
new file mode 100644
index 000000000000..587501bd1aa5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
@@ -0,0 +1,35 @@
+/*
+ * 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.biometrics;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations for non keyguard, enrollment or biometric prompt
+ * states.
+ *
+ * Currently only shows the fp drawable.
+ */
+class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmOtherView> {
+ protected UdfpsFpmOtherViewController(
+ UdfpsFpmOtherView view,
+ StatusBarStateController statusBarStateController,
+ StatusBar statusBar) {
+ super(view, statusBarStateController, statusBar);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
index 5f268cfa8fa5..b0c5da09d916 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
@@ -21,28 +21,25 @@ import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorFilter;
import android.util.MathUtils;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
/**
* UDFPS animations that should be shown when authenticating on keyguard.
*/
-public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiver,
- StatusBarStateController.StateListener {
+public class UdfpsKeyguardDrawable extends UdfpsDrawable implements DozeReceiver {
private static final String TAG = "UdfpsAnimationKeyguard";
+ private final int mLockScreenColor;
+ private final int mAmbientDisplayColor;
@NonNull private final Context mContext;
- @NonNull private final StatusBarStateController mStatusBarStateController;
private final int mMaxBurnInOffsetX;
private final int mMaxBurnInOffsetY;
@@ -51,18 +48,19 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
private float mBurnInOffsetX;
private float mBurnInOffsetY;
- UdfpsAnimationKeyguard(@NonNull Context context,
- @NonNull StatusBarStateController statusBarStateController) {
+ UdfpsKeyguardDrawable(@NonNull Context context) {
super(context);
mContext = context;
- mStatusBarStateController = statusBarStateController;
+ // TODO: move burn-in to view
mMaxBurnInOffsetX = context.getResources()
.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
mMaxBurnInOffsetY = context.getResources()
.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
- statusBarStateController.addCallback(this);
+ mLockScreenColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
+ mAmbientDisplayColor = Color.WHITE;
+ updateAodPositionAndColor();
}
private void updateAodPositionAndColor() {
@@ -74,18 +72,14 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
- mMaxBurnInOffsetY,
mInterpolatedDarkAmount);
- updateColor();
- postInvalidateView();
- }
- @Override
- public void dozeTimeTick() {
- updateAodPositionAndColor();
+ mFingerprintDrawable.setTint(ColorUtils.blendARGB(mLockScreenColor,
+ mAmbientDisplayColor, mInterpolatedDarkAmount));
+ invalidateSelf();
}
@Override
- public void onDozeAmountChanged(float linear, float eased) {
- mInterpolatedDarkAmount = eased;
+ public void dozeTimeTick() {
updateAodPositionAndColor();
}
@@ -94,34 +88,11 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
if (isIlluminationShowing()) {
return;
}
-
- canvas.save();
- canvas.translate(mBurnInOffsetX, mBurnInOffsetY);
mFingerprintDrawable.draw(canvas);
- canvas.restore();
}
- @Override
- public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
- }
-
- @Override
- public int getOpacity() {
- return 0;
- }
-
- @Override
- protected void updateColor() {
- final int lockScreenIconColor = Utils.getColorAttrDefaultColor(mContext,
- R.attr.wallpaperTextColor);
- final int ambientDisplayIconColor = Color.WHITE;
- mFingerprintDrawable.setTint(ColorUtils.blendARGB(lockScreenIconColor,
- ambientDisplayIconColor, mInterpolatedDarkAmount));
- }
-
- @Override
- protected void onDestroy() {
- mStatusBarStateController.removeCallback(this);
+ void onDozeAmountChanged(float linear, float eased) {
+ mInterpolatedDarkAmount = eased;
+ updateAodPositionAndColor();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
new file mode 100644
index 000000000000..6a9356034d22
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -0,0 +1,60 @@
+/*
+ * 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.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * View corresponding with udfps_keyguard_view.xml
+ */
+public class UdfpsKeyguardView extends UdfpsAnimationView {
+ private final UdfpsKeyguardDrawable mFingerprintDrawable;
+ private ImageView mFingerprintView;
+
+ public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mFingerprintDrawable = new UdfpsKeyguardDrawable(mContext);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mFingerprintView = findViewById(R.id.udfps_keyguard_animation_fp_view);
+ mFingerprintView.setImageDrawable(mFingerprintDrawable);
+ }
+
+ @Override
+ public UdfpsDrawable getDrawable() {
+ return mFingerprintDrawable;
+ }
+
+ @Override
+ public boolean dozeTimeTick() {
+ // TODO: burnin
+ mFingerprintDrawable.dozeTimeTick();
+ return true;
+ }
+
+ void onDozeAmountChanged(float linear, float eased) {
+ mFingerprintDrawable.onDozeAmountChanged(linear, eased);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
new file mode 100644
index 000000000000..14bb3fee1174
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -0,0 +1,81 @@
+/*
+ * 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.biometrics;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations during keyguard authentication.
+ */
+public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> {
+ private boolean mForceShow;
+
+ protected UdfpsKeyguardViewController(
+ UdfpsKeyguardView view,
+ StatusBarStateController statusBarStateController,
+ StatusBar statusBar) {
+ super(view, statusBarStateController, statusBar);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+ mStatusBarStateController.addCallback(mStateListener);
+ final float dozeAmount = mStatusBarStateController.getDozeAmount();
+ mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mStatusBarStateController.removeCallback(mStateListener);
+ }
+
+ /**
+ * Overrides non-force show logic in shouldPauseAuth to still auth.
+ */
+ private void forceShow(boolean forceShow) {
+ if (mForceShow == forceShow) {
+ return;
+ }
+
+ mForceShow = forceShow;
+ updatePauseAuth();
+ // TODO: animate show/hide background protection
+ }
+
+ /**
+ * Returns true if the fingerprint manager is running but we want to temporarily pause
+ * authentication. On the keyguard, we may want to show udfps when the shade
+ * is expanded, so this can be overridden with the forceShow method.
+ */
+ public boolean shouldPauseAuth() {
+ if (mForceShow) {
+ return false;
+ }
+ return super.shouldPauseAuth();
+ }
+
+ private final StatusBarStateController.StateListener mStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onDozeAmountChanged(float linear, float eased) {
+ mView.onDozeAmountChanged(linear, eased);
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index a52bddc1dcd5..42d0d8438e15 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -16,10 +16,6 @@
package com.android.systemui.biometrics;
-import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER;
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -39,15 +35,12 @@ import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.StatusBar;
/**
* A view containing 1) A SurfaceView for HBM, and 2) A normal drawable view for all other
* animations.
*/
-public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator,
- StatusBarStateController.StateListener, StatusBar.ExpansionChangedListener {
+public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator {
private static final String TAG = "UdfpsView";
private static final int DEBUG_TEXT_SIZE_PX = 32;
@@ -56,7 +49,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@NonNull private final Paint mDebugTextPaint;
@NonNull private UdfpsSurfaceView mHbmSurfaceView;
- @Nullable private UdfpsAnimationView mAnimationView;
+ @Nullable private UdfpsAnimationViewController mAnimationViewController;
// Used to obtain the sensor location.
@NonNull private FingerprintSensorPropertiesInternal mSensorProps;
@@ -64,8 +57,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
private final float mSensorTouchAreaCoefficient;
@Nullable private String mDebugMessage;
private boolean mIlluminationRequested;
- private int mStatusBarState;
- private boolean mNotificationShadeExpanded;
public UdfpsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -108,15 +99,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
mSensorProps = properties;
}
- void setAnimationView(@NonNull UdfpsAnimationView animation) {
- mAnimationView = animation;
- animation.setParent(this);
-
- // TODO: Consider using a ViewStub placeholder to maintain positioning and inflating it
- // after the animation type has been decided.
- addView(animation, 0);
- }
-
@Override
public void setHbmCallback(@Nullable HbmCallback callback) {
mHbmSurfaceView.setHbmCallback(callback);
@@ -124,45 +106,38 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void dozeTimeTick() {
- if (mAnimationView == null) {
- return;
- }
- mAnimationView.dozeTimeTick();
- }
-
- @Override
- public void onStateChanged(int newState) {
- mStatusBarState = newState;
- }
-
- @Override
- public void onExpansionChanged(float expansion, boolean expanded) {
- mNotificationShadeExpanded = expanded;
-
- if (mAnimationView != null) {
- mAnimationView.onExpansionChanged(expansion, expanded);
+ if (mAnimationViewController != null) {
+ mAnimationViewController.dozeTimeTick();
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- mSensorRect.set(0 + mAnimationView.getPaddingX(),
- 0 + mAnimationView.getPaddingY(),
- 2 * mSensorProps.sensorRadius + mAnimationView.getPaddingX(),
- 2 * mSensorProps.sensorRadius + mAnimationView.getPaddingY());
+ int paddingX = mAnimationViewController == null ? 0
+ : mAnimationViewController.getPaddingX();
+ int paddingY = mAnimationViewController == null ? 0
+ : mAnimationViewController.getPaddingY();
+ mSensorRect.set(
+ paddingX,
+ paddingY,
+ 2 * mSensorProps.sensorRadius + paddingX,
+ 2 * mSensorProps.sensorRadius + paddingY);
mHbmSurfaceView.onSensorRectUpdated(new RectF(mSensorRect));
- mAnimationView.onSensorRectUpdated(new RectF(mSensorRect));
+ if (mAnimationViewController != null) {
+ mAnimationViewController.onSensorRectUpdated(new RectF(mSensorRect));
+ }
+ }
+
+ void setAnimationViewController(UdfpsAnimationViewController animationViewController) {
+ mAnimationViewController = animationViewController;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Log.v(TAG, "onAttachedToWindow");
-
- // Retrieve the colors each time, since it depends on day/night mode
- mAnimationView.updateColor();
}
@Override
@@ -188,7 +163,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
boolean isWithinSensorArea(float x, float y) {
// The X and Y coordinates of the sensor's center.
- final PointF translation = mAnimationView.getTouchTranslation();
+ final PointF translation = mAnimationViewController == null
+ ? new PointF(0, 0)
+ : mAnimationViewController.getTouchTranslation();
final float cx = mSensorRect.centerX() + translation.x;
final float cy = mSensorRect.centerY() + translation.y;
// Radii along the X and Y axes.
@@ -199,18 +176,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
&& x < (cx + rx * mSensorTouchAreaCoefficient)
&& y > (cy - ry * mSensorTouchAreaCoefficient)
&& y < (cy + ry * mSensorTouchAreaCoefficient)
- && !shouldPauseAuth();
- }
-
- /**
- * States where UDFPS should temporarily not be authenticating. Instead of completely stopping
- * authentication which would cause the UDFPS icons to abruptly disappear, do it here by not
- * sending onFingerDown and smoothly animating away.
- */
- boolean shouldPauseAuth() {
- return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD)
- || mStatusBarState == SHADE_LOCKED
- || mStatusBarState == FULLSCREEN_USER_SWITCHER;
+ && !mAnimationViewController.shouldPauseAuth();
}
boolean isIlluminationRequested() {
@@ -223,7 +189,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void startIllumination(@Nullable Runnable onIlluminatedRunnable) {
mIlluminationRequested = true;
- mAnimationView.onIlluminationStarting();
+ if (mAnimationViewController != null) {
+ mAnimationViewController.onIlluminationStarting();
+ }
mHbmSurfaceView.setVisibility(View.VISIBLE);
mHbmSurfaceView.startIllumination(onIlluminatedRunnable);
}
@@ -231,7 +199,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void stopIllumination() {
mIlluminationRequested = false;
- mAnimationView.onIlluminationStopped();
+ if (mAnimationViewController != null) {
+ mAnimationViewController.onIlluminationStopped();
+ }
mHbmSurfaceView.setVisibility(View.INVISIBLE);
mHbmSurfaceView.stopIllumination();
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index 03c184336364..f87ea7c61ca8 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -214,9 +214,7 @@ class PrivacyDialogController(
private fun filterType(type: PrivacyType?): PrivacyType? {
return type?.let {
- if (privacyItemController.allIndicatorsAvailable) {
- it
- } else if ((it == PrivacyType.TYPE_CAMERA || it == PrivacyType.TYPE_MICROPHONE) &&
+ if ((it == PrivacyType.TYPE_CAMERA || it == PrivacyType.TYPE_MICROPHONE) &&
privacyItemController.micCameraAvailable) {
it
} else if (it == PrivacyType.TYPE_LOCATION && privacyItemController.locationAvailable) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 1e0451601e50..03d6154706eb 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -68,8 +68,6 @@ class PrivacyItemController @Inject constructor(
addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
}
const val TAG = "PrivacyItemController"
- private const val ALL_INDICATORS =
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
private const val DEFAULT_ALL_INDICATORS = false
@@ -83,11 +81,6 @@ class PrivacyItemController @Inject constructor(
@Synchronized get() = field.toList() // Returns a shallow copy of the list
@Synchronized set
- private fun isAllIndicatorsEnabled(): Boolean {
- return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- ALL_INDICATORS, DEFAULT_ALL_INDICATORS)
- }
-
private fun isMicCameraEnabled(): Boolean {
return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
MIC_CAMERA, DEFAULT_MIC_CAMERA)
@@ -120,34 +113,29 @@ class PrivacyItemController @Inject constructor(
uiExecutor.execute(notifyChanges)
}
- var allIndicatorsAvailable = isAllIndicatorsEnabled()
- private set
var micCameraAvailable = isMicCameraEnabled()
private set
var locationAvailable = isLocationEnabled()
+ var allIndicatorsAvailable = micCameraAvailable && locationAvailable
+
private val devicePropertiesChangedListener =
object : DeviceConfig.OnPropertiesChangedListener {
override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
- (properties.keyset.contains(ALL_INDICATORS) ||
- properties.keyset.contains(MIC_CAMERA) ||
+ (properties.keyset.contains(MIC_CAMERA) ||
properties.keyset.contains(LOCATION))) {
// Running on the ui executor so can iterate on callbacks
- if (properties.keyset.contains(ALL_INDICATORS)) {
- allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS,
- DEFAULT_ALL_INDICATORS)
- callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) }
- }
-
if (properties.keyset.contains(MIC_CAMERA)) {
micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
+ allIndicatorsAvailable = micCameraAvailable && locationAvailable
callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
}
if (properties.keyset.contains(LOCATION)) {
locationAvailable = properties.getBoolean(LOCATION, DEFAULT_LOCATION)
+ allIndicatorsAvailable = micCameraAvailable && locationAvailable
callbacks.forEach { it.get()?.onFlagLocationChanged(locationAvailable) }
}
internalUiExecutor.updateListeningState()
@@ -163,8 +151,7 @@ class PrivacyItemController @Inject constructor(
active: Boolean
) {
// Check if we care about this code right now
- if (!allIndicatorsAvailable &&
- (code in OPS_LOCATION && !locationAvailable)) {
+ if (code in OPS_LOCATION && !locationAvailable) {
return
}
val userId = UserHandle.getUserId(uid)
@@ -231,7 +218,7 @@ class PrivacyItemController @Inject constructor(
*/
private fun setListeningState() {
val listen = !callbacks.isEmpty() and
- (allIndicatorsAvailable || micCameraAvailable || locationAvailable)
+ (micCameraAvailable || locationAvailable)
if (listening == listen) return
listening = listen
if (listening) {
@@ -338,7 +325,7 @@ class PrivacyItemController @Inject constructor(
AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
else -> return null
}
- if (type == PrivacyType.TYPE_LOCATION && (!allIndicatorsAvailable && !locationAvailable)) {
+ if (type == PrivacyType.TYPE_LOCATION && !locationAvailable) {
return null
}
val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
index 0fa7b59d0e54..5ab7bd88e49b 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
+++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
@@ -88,7 +88,6 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
private boolean mViewAndWindowAdded;
private ObjectAnimator mAnimator;
- private boolean mAllIndicatorsFlagEnabled;
private boolean mMicCameraIndicatorFlagEnabled;
private boolean mLocationIndicatorEnabled;
private List<PrivacyItem> mPrivacyItems;
@@ -111,12 +110,10 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
mIconMarginStart = Math.round(res.getDimension(R.dimen.privacy_chip_icon_margin));
mIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_icon_size);
- mAllIndicatorsFlagEnabled = privacyItemController.getAllIndicatorsAvailable();
mMicCameraIndicatorFlagEnabled = privacyItemController.getMicCameraAvailable();
mLocationIndicatorEnabled = privacyItemController.getLocationAvailable();
if (DEBUG) {
- Log.d(TAG, "allIndicators: " + mAllIndicatorsFlagEnabled);
Log.d(TAG, "micCameraIndicators: " + mMicCameraIndicatorFlagEnabled);
Log.d(TAG, "locationIndicators: " + mLocationIndicatorEnabled);
}
@@ -135,12 +132,6 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
}
@Override
- public void onFlagAllChanged(boolean flag) {
- if (DEBUG) Log.d(TAG, "all indicators enabled: " + flag);
- mAllIndicatorsFlagEnabled = flag;
- }
-
- @Override
public void onFlagMicCameraChanged(boolean flag) {
if (DEBUG) Log.d(TAG, "mic/camera indicators enabled: " + flag);
mMicCameraIndicatorFlagEnabled = flag;
@@ -155,8 +146,8 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl
private void updateUI() {
if (DEBUG) Log.d(TAG, mPrivacyItems.size() + " privacy items");
- if ((mMicCameraIndicatorFlagEnabled || mAllIndicatorsFlagEnabled
- || mLocationIndicatorEnabled) && !mPrivacyItems.isEmpty()) {
+ if ((mMicCameraIndicatorFlagEnabled || mLocationIndicatorEnabled)
+ && !mPrivacyItems.isEmpty()) {
if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) {
showIndicator();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index fe76668ab68b..b8823e148e68 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,6 +14,8 @@
package com.android.systemui.qs;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
import android.util.Log;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
@@ -35,6 +37,7 @@ import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -77,6 +80,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
// This animates fading of SecurityFooter and media divider
private TouchAnimator mAllPagesDelayedAnimator;
private TouchAnimator mBrightnessAnimator;
+ private HeightExpansionAnimator mQQSTileHeightAnimator;
+ private HeightExpansionAnimator mOtherTilesExpandAnimator;
+
private boolean mNeedsAnimatorUpdate = false;
private boolean mOnKeyguard;
@@ -161,7 +167,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
@Override
public void onViewAttachedToWindow(View v) {
mTunerService.addTunable(this, ALLOW_FANCY_ANIMATION,
- MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES);
+ MOVE_FULL_ROWS);
}
@Override
@@ -179,9 +185,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
}
} else if (MOVE_FULL_ROWS.equals(key)) {
mFullRows = TunerService.parseIntegerSwitch(newValue, true);
- } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
- mNumQuickTiles = QuickQSPanel.parseNumTiles(newValue);
- clearAnimationState();
}
updateAnimators();
}
@@ -209,6 +212,10 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
clearAnimationState();
mAllViews.clear();
mQuickQsViews.clear();
+ mQQSTileHeightAnimator = null;
+ mOtherTilesExpandAnimator = null;
+
+ mNumQuickTiles = mQuickQsPanel.getNumQuickTiles();
QSTileLayout tileLayout = mQsPanelController.getTileLayout();
mAllViews.add((View) tileLayout);
@@ -218,6 +225,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
+ mQs.getHeader().getPaddingBottom();
firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0);
+ boolean qsSideLabelsEnabled = mFeatureFlags.isQSLabelsEnabled();
+ int qqsTileHeight = 0;
+
for (QSTile tile : tiles) {
QSTileView tileView = mQsPanelController.getTileView(tile);
if (tileView == null) {
@@ -237,22 +247,47 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view);
getRelativePosition(loc2, tileIcon, view);
final int xDiff = loc2[0] - loc1[0];
- final int yDiff = loc2[1] - loc1[1];
-
+ int yDiff = loc2[1] - loc1[1];
if (count < tileLayout.getNumVisibleTiles()) {
+ getRelativePosition(loc1, quickTileView, view);
+ getRelativePosition(loc2, tileView, view);
+ int yOffset = qsSideLabelsEnabled ? loc2[1] - loc1[1] : 0;
// Move the quick tile right from its location to the new one.
- translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
- translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
-
- // Counteract the parent translation on the tile. So we have a static base to
- // animate the label position off from.
- //firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
+ View v = qsSideLabelsEnabled ? quickTileView.getIcon() : quickTileView;
+ translationXBuilder.addFloat(v, "translationX", 0, xDiff);
+ translationYBuilder.addFloat(v, "translationY", 0, yDiff - yOffset);
+ mAllViews.add(v);
// Move the real tile from the quick tile position to its final
// location.
- translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
- translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
+ v = qsSideLabelsEnabled ? tileIcon : tileView;
+ translationXBuilder.addFloat(v, "translationX", -xDiff, 0);
+ translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0);
+
+ if (qsSideLabelsEnabled) {
+ translationYBuilder.addFloat(quickTileView, "translationY", 0, yOffset);
+ translationYBuilder.addFloat(tileView, "translationY", -yOffset, 0);
+
+ if (mQQSTileHeightAnimator == null) {
+ mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
+ quickTileView.getHeight(), tileView.getHeight());
+ qqsTileHeight = quickTileView.getHeight();
+ }
+
+ mQQSTileHeightAnimator.addView(quickTileView);
+ View qqsLabelContainer = quickTileView.getLabelContainer();
+ View qsLabelContainer = tileView.getLabelContainer();
+
+ getRelativePosition(loc1, qqsLabelContainer, view);
+ getRelativePosition(loc2, qsLabelContainer, view);
+ yDiff = loc2[1] - loc1[1] - yOffset;
+
+ translationYBuilder.addFloat(qqsLabelContainer, "translationY", 0, yDiff);
+ translationYBuilder.addFloat(qsLabelContainer, "translationY", -yDiff, 0);
+ mAllViews.add(qqsLabelContainer);
+ mAllViews.add(qsLabelContainer);
+ }
} else { // These tiles disappear when expanding
firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
@@ -265,7 +300,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
translationX);
}
- if (mFeatureFlags.isQSLabelsEnabled()) {
+ if (qsSideLabelsEnabled) {
mQuickQsViews.add(tileView);
} else {
mQuickQsViews.add(tileView.getIconWithBackground());
@@ -278,9 +313,29 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
mAllViews.add(tileIcon);
} else {
- firstPageBuilder.addFloat(tileView, "alpha", 0, 1);
- firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
+ if (!qsSideLabelsEnabled) {
+ firstPageBuilder.addFloat(tileView, "alpha", 0, 1);
+ firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
+ } else {
+ // Pretend there's a corresponding QQS tile (for the position) that we are
+ // expanding from.
+ SideLabelTileLayout qqsLayout =
+ (SideLabelTileLayout) mQuickQsPanel.getTileLayout();
+ getRelativePosition(loc1, qqsLayout, view);
+ getRelativePosition(loc2, tileView, view);
+ int diff = loc2[1] - (loc1[1] + qqsLayout.getPhantomTopPosition(count));
+ translationYBuilder.addFloat(tileView, "translationY", -diff, 0);
+ if (mOtherTilesExpandAnimator == null) {
+ mOtherTilesExpandAnimator =
+ new HeightExpansionAnimator(
+ this, qqsTileHeight, tileView.getHeight());
+ }
+ mOtherTilesExpandAnimator.addView(tileView);
+ tileView.setClipChildren(true);
+ tileView.setClipToPadding(true);
+ }
}
+
mAllViews.add(tileView);
count++;
}
@@ -303,7 +358,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
.build();
// Fade in the tiles/labels as we reach the final position.
Builder builder = new Builder()
- .setStartDelay(EXPANDED_TILE_DELAY)
+ .setStartDelay(qsSideLabelsEnabled ? 0 : EXPANDED_TILE_DELAY)
.addFloat(tileLayout, "alpha", 0, 1);
mFirstPageDelayedAnimator = builder.build();
@@ -331,6 +386,12 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
mTranslationXAnimator = translationXBuilder.build();
mTranslationYAnimator = translationYBuilder.build();
+ if (mQQSTileHeightAnimator != null) {
+ mQQSTileHeightAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+ }
+ if (mOtherTilesExpandAnimator != null) {
+ mOtherTilesExpandAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+ }
}
mNonfirstPageAnimator = new TouchAnimator.Builder()
.addFloat(mQuickQsPanel, "alpha", 1, 0)
@@ -404,6 +465,12 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
if (mBrightnessAnimator != null) {
mBrightnessAnimator.setPosition(position);
}
+ if (mQQSTileHeightAnimator != null) {
+ mQQSTileHeightAnimator.setPosition(position);
+ }
+ if (mOtherTilesExpandAnimator != null) {
+ mOtherTilesExpandAnimator.setPosition(position);
+ }
} else {
mNonfirstPageAnimator.setPosition(position);
mNonfirstPageDelayedAnimator.setPosition(position);
@@ -446,6 +513,16 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
v.setAlpha(1);
v.setTranslationX(0);
v.setTranslationY(0);
+ if (v instanceof SideLabelTileLayout) {
+ ((SideLabelTileLayout) v).setClipChildren(false);
+ ((SideLabelTileLayout) v).setClipToPadding(false);
+ }
+ }
+ if (mQQSTileHeightAnimator != null) {
+ mQQSTileHeightAnimator.resetViewsHeights();
+ }
+ if (mOtherTilesExpandAnimator != null) {
+ mOtherTilesExpandAnimator.resetViewsHeights();
}
final int N2 = mQuickQsViews.size();
for (int i = 0; i < N2; i++) {
@@ -483,4 +560,61 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
updateAnimators();
setCurrentPosition();
};
+
+ static class HeightExpansionAnimator {
+ private final List<View> mViews = new ArrayList<>();
+ private final ValueAnimator mAnimator;
+ private final TouchAnimator.Listener mListener;
+
+ private final ValueAnimator.AnimatorUpdateListener mUpdateListener =
+ new ValueAnimator.AnimatorUpdateListener() {
+ float mLastT = -1;
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ float t = valueAnimator.getAnimatedFraction();
+ if (t == 0f) {
+ mListener.onAnimationAtStart();
+ } else if (t == 1f) {
+ mListener.onAnimationAtEnd();
+ } else if (mLastT <= 0 || mLastT == 1) {
+ mListener.onAnimationStarted();
+ }
+ mLastT = t;
+ final int viewCount = mViews.size();
+ int height = (Integer) valueAnimator.getAnimatedValue();
+ for (int i = 0; i < viewCount; i++) {
+ View v = mViews.get(i);
+ v.setBottom(v.getTop() + height);
+ }
+ }
+ };
+
+ HeightExpansionAnimator(TouchAnimator.Listener listener, int startHeight, int endHeight) {
+ mListener = listener;
+ mAnimator = ValueAnimator.ofInt(startHeight, endHeight);
+ mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+ mAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ mAnimator.addUpdateListener(mUpdateListener);
+ }
+
+ void addView(View v) {
+ mViews.add(v);
+ }
+
+ void setInterpolator(TimeInterpolator interpolator) {
+ mAnimator.setInterpolator(interpolator);
+ }
+
+ void setPosition(float position) {
+ mAnimator.setCurrentFraction(position);
+ }
+
+ void resetViewsHeights() {
+ final int viewsCount = mViews.size();
+ for (int i = 0; i < viewsCount; i++) {
+ View v = mViews.get(i);
+ v.setBottom(v.getTop() + v.getMeasuredHeight());
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index ed308ae1ac6c..7c9f0b082d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -430,6 +430,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
mQSPanelController.setRevealExpansion(expansion);
mQSPanelController.getTileLayout().setExpansion(expansion);
+ mQuickQSPanelController.getTileLayout().setExpansion(expansion);
mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
if (fullyCollapsed) {
mQSPanelScrollView.setScrollY(0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index ff9b9120c6e1..c794a2121cc2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -315,7 +315,7 @@ public class QSPanel extends LinearLayout implements Tunable {
int tileBg = getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
mFooterMarginStartHorizontal = getResources().getDimensionPixelSize(
R.dimen.qs_footer_horizontal_margin);
- mVisualTilePadding = (int) ((tileSize - tileBg) / 2.0f);
+ mVisualTilePadding = mSideLabels ? 0 : (int) ((tileSize - tileBg) / 2.0f);
updatePadding();
updatePageIndicator();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index f51d7ef381ee..e7828c366b64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -348,19 +348,20 @@ public class QuickQSPanel extends QSPanel {
}
static class QQSSideLabelTileLayout extends SideLabelTileLayout {
+
QQSSideLabelTileLayout(Context context) {
super(context, null);
setClipChildren(false);
setClipToPadding(false);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
- lp.gravity = Gravity.CENTER_HORIZONTAL;
setLayoutParams(lp);
setMaxColumns(4);
}
@Override
public boolean updateResources() {
+ mCellHeightResId = R.dimen.qs_quick_tile_size;
boolean b = super.updateResources();
mMaxAllowedRows = 2;
return b;
@@ -379,5 +380,38 @@ public class QuickQSPanel extends QSPanel {
updateMaxRows(10000, mRecords.size());
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
+
+ @Override
+ public void setListening(boolean listening, UiEventLogger uiEventLogger) {
+ boolean startedListening = !mListening && listening;
+ super.setListening(listening, uiEventLogger);
+ if (startedListening) {
+ // getNumVisibleTiles() <= mRecords.size()
+ for (int i = 0; i < getNumVisibleTiles(); i++) {
+ QSTile tile = mRecords.get(i).tile;
+ uiEventLogger.logWithInstanceId(QSEvent.QQS_TILE_VISIBLE, 0,
+ tile.getMetricsSpec(), tile.getInstanceId());
+ }
+ }
+ }
+
+ @Override
+ public void setExpansion(float expansion) {
+ if (expansion > 0f && expansion < 1f) {
+ return;
+ }
+ boolean selected = expansion == 0f;
+ // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this
+ // point we want them to be selected so the tiles will marquee (but not at other points
+ // of expansion.
+ // We set it as not important while we change this, so setting each tile as selected
+ // will not cause them to announce themselves until the user has actually selected the
+ // item.
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ for (int i = 0; i < getChildCount(); i++) {
+ getChildAt(i).setSelected(selected);
+ }
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 671f8f7dd2d1..fee56b984ecc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -99,7 +99,7 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
break;
}
}
- super.setTiles(tiles, !mQSLabelFlag);
+ super.setTiles(tiles, /* collapsedView */ true);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index e3c39aa77402..eedcdab68b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -97,7 +97,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
private boolean mListening;
private AlarmClockInfo mNextAlarm;
- private boolean mAllIndicatorsEnabled;
private boolean mMicCameraIndicatorsEnabled;
private boolean mLocationIndicatorsEnabled;
private boolean mPrivacyChipLogged;
@@ -151,14 +150,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
}
@Override
- public void onFlagAllChanged(boolean flag) {
- if (mAllIndicatorsEnabled != flag) {
- mAllIndicatorsEnabled = flag;
- update();
- }
- }
-
- @Override
public void onFlagMicCameraChanged(boolean flag) {
if (mMicCameraIndicatorsEnabled != flag) {
mMicCameraIndicatorsEnabled = flag;
@@ -270,7 +261,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
mRingerContainer.setOnClickListener(mOnClickListener);
mPrivacyChip.setOnClickListener(mOnClickListener);
- mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
@@ -321,7 +311,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
mNextAlarmController.addCallback(mNextAlarmChangeCallback);
mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
// Get the most up to date info
- mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
mPrivacyItemController.addCallback(mPICCallback);
@@ -353,13 +342,13 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
private List<String> getIgnoredIconSlots() {
ArrayList<String> ignored = new ArrayList<>();
if (getChipEnabled()) {
- if (mAllIndicatorsEnabled || mMicCameraIndicatorsEnabled) {
+ if (mMicCameraIndicatorsEnabled) {
ignored.add(mView.getResources().getString(
com.android.internal.R.string.status_bar_camera));
ignored.add(mView.getResources().getString(
com.android.internal.R.string.status_bar_microphone));
}
- if (mAllIndicatorsEnabled || mLocationIndicatorsEnabled) {
+ if (mLocationIndicatorsEnabled) {
ignored.add(mView.getResources().getString(
com.android.internal.R.string.status_bar_location));
}
@@ -368,7 +357,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
}
private boolean getChipEnabled() {
- return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled || mAllIndicatorsEnabled;
+ return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
}
private boolean isZenOverridingRinger() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 52f111e7ab48..0ebadfd2fa11 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -33,4 +33,15 @@ open class SideLabelTileLayout(
override fun isFull(): Boolean {
return mRecords.size >= maxTiles()
}
+
+ /**
+ * Return the position from the top of the layout of the tile with this index.
+ *
+ * This will return a position even for indices that go beyond [maxTiles], continuing the rows
+ * beyond that.
+ */
+ fun getPhantomTopPosition(index: Int): Int {
+ val row = index / mColumns
+ return getRowTop(row)
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index c1ce4a577dda..9e0aa5ad78b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -25,6 +25,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
protected int mColumns;
protected int mCellWidth;
+ protected int mCellHeightResId = R.dimen.qs_tile_height;
protected int mCellHeight;
protected int mMaxCellHeight;
protected int mCellMarginHorizontal;
@@ -118,7 +119,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
final Resources res = mContext.getResources();
mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
updateColumns();
- mMaxCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
+ mMaxCellHeight = mContext.getResources().getDimensionPixelSize(mCellHeightResId);
mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
@@ -235,7 +236,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
layoutTileRecords(mRecords.size());
}
- private int getRowTop(int row) {
+ protected int getRowTop(int row) {
return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
index f8c0dd4239d9..7977b4904a7d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
@@ -14,7 +14,7 @@ import com.android.systemui.qs.tileimpl.QSTileViewHorizontal
class CustomizeTileViewHorizontal(
context: Context,
icon: QSIconView
-) : QSTileViewHorizontal(context, icon),
+) : QSTileViewHorizontal(context, icon, collapsed = false),
TileAdapter.CustomizeView {
private var showAppLabel = false
@@ -27,6 +27,7 @@ class CustomizeTileViewHorizontal(
override fun handleStateChanged(state: QSTile.State) {
super.handleStateChanged(state)
+ mShowRippleEffect = false
mSecondLine.visibility = if (showAppLabel) View.VISIBLE else View.GONE
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 9e5fe732cd0c..29b9e64d1659 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -248,10 +248,10 @@ public class QSFactoryImpl implements QSFactory {
public QSTileView createTileView(QSTile tile, boolean collapsedView) {
Context context = new ContextThemeWrapper(mQsHostLazy.get().getContext(), R.style.qs_theme);
QSIconView icon = tile.createTileView(context);
- if (collapsedView) {
+ if (mSideLabels) {
+ return new QSTileViewHorizontal(context, icon, collapsedView);
+ } else if (collapsedView) {
return new QSTileBaseView(context, icon, collapsedView);
- } else if (mSideLabels) {
- return new QSTileViewHorizontal(context, icon);
} else {
return new com.android.systemui.qs.tileimpl.QSTileView(context, icon);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 33ca7d6bafd8..a45b131902c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -58,13 +58,13 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
private static final int ICON_MASK_ID = com.android.internal.R.string.config_icon_mask;
protected final Handler mHandler = new H();
private final int[] mLocInScreen = new int[2];
- private final FrameLayout mIconFrame;
+ protected final FrameLayout mIconFrame;
protected QSIconView mIcon;
protected RippleDrawable mRipple;
protected Drawable mTileBackground;
private String mAccessibilityClass;
private boolean mTileState;
- private boolean mCollapsedView;
+ protected boolean mCollapsedView;
protected boolean mShowRippleEffect = true;
private float mStrokeWidthActive;
private float mStrokeWidthInactive;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index b59326ae56d5..c7ed89ba49b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -26,6 +26,8 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.Utils;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
@@ -116,7 +118,8 @@ public class QSTileView extends QSTileBaseView {
}
}
- private boolean shouldLabelBeSingleLine() {
+ protected boolean shouldLabelBeSingleLine() {
+ if (mCollapsedView) return true;
if (mLabel.getLineCount() > mMaxLabelLines) {
return true;
} else if (!TextUtils.isEmpty(mSecondLine.getText())
@@ -138,14 +141,14 @@ public class QSTileView extends QSTileBaseView {
} else {
labelColor = mColorLabelUnavailable;
}
- mLabel.setTextColor(labelColor);
+ changeLabelColor(labelColor);
mState = state.state;
mLabel.setText(state.label);
}
if (!Objects.equals(mSecondLine.getText(), state.secondaryLabel)) {
mSecondLine.setText(state.secondaryLabel);
- mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE
- : View.VISIBLE);
+ mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) || mCollapsedView
+ ? View.GONE : View.VISIBLE);
}
boolean dualTarget = mDualTargetAllowed && state.dualTarget;
handleExpand(dualTarget);
@@ -160,6 +163,10 @@ public class QSTileView extends QSTileBaseView {
mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
}
+ protected void changeLabelColor(ColorStateList color) {
+ mLabel.setTextColor(color);
+ }
+
protected void handleExpand(boolean dualTarget) {
mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
@@ -178,4 +185,10 @@ public class QSTileView extends QSTileBaseView {
public TextView getAppLabel() {
return mSecondLine;
}
+
+ @Nullable
+ @Override
+ public View getLabelContainer() {
+ return mLabelContainer;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index 328c2c353a29..32285cf797e4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -21,8 +21,7 @@ import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.drawable.Drawable
-import android.graphics.drawable.ShapeDrawable
-import android.graphics.drawable.shapes.RoundRectShape
+import android.graphics.drawable.RippleDrawable
import android.service.quicksettings.Tile.STATE_ACTIVE
import android.view.Gravity
import android.widget.LinearLayout
@@ -32,25 +31,31 @@ import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState
-// Placeholder
-private const val CORNER_RADIUS = 40f
-private val RADII = (1..8).map { CORNER_RADIUS }.toFloatArray()
-
open class QSTileViewHorizontal(
context: Context,
- icon: QSIconView
-) : QSTileView(context, icon, false) {
+ icon: QSIconView,
+ collapsed: Boolean
+) : QSTileView(context, icon, collapsed) {
- protected var backgroundDrawable: ShapeDrawable? = null
+ protected var colorBackgroundDrawable: Drawable? = null
private var paintColor = Color.WHITE
private var paintAnimator: ValueAnimator? = null
+ private var labelAnimator: ValueAnimator? = null
init {
orientation = HORIZONTAL
+ gravity = Gravity.CENTER_VERTICAL or Gravity.START
mDualTargetAllowed = false
+ val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
+ setPadding(padding, paddingTop, padding, paddingBottom)
+
mBg.setImageDrawable(null)
+ mIconFrame.removeAllViews()
+ removeView(mIconFrame)
+ val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
+ addView(mIcon, 0, LayoutParams(iconSize, iconSize))
+
mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
- mMaxLabelLines = 3
}
override fun createLabel() {
@@ -61,65 +66,112 @@ open class QSTileViewHorizontal(
removeRule(RelativeLayout.ALIGN_PARENT_TOP)
}
}
+ mLabelContainer.setPadding(0, 0, 0, 0)
+ (mLabelContainer.layoutParams as MarginLayoutParams).apply {
+ marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
+ }
mLabel.gravity = Gravity.START
mLabel.textDirection = TEXT_DIRECTION_LOCALE
mSecondLine.gravity = Gravity.START
mSecondLine.textDirection = TEXT_DIRECTION_LOCALE
- val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
- mLabelContainer.setPaddingRelative(0, padding, padding, padding)
+
(mLabelContainer.layoutParams as LayoutParams).gravity =
Gravity.CENTER_VERTICAL or Gravity.START
+ if (mCollapsedView) {
+ mSecondLine.visibility = GONE
+ }
+ }
+
+ override fun shouldLabelBeSingleLine(): Boolean {
+ return true
}
override fun updateRippleSize() {
}
override fun newTileBackground(): Drawable? {
- backgroundDrawable = ShapeDrawable(RoundRectShape(RADII, null, null))
- return backgroundDrawable
+ val ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
+ colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background)
+ return ripple
}
override fun setClickable(clickable: Boolean) {
super.setClickable(clickable)
- background = mTileBackground
+ background = if (clickable && mShowRippleEffect) {
+ mTileBackground
+ } else {
+ colorBackgroundDrawable
+ }
}
override fun handleStateChanged(state: QSTile.State) {
super.handleStateChanged(state)
- mSecondLine.setTextColor(mLabel.textColors)
mLabelContainer.background = null
val allowAnimations = animationsEnabled() && paintColor != Color.WHITE
val newColor = getCircleColor(state.state)
if (allowAnimations) {
- animateToNewState(newColor)
+ animateBackground(newColor)
} else {
if (newColor != paintColor) {
- clearAnimator()
- backgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))
+ clearBackgroundAnimator()
+ colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))?.also {
+ paintColor = newColor
+ }
paintColor = newColor
}
}
}
- private fun animateToNewState(newColor: Int) {
- if (newColor != paintColor) {
- clearAnimator()
- paintAnimator = ValueAnimator.ofArgb(paintColor, newColor)
+ private fun animateBackground(newBackgroundColor: Int) {
+ if (newBackgroundColor != paintColor) {
+ clearBackgroundAnimator()
+ paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor)
.setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
addUpdateListener { animation: ValueAnimator ->
val c = animation.animatedValue as Int
- backgroundDrawable?.setTintList(ColorStateList.valueOf(c))
- paintColor = c
+ colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(c))?.also {
+ paintColor = c
+ }
}
start()
}
}
}
- private fun clearAnimator() {
+ override fun changeLabelColor(color: ColorStateList) {
+ val allowAnimations = animationsEnabled()
+ val currentColor = mLabel.textColors.defaultColor
+ if (currentColor != color.defaultColor) {
+ clearLabelAnimator()
+ if (allowAnimations) {
+ labelAnimator = ValueAnimator.ofArgb(currentColor, color.defaultColor)
+ .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
+ addUpdateListener {
+ setLabelsColor(ColorStateList.valueOf(it.animatedValue as Int))
+ }
+ start()
+ }
+ } else {
+ setLabelsColor(color)
+ }
+ }
+ }
+
+ private fun setLabelsColor(color: ColorStateList) {
+ mLabel.setTextColor(color)
+ if (!mCollapsedView) {
+ mSecondLine.setTextColor(color)
+ }
+ }
+
+ private fun clearBackgroundAnimator() {
paintAnimator?.cancel()?.also { paintAnimator = null }
}
+ private fun clearLabelAnimator() {
+ labelAnimator?.cancel()?.also { labelAnimator = null }
+ }
+
override fun handleExpand(dualTarget: Boolean) {}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 2b194ba15816..f65ae0c39331 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -337,6 +337,7 @@ public class NotificationChildrenContainer extends ViewGroup {
} else {
header.reapply(getContext(), mNotificationHeader);
}
+ mNotificationHeaderWrapper.setExpanded(mChildrenExpanded);
mNotificationHeaderWrapper.onContentUpdated(mContainingNotification);
if (mNotificationHeaderWrapper instanceof NotificationHeaderViewWrapper) {
NotificationHeaderViewWrapper headerWrapper =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index d53724159244..f1405dea1294 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -675,8 +675,7 @@ public class PhoneStatusBarPolicy
mIconController.setIconVisibility(mSlotCamera, showCamera);
mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
- if (mPrivacyItemController.getAllIndicatorsAvailable()
- || mPrivacyItemController.getLocationAvailable()) {
+ if (mPrivacyItemController.getLocationAvailable()) {
mIconController.setIconVisibility(mSlotLocation, showLocation);
}
mPrivacyLogger.logStatusBarIconsVisible(showCamera, showMicrophone, showLocation);
@@ -684,8 +683,7 @@ public class PhoneStatusBarPolicy
@Override
public void onLocationActiveChanged(boolean active) {
- if (!mPrivacyItemController.getAllIndicatorsAvailable()
- && !mPrivacyItemController.getLocationAvailable()) {
+ if (!mPrivacyItemController.getLocationAvailable()) {
updateLocationFromController();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 07686181649d..d6f4958942dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -246,20 +246,4 @@ public class UdfpsControllerTest extends SysuiTestCase {
// THEN the illumination is hidden
verify(mUdfpsView).stopIllumination();
}
-
- @Test
- public void registersAndUnregistersViewForCallbacks() throws RemoteException {
- mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
- verify(mStatusBarStateController).addCallback(mUdfpsController.mStatusBarStateListener);
- verify(mStatusBar).addExpansionChangedListener(
- mUdfpsController.mStatusBarExpansionListener);
-
- mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
- mFgExecutor.runAllReady();
- verify(mStatusBarStateController).removeCallback(mUdfpsController.mStatusBarStateListener);
- verify(mStatusBar).removeExpansionChangedListener(
- mUdfpsController.mStatusBarExpansionListener);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
new file mode 100644
index 000000000000..480b33556b27
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
+ // Dependencies
+ @Mock
+ private UdfpsKeyguardView mView;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private StatusBar mStatusBar;
+
+ private UdfpsKeyguardViewController mController;
+
+ // Capture listeners so that they can be used to send events
+ @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
+ private StatusBarStateController.StateListener mParentListener;
+ private StatusBarStateController.StateListener mDozeListener;
+
+ @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor;
+ private StatusBar.ExpansionChangedListener mExpansionListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new UdfpsKeyguardViewController(
+ mView,
+ mStatusBarStateController,
+ mStatusBar);
+ }
+
+ @Test
+ public void testRegistersExpansionChangedListenerOnAttached() {
+ mController.onViewAttached();
+ captureExpansionListener();
+ }
+
+ @Test
+ public void testRegistersStatusBarStateListenersOnAttached() {
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ }
+
+ @Test
+ public void testViewControllerQueriesSBStateOnAttached() {
+ mController.onViewAttached();
+ verify(mStatusBarStateController).getState();
+ verify(mStatusBarStateController).getDozeAmount();
+
+ final float dozeAmount = .88f;
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
+ when(mStatusBarStateController.getDozeAmount()).thenReturn(dozeAmount);
+ captureStatusBarStateListeners();
+
+ mController.onViewAttached();
+ verify(mView).setPauseAuth(true);
+ verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount);
+ }
+
+ @Test
+ public void testListenersUnregisteredOnDetached() {
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ captureExpansionListener();
+ mController.onViewDetached();
+
+ verify(mStatusBarStateController).removeCallback(mParentListener);
+ verify(mStatusBarStateController).removeCallback(mDozeListener);
+ verify(mStatusBar).removeExpansionChangedListener(mExpansionListener);
+ }
+
+ @Test
+ public void testDozeEventsSentToView() {
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+
+ final float linear = .55f;
+ final float eased = .65f;
+ mDozeListener.onDozeAmountChanged(linear, eased);
+
+ verify(mView).onDozeAmountChanged(linear, eased);
+ }
+
+ private void captureStatusBarStateListeners() {
+ verify(mStatusBarStateController, times(2)).addCallback(mStateListenerCaptor.capture());
+ List<StatusBarStateController.StateListener> stateListeners =
+ mStateListenerCaptor.getAllValues();
+ mParentListener = stateListeners.get(0);
+ mDozeListener = stateListeners.get(1);
+ }
+
+ private void captureExpansionListener() {
+ verify(mStatusBar).addExpansionChangedListener(mExpansionListenerCaptor.capture());
+ mExpansionListener = mExpansionListenerCaptor.getValue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index 1f9862c07a4c..3d4425cf4bd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -35,6 +35,7 @@ import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -49,7 +50,7 @@ import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class SeekBarViewModelTest : SysuiTestCase() {
private lateinit var viewModel: SeekBarViewModel
@@ -124,6 +125,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun updateDurationWithPlayback() {
// GIVEN that the duration is contained within the metadata
val duration = 12000L
@@ -146,6 +148,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun updateDurationWithoutPlayback() {
// GIVEN that the duration is contained within the metadata
val duration = 12000L
@@ -204,6 +207,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun updateDurationNoMetadata() {
// GIVEN that the metadata is null
whenever(mockController.getMetadata()).thenReturn(null)
@@ -235,6 +239,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun updateSeekAvailable() {
// GIVEN that seek is included in actions
val state = PlaybackState.Builder().run {
@@ -249,6 +254,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun updateSeekNotAvailable() {
// GIVEN that seek is not included in actions
val state = PlaybackState.Builder().run {
@@ -303,6 +309,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun onSeekProgressWithSeekStarting() {
val pos = 42L
with(viewModel) {
@@ -314,6 +321,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun onProgressChangedFromUser() {
// WHEN user starts dragging the seek bar
val pos = 42
@@ -614,6 +622,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ @Ignore
fun clearSeekBar() {
// GIVEN that the duration is contained within the metadata
val metadata = MediaMetadata.Builder().run {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index 072f7b8a7756..791dd121852f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -395,9 +395,8 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
`when`(permissionManager.indicatorAppOpUsageData).thenReturn(
listOf(usage_camera, usage_location, usage_microphone)
)
- `when`(privacyItemController.micCameraAvailable).thenReturn(false)
- `when`(privacyItemController.locationAvailable).thenReturn(false)
- `when`(privacyItemController.allIndicatorsAvailable).thenReturn(true)
+ `when`(privacyItemController.micCameraAvailable).thenReturn(true)
+ `when`(privacyItemController.locationAvailable).thenReturn(true)
controller.showDialog(context)
exhaustExecutors()
@@ -422,7 +421,6 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
)
`when`(privacyItemController.micCameraAvailable).thenReturn(false)
`when`(privacyItemController.locationAvailable).thenReturn(false)
- `when`(privacyItemController.allIndicatorsAvailable).thenReturn(false)
controller.showDialog(context)
exhaustExecutors()
@@ -525,7 +523,6 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
`when`(privacyItemController.locationAvailable).thenReturn(true)
`when`(privacyItemController.micCameraAvailable).thenReturn(true)
- `when`(privacyItemController.allIndicatorsAvailable).thenReturn(false)
`when`(userTracker.userProfiles).thenReturn(listOf(
UserInfo(USER_ID, "", 0),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
index 132bee0e7fdf..f991e718122e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -37,7 +37,6 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@@ -51,8 +50,6 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
fun <T> eq(value: T): T = Mockito.eq(value) ?: value
fun <T> any(): T = Mockito.any<T>()
- private const val ALL_INDICATORS =
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
}
@@ -96,11 +93,6 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
}
@Test
- fun testNotListeningAllByDefault() {
- assertFalse(privacyItemController.allIndicatorsAvailable)
- }
-
- @Test
fun testMicCameraListeningByDefault() {
assertTrue(privacyItemController.micCameraAvailable)
}
@@ -111,10 +103,8 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
executor.runAllReady()
verify(callback).onFlagMicCameraChanged(false)
- verify(callback, never()).onFlagAllChanged(anyBoolean())
assertFalse(privacyItemController.micCameraAvailable)
- assertFalse(privacyItemController.allIndicatorsAvailable)
}
@Test
@@ -127,26 +117,15 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
}
@Test
- fun testAllChanged() {
- changeAll(true)
- executor.runAllReady()
-
- verify(callback).onFlagAllChanged(true)
- verify(callback, never()).onFlagMicCameraChanged(anyBoolean())
-
- assertTrue(privacyItemController.allIndicatorsAvailable)
- }
-
- @Test
fun testBothChanged() {
changeAll(true)
changeMicCamera(false)
executor.runAllReady()
- verify(callback, atLeastOnce()).onFlagAllChanged(true)
+ verify(callback, atLeastOnce()).onFlagLocationChanged(true)
verify(callback, atLeastOnce()).onFlagMicCameraChanged(false)
- assertTrue(privacyItemController.allIndicatorsAvailable)
+ assertTrue(privacyItemController.locationAvailable)
assertFalse(privacyItemController.micCameraAvailable)
}
@@ -186,28 +165,6 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
}
@Test
- fun testSomeListening_stillListening() {
- // Mic and camera are true by default
- changeAll(true)
- executor.runAllReady()
- changeAll(false)
- executor.runAllReady()
-
- verify(appOpsController, never()).removeCallback(any(), any())
- }
-
- @Test
- fun testAllDeleted_micCameraFalse_stopListening() {
- changeMicCamera(false)
- changeAll(true)
- executor.runAllReady()
- changeAll(null)
- executor.runAllReady()
-
- verify(appOpsController).removeCallback(any(), any())
- }
-
- @Test
fun testMicDeleted_stillListening() {
changeMicCamera(true)
executor.runAllReady()
@@ -219,7 +176,10 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() {
private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
private fun changeLocation(value: Boolean?) = changeProperty(LOCATION, value)
- private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
+ private fun changeAll(value: Boolean?) {
+ changeMicCamera(value)
+ changeLocation(value)
+ }
private fun changeProperty(name: String, value: Boolean?) {
deviceConfigProxy.setProperty(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 7ca468edfd9c..b87c7a6ad2d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -43,7 +43,6 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertThat
import org.junit.Assert.assertTrue
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -72,8 +71,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {
val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
const val TEST_PACKAGE_NAME = "test"
- private const val ALL_INDICATORS =
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
+ private const val LOCATION_INDICATOR =
+ SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
fun <T> eq(value: T): T = Mockito.eq(value) ?: value
@@ -119,7 +118,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {
deviceConfigProxy = DeviceConfigProxyFake()
// Listen to everything by default
- changeAll(true)
+ changeMicCamera(true)
+ changeLocation(true)
`when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(CURRENT_USER_ID, "", 0)))
@@ -259,9 +259,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {
}
@Test
- @Ignore // TODO(b/168209929)
fun testNotListeningWhenIndicatorsDisabled() {
- changeAll(false)
+ changeLocation(false)
changeMicCamera(false)
privacyItemController.addCallback(callback)
executor.runAllReady()
@@ -271,7 +270,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
@Test
fun testNotSendingLocationWhenOnlyMicCamera() {
- changeAll(false)
+ changeLocation(false)
changeMicCamera(true)
executor.runAllReady()
@@ -294,7 +293,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
.`when`(appOpsController).getActiveAppOpsForUser(anyInt())
privacyItemController.addCallback(callback)
- changeAll(false)
+ changeLocation(false)
changeMicCamera(true)
executor.runAllReady()
reset(callback) // Clean callback
@@ -521,7 +520,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
}
private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
- private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
+ private fun changeLocation(value: Boolean?) = changeProperty(LOCATION_INDICATOR, value)
private fun changeProperty(name: String, value: Boolean?) {
deviceConfigProxy.setProperty(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 97a845916185..4948c2b18746 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -148,7 +148,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Test
fun testIgnoredSlotsOnAttached_noIndicators() {
- setPrivacyController(false, false, false)
+ setPrivacyController(micCamera = false, location = false)
controller.init()
@@ -160,7 +160,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Test
fun testIgnoredSlotsOnAttached_onlyMicCamera() {
- setPrivacyController(false, true, false)
+ setPrivacyController(micCamera = true, location = false)
controller.init()
@@ -177,7 +177,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Test
fun testIgnoredSlotsOnAttached_onlyLocation() {
- setPrivacyController(false, false, true)
+ setPrivacyController(micCamera = false, location = true)
controller.init()
@@ -192,26 +192,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Test
fun testIgnoredSlotsOnAttached_locationMicCamera() {
- setPrivacyController(false, true, true)
-
- controller.init()
-
- val captor = argumentCaptor<List<String>>()
- verify(iconContainer).setIgnoredSlots(capture(captor))
-
- val cameraString = mContext.resources.getString(
- com.android.internal.R.string.status_bar_camera)
- val micString = mContext.resources.getString(
- com.android.internal.R.string.status_bar_microphone)
- val locationString = mContext.resources.getString(
- com.android.internal.R.string.status_bar_location)
-
- assertThat(captor.value).containsExactly(cameraString, micString, locationString)
- }
-
- @Test
- fun testIgnoredSlotsOnAttached_all() {
- setPrivacyController(true, false, false)
+ setPrivacyController(micCamera = true, location = true)
controller.init()
@@ -248,8 +229,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
`when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock)
}
- private fun setPrivacyController(all: Boolean, micCamera: Boolean, location: Boolean) {
- `when`(privacyItemController.allIndicatorsAvailable).thenReturn(all)
+ private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
`when`(privacyItemController.micCameraAvailable).thenReturn(micCamera)
`when`(privacyItemController.locationAvailable).thenReturn(location)
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index bb4bbd5bc6d4..1e608f5c1240 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -918,19 +918,7 @@ public final class BatteryService extends SystemService {
int opts = parseOptions(shell);
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
- if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
- }
- mHealthInfo.chargerAcOnline = false;
- mHealthInfo.chargerUsbOnline = false;
- mHealthInfo.chargerWirelessOnline = false;
- final long ident = Binder.clearCallingIdentity();
- try {
- mUpdatesStopped = true;
- processValuesFromShellLocked(pw, opts);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ unplugBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
} break;
case "set": {
int opts = parseOptions(shell);
@@ -990,7 +978,8 @@ public final class BatteryService extends SystemService {
final long ident = Binder.clearCallingIdentity();
try {
mUpdatesStopped = true;
- processValuesFromShellLocked(pw, opts);
+ processValuesLocked(
+ /* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1004,30 +993,12 @@ public final class BatteryService extends SystemService {
int opts = parseOptions(shell);
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
- final long ident = Binder.clearCallingIdentity();
- try {
- if (mUpdatesStopped) {
- mUpdatesStopped = false;
- copy(mHealthInfo, mLastHealthInfo);
- processValuesFromShellLocked(pw, opts);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- if (mBatteryInputSuspended) {
- PowerProperties.battery_input_suspended(false);
- mBatteryInputSuspended = false;
- }
+ resetBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
} break;
case "suspend_input": {
- if (!Build.IS_DEBUGGABLE) {
- throw new SecurityException(
- "battery suspend_input is only supported on debuggable builds");
- }
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
- PowerProperties.battery_input_suspended(true);
- mBatteryInputSuspended = true;
+ suspendBatteryInput();
} break;
default:
return shell.handleDefaultCommands(cmd);
@@ -1035,9 +1006,59 @@ public final class BatteryService extends SystemService {
return 0;
}
- private void processValuesFromShellLocked(PrintWriter pw, int opts) {
- processValuesLocked((opts & OPTION_FORCE_UPDATE) != 0);
- if ((opts & OPTION_FORCE_UPDATE) != 0) {
+ private void setChargerAcOnline(boolean online, boolean forceUpdate) {
+ if (!mUpdatesStopped) {
+ copy(mLastHealthInfo, mHealthInfo);
+ }
+ mHealthInfo.chargerAcOnline = online;
+ mUpdatesStopped = true;
+ Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate));
+ }
+
+ private void setBatteryLevel(int level, boolean forceUpdate) {
+ if (!mUpdatesStopped) {
+ copy(mLastHealthInfo, mHealthInfo);
+ }
+ mHealthInfo.batteryLevel = level;
+ mUpdatesStopped = true;
+ Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate));
+ }
+
+ private void unplugBattery(boolean forceUpdate, PrintWriter pw) {
+ if (!mUpdatesStopped) {
+ copy(mLastHealthInfo, mHealthInfo);
+ }
+ mHealthInfo.chargerAcOnline = false;
+ mHealthInfo.chargerUsbOnline = false;
+ mHealthInfo.chargerWirelessOnline = false;
+ mUpdatesStopped = true;
+ Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
+ }
+
+ private void resetBattery(boolean forceUpdate, @Nullable PrintWriter pw) {
+ if (mUpdatesStopped) {
+ mUpdatesStopped = false;
+ copy(mHealthInfo, mLastHealthInfo);
+ Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
+ }
+ if (mBatteryInputSuspended) {
+ PowerProperties.battery_input_suspended(false);
+ mBatteryInputSuspended = false;
+ }
+ }
+
+ private void suspendBatteryInput() {
+ if (!Build.IS_DEBUGGABLE) {
+ throw new SecurityException(
+ "battery suspend_input is only supported on debuggable builds");
+ }
+ PowerProperties.battery_input_suspended(true);
+ mBatteryInputSuspended = true;
+ }
+
+ private void processValuesLocked(boolean forceUpdate, @Nullable PrintWriter pw) {
+ processValuesLocked(forceUpdate);
+ if (pw != null && forceUpdate) {
pw.println(mSequence);
}
}
@@ -1363,6 +1384,41 @@ public final class BatteryService extends SystemService {
return mInvalidCharger;
}
}
+
+ @Override
+ public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+ BatteryService.this.setChargerAcOnline(online, forceUpdate);
+ }
+
+ @Override
+ public void setBatteryLevel(int level, boolean forceUpdate) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+ BatteryService.this.setBatteryLevel(level, forceUpdate);
+ }
+
+ @Override
+ public void unplugBattery(boolean forceUpdate) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+ BatteryService.this.unplugBattery(forceUpdate, /* printWriter= */ null);
+ }
+
+ @Override
+ public void resetBattery(boolean forceUpdate) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+ BatteryService.this.resetBattery(forceUpdate, /* printWriter= */ null);
+ }
+
+ @Override
+ public void suspendBatteryInput() {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+ BatteryService.this.suspendBatteryInput();
+ }
}
/**
@@ -1539,6 +1595,8 @@ public final class BatteryService extends SystemService {
if (Objects.equals(newService, oldService)) return;
Slog.i(TAG, "health: new instance registered " + mInstanceName);
+ // #init() may be called with null callback. Skip null callbacks.
+ if (mCallback == null) return;
mCallback.onRegistration(oldService, newService, mInstanceName);
} catch (NoSuchElementException | RemoteException ex) {
Slog.e(TAG, "health: Cannot get instance '" + mInstanceName
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index e12586bfdc06..c9836001da77 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -18,6 +18,7 @@ per-file ServiceWatcher.java = sooniln@google.com
per-file *Alarm* = file:/apex/jobscheduler/OWNERS
per-file *AppOp* = file:/core/java/android/permission/OWNERS
+per-file *Battery* = file:/BATTERY_STATS_OWNERS
per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a9904ba0de91..5adbdff150ea 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -318,7 +318,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private int[] mDataEnabledReason;
- private Map<Integer, Long> mAllowedNetworkTypesList;
+ private int[] mAllowedNetworkTypeReason;
+ private long[] mAllowedNetworkTypeValue;
/**
* Per-phone map of precise data connection state. The key of the map is the pair of transport
@@ -383,7 +384,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) {
return events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
|| events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
- || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
+ || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
+ || events.contains(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
}
private static final int MSG_USER_SWITCHED = 1;
@@ -527,6 +529,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones);
mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones);
mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones);
+ mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones);
+ mAllowedNetworkTypeValue = copyOf(mAllowedNetworkTypeValue, mNumPhones);
// ds -> ss switch.
if (mNumPhones < oldNumPhones) {
@@ -571,6 +575,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
+ mAllowedNetworkTypeReason[i] = -1;
+ mAllowedNetworkTypeValue[i] = -1;
}
}
@@ -630,9 +636,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mBarringInfo = new ArrayList<>();
mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones];
mPhysicalChannelConfigs = new ArrayList<>();
- mAllowedNetworkTypesList = new HashMap<>();
+ mAllowedNetworkTypeReason = new int[numPhones];
+ mAllowedNetworkTypeValue = new long[numPhones];
mIsDataEnabled = new boolean[numPhones];
mDataEnabledReason = new int[numPhones];
+
for (int i = 0; i < numPhones; i++) {
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -665,6 +673,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
+ mAllowedNetworkTypeReason[i] = -1;
+ mAllowedNetworkTypeValue[i] = -1;
}
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -1172,14 +1182,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
- if (events.contains(
- TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)) {
- try {
- r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
- }
}
}
}
@@ -2454,18 +2456,19 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
*
* @param phoneId the phone id.
* @param subId the subId.
- * @param allowedNetworkTypesList Map associating all allowed network type reasons with reason's
- * allowed network type values.
+ * @param reason the allowed network type reason.
+ * @param allowedNetworkType the allowed network type value.
*/
- public void notifyAllowedNetworkTypesChanged(int phoneId, int subId,
- Map allowedNetworkTypesList) {
+ public void notifyAllowedNetworkTypesChanged(int phoneId, int subId, int reason,
+ long allowedNetworkType) {
if (!checkNotifyPermission("notifyAllowedNetworkTypesChanged()")) {
return;
}
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
- mAllowedNetworkTypesList = allowedNetworkTypesList;
+ mAllowedNetworkTypeReason[phoneId] = reason;
+ mAllowedNetworkTypeValue[phoneId] = allowedNetworkType;
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
@@ -2473,10 +2476,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
&& idMatch(r.subId, subId, phoneId)) {
try {
if (VDBG) {
- log("notifyAllowedNetworkTypesChanged: AllowedNetworkTypesList= "
- + mAllowedNetworkTypesList.toString());
+ log("notifyAllowedNetworkTypesChanged: reason= " + reason
+ + ", allowed network type:"
+ + TelephonyManager.convertNetworkTypeBitmaskToString(
+ allowedNetworkType));
}
- r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList);
+ r.callback.onAllowedNetworkTypesChanged(reason, allowedNetworkType);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -2531,6 +2536,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
pw.println("mIsDataEnabled=" + mIsDataEnabled);
pw.println("mDataEnabledReason=" + mDataEnabledReason);
+ pw.println("mAllowedNetworkTypeReason=" + mAllowedNetworkTypeReason[i]);
+ pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]);
pw.decreaseIndent();
}
pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index cd3892da43e4..140f24f7cf8f 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -666,6 +666,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull IVcnUnderlyingNetworkPolicyListener listener) {
requireNonNull(listener, "listener was null");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.NETWORK_FACTORY,
+ "Must have permission NETWORK_FACTORY to unregister a policy listener");
+
Binder.withCleanCallingIdentity(() -> {
synchronized (mLock) {
PolicyListenerBinderDeath listenerBinderDeath =
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index dbfa7f34c6f9..6b9fc0718879 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -32,6 +32,7 @@ import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.os.BatteryManagerInternal;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.BatteryUsageStats;
@@ -184,6 +185,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
};
+ private BatteryManagerInternal mBatteryManagerInternal;
+
private void populatePowerEntityMaps() {
PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo();
if (entities == null) {
@@ -370,6 +373,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
Slog.e(TAG, "Could not register PowerStatsInternal");
}
}
+ mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
Watchdog.getInstance().addMonitor(this);
@@ -2715,4 +2719,44 @@ public final class BatteryStatsService extends IBatteryStats.Stub
});
}
}
+
+ /**
+ * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+ */
+ @Override
+ public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+ mBatteryManagerInternal.setChargerAcOnline(online, forceUpdate);
+ }
+
+ /**
+ * Sets battery level, and freezes the battery state.
+ */
+ @Override
+ public void setBatteryLevel(int level, boolean forceUpdate) {
+ mBatteryManagerInternal.setBatteryLevel(level, forceUpdate);
+ }
+
+ /**
+ * Unplugs battery, and freezes the battery state.
+ */
+ @Override
+ public void unplugBattery(boolean forceUpdate) {
+ mBatteryManagerInternal.unplugBattery(forceUpdate);
+ }
+
+ /**
+ * Unfreezes battery state, returning to current hardware values.
+ */
+ @Override
+ public void resetBattery(boolean forceUpdate) {
+ mBatteryManagerInternal.resetBattery(forceUpdate);
+ }
+
+ /**
+ * Suspend charging even if plugged in.
+ */
+ @Override
+ public void suspendBatteryInput() {
+ mBatteryManagerInternal.suspendBatteryInput();
+ }
}
diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java
index 3e32380b60a9..2982545a5e6f 100644
--- a/services/core/java/com/android/server/app/GameManagerSettings.java
+++ b/services/core/java/com/android/server/app/GameManagerSettings.java
@@ -137,6 +137,11 @@ public class GameManagerSettings {
boolean readPersistentDataLocked() {
mGameModes.clear();
+ if (!mSettingsFile.exists()) {
+ Slog.v(GameManagerService.TAG, "Settings file doesn't exists, skip reading");
+ return false;
+ }
+
try {
final FileInputStream str = mSettingsFile.openRead();
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6614e06aba8c..1122f7f4115a 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2098,26 +2098,28 @@ public class AppOpsService extends IAppOpsService.Stub {
ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
beginTimeMillis, endTimeMillis, flags);
Objects.requireNonNull(callback, "callback cannot be null");
-
ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
- boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
- boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
- boolean isCallerPermissionController;
- try {
- isCallerPermissionController = pm.getPackageUid(
- mContext.getPackageManager().getPermissionControllerPackageName(), 0)
- == Binder.getCallingUid();
- } catch (PackageManager.NameNotFoundException doesNotHappen) {
- return;
- }
+ boolean isSelfRequest = (filter & FILTER_BY_UID) != 0 && uid == Binder.getCallingUid();
+ if (!isSelfRequest) {
+ boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
+ boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
+ boolean isCallerPermissionController;
+ try {
+ isCallerPermissionController = pm.getPackageUid(
+ mContext.getPackageManager().getPermissionControllerPackageName(), 0)
+ == Binder.getCallingUid();
+ } catch (PackageManager.NameNotFoundException doesNotHappen) {
+ return;
+ }
- if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) {
- mHandler.post(() -> callback.sendResult(new Bundle()));
- return;
- }
+ if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) {
+ mHandler.post(() -> callback.sendResult(new Bundle()));
+ return;
+ }
- mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
+ mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
+ }
final String[] opNamesArray = (opNames != null)
? opNames.toArray(new String[opNames.size()]) : null;
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 22d628b8e789..eeed4521f6c4 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -795,7 +795,7 @@ final class HistoricalRegistry {
private static boolean isApiEnabled() {
return Binder.getCallingUid() == Process.myUid()
|| DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_PERMISSIONS_HUB_ENABLED, false);
+ PROPERTY_PERMISSIONS_HUB_ENABLED, true);
}
private static final class Persistence {
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 5cf478a3ef1f..ae9b0015de43 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change;
@@ -55,7 +56,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
* A change ID to be used only in the CTS test for this SystemApi
*/
@ChangeId
- @EnabledSince(targetSdkVersion = 1235) // Needs to be > test APK targetSdkVersion.
+ @EnabledSince(targetSdkVersion = 31) // Needs to be > test APK targetSdkVersion.
static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id.
/**
@@ -233,7 +234,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @param app Info about the app in question
* @return {@code true} if the change should be enabled for the package.
*/
- boolean isEnabled(ApplicationInfo app) {
+ boolean isEnabled(ApplicationInfo app, AndroidBuildClassifier buildClassifier) {
if (app == null) {
return defaultValue();
}
@@ -244,7 +245,13 @@ public final class CompatChange extends CompatibilityChangeInfo {
return false;
}
if (getEnableSinceTargetSdk() != -1) {
- return app.targetSdkVersion >= getEnableSinceTargetSdk();
+ // If the change is gated by a platform version newer than the one currently installed
+ // on the device, disregard the app's target sdk version.
+ int compareSdk = Math.min(app.targetSdkVersion, buildClassifier.platformTargetSdk());
+ if (compareSdk != app.targetSdkVersion) {
+ compareSdk = app.targetSdkVersion;
+ }
+ return compareSdk >= getEnableSinceTargetSdk();
}
return true;
}
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 2c053b421904..ef86f42d6c3c 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -74,12 +74,14 @@ final class CompatConfig {
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
private final OverrideValidatorImpl mOverrideValidator;
+ private final AndroidBuildClassifier mAndroidBuildClassifier;
private Context mContext;
private File mOverridesFile;
@VisibleForTesting
CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this);
+ mAndroidBuildClassifier = androidBuildClassifier;
mContext = context;
}
@@ -133,7 +135,7 @@ final class CompatConfig {
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
CompatChange c = mChanges.valueAt(i);
- if (!c.isEnabled(app)) {
+ if (!c.isEnabled(app, mAndroidBuildClassifier)) {
disabled.add(c.getId());
}
}
@@ -175,7 +177,7 @@ final class CompatConfig {
// we know nothing about this change: default behaviour is enabled.
return true;
}
- return c.isEnabled(app);
+ return c.isEnabled(app, mAndroidBuildClassifier);
}
}
@@ -475,7 +477,7 @@ final class CompatConfig {
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
CompatChange c = mChanges.valueAt(i);
- if (c.isEnabled(applicationInfo)) {
+ if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) {
enabled.add(c.getId());
} else {
disabled.add(c.getId());
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index fe5b4a98797d..aa66a1a8b01f 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -22,6 +22,7 @@ import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARG
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
+import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -85,6 +86,9 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub {
if (debuggableBuild) {
return new OverrideAllowedState(ALLOWED, -1, -1);
}
+ if (maxTargetSdk >= mAndroidBuildClassifier.platformTargetSdk()) {
+ return new OverrideAllowedState(PLATFORM_TOO_OLD, -1, maxTargetSdk);
+ }
PackageManager packageManager = mContext.getPackageManager();
if (packageManager == null) {
throw new IllegalStateException("No PackageManager!");
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index d17753fe81bd..2be39aa24294 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -66,18 +66,22 @@ public class PlatformCompat extends IPlatformCompat.Stub {
private final Context mContext;
private final ChangeReporter mChangeReporter;
private final CompatConfig mCompatConfig;
+ private final AndroidBuildClassifier mBuildClassifier;
public PlatformCompat(Context context) {
mContext = context;
mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
- mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
+ mBuildClassifier = new AndroidBuildClassifier();
+ mCompatConfig = CompatConfig.create(mBuildClassifier, mContext);
}
@VisibleForTesting
- PlatformCompat(Context context, CompatConfig compatConfig) {
+ PlatformCompat(Context context, CompatConfig compatConfig,
+ AndroidBuildClassifier buildClassifier) {
mContext = context;
mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
mCompatConfig = compatConfig;
+ mBuildClassifier = buildClassifier;
registerPackageReceiver(context);
}
@@ -392,7 +396,8 @@ public class PlatformCompat extends IPlatformCompat.Stub {
return false;
}
if (change.getEnableSinceTargetSdk() > 0) {
- return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q;
+ return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q
+ && change.getEnableSinceTargetSdk() <= mBuildClassifier.platformTargetSdk();
}
return true;
}
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index f275663a1309..1eef0de3a05d 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -27,6 +27,7 @@ import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
import android.location.GnssStatus;
import android.location.Location;
+import android.os.Binder;
import android.os.SystemClock;
import android.util.Log;
@@ -921,6 +922,7 @@ public class GnssNative {
@NativeEntryPoint
void reportGnssServiceDied() {
+ // Not necessary to clear (and restore) binder identity since it runs on another thread.
Log.e(TAG, "gnss hal died - restarting shortly...");
// move to another thread just in case there is some awkward gnss thread dependency with
@@ -940,96 +942,111 @@ public class GnssNative {
@NativeEntryPoint
void reportLocation(boolean hasLatLong, Location location) {
- if (hasLatLong && !mHasFirstFix) {
- mHasFirstFix = true;
-
- // notify status listeners
- int ttff = (int) (SystemClock.elapsedRealtime() - mStartRealtimeMs);
- for (int i = 0; i < mStatusCallbacks.length; i++) {
- mStatusCallbacks[i].onReportFirstFix(ttff);
+ Binder.withCleanCallingIdentity(() -> {
+ if (hasLatLong && !mHasFirstFix) {
+ mHasFirstFix = true;
+
+ // notify status listeners
+ int ttff = (int) (SystemClock.elapsedRealtime() - mStartRealtimeMs);
+ for (int i = 0; i < mStatusCallbacks.length; i++) {
+ mStatusCallbacks[i].onReportFirstFix(ttff);
+ }
}
- }
- if (location.hasSpeed()) {
- boolean exceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
- if (!mItarSpeedLimitExceeded && exceeded) {
- Log.w(TAG, "speed nearing ITAR threshold - blocking further GNSS output");
- } else if (mItarSpeedLimitExceeded && !exceeded) {
- Log.w(TAG, "speed leaving ITAR threshold - allowing further GNSS output");
+ if (location.hasSpeed()) {
+ boolean exceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
+ if (!mItarSpeedLimitExceeded && exceeded) {
+ Log.w(TAG, "speed nearing ITAR threshold - blocking further GNSS output");
+ } else if (mItarSpeedLimitExceeded && !exceeded) {
+ Log.w(TAG, "speed leaving ITAR threshold - allowing further GNSS output");
+ }
+ mItarSpeedLimitExceeded = exceeded;
}
- mItarSpeedLimitExceeded = exceeded;
- }
- if (mItarSpeedLimitExceeded) {
- return;
- }
+ if (mItarSpeedLimitExceeded) {
+ return;
+ }
- for (int i = 0; i < mLocationCallbacks.length; i++) {
- mLocationCallbacks[i].onReportLocation(hasLatLong, location);
- }
+ for (int i = 0; i < mLocationCallbacks.length; i++) {
+ mLocationCallbacks[i].onReportLocation(hasLatLong, location);
+ }
+ });
}
@NativeEntryPoint
void reportStatus(@StatusCallbacks.GnssStatusValue int gnssStatus) {
- for (int i = 0; i < mStatusCallbacks.length; i++) {
- mStatusCallbacks[i].onReportStatus(gnssStatus);
- }
+ Binder.withCleanCallingIdentity(() -> {
+ for (int i = 0; i < mStatusCallbacks.length; i++) {
+ mStatusCallbacks[i].onReportStatus(gnssStatus);
+ }
+ });
}
@NativeEntryPoint
void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs,
float[] elevations, float[] azimuths, float[] carrierFrequencies,
float[] basebandCn0DbHzs) {
- GnssStatus gnssStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0DbHzs, elevations,
- azimuths, carrierFrequencies, basebandCn0DbHzs);
- for (int i = 0; i < mSvStatusCallbacks.length; i++) {
- mSvStatusCallbacks[i].onReportSvStatus(gnssStatus);
- }
+ Binder.withCleanCallingIdentity(() -> {
+ GnssStatus gnssStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0DbHzs, elevations,
+ azimuths, carrierFrequencies, basebandCn0DbHzs);
+ for (int i = 0; i < mSvStatusCallbacks.length; i++) {
+ mSvStatusCallbacks[i].onReportSvStatus(gnssStatus);
+ }
+ });
}
@NativeEntryPoint
void reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
- mAGpsCallbacks.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
+ Binder.withCleanCallingIdentity(
+ () -> mAGpsCallbacks.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr));
}
@NativeEntryPoint
void reportNmea(long timestamp) {
- if (mItarSpeedLimitExceeded) {
- return;
- }
+ Binder.withCleanCallingIdentity(() -> {
+ if (mItarSpeedLimitExceeded) {
+ return;
+ }
- for (int i = 0; i < mNmeaCallbacks.length; i++) {
- mNmeaCallbacks[i].onReportNmea(timestamp);
- }
+ for (int i = 0; i < mNmeaCallbacks.length; i++) {
+ mNmeaCallbacks[i].onReportNmea(timestamp);
+ }
+ });
}
@NativeEntryPoint
void reportMeasurementData(GnssMeasurementsEvent event) {
- if (mItarSpeedLimitExceeded) {
- return;
- }
+ Binder.withCleanCallingIdentity(() -> {
+ if (mItarSpeedLimitExceeded) {
+ return;
+ }
- for (int i = 0; i < mMeasurementCallbacks.length; i++) {
- mMeasurementCallbacks[i].onReportMeasurements(event);
- }
+ for (int i = 0; i < mMeasurementCallbacks.length; i++) {
+ mMeasurementCallbacks[i].onReportMeasurements(event);
+ }
+ });
}
@NativeEntryPoint
void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
- for (int i = 0; i < mAntennaInfoCallbacks.length; i++) {
- mAntennaInfoCallbacks[i].onReportAntennaInfo(antennaInfos);
- }
+ Binder.withCleanCallingIdentity(() -> {
+ for (int i = 0; i < mAntennaInfoCallbacks.length; i++) {
+ mAntennaInfoCallbacks[i].onReportAntennaInfo(antennaInfos);
+ }
+ });
}
@NativeEntryPoint
void reportNavigationMessage(GnssNavigationMessage event) {
- if (mItarSpeedLimitExceeded) {
- return;
- }
+ Binder.withCleanCallingIdentity(() -> {
+ if (mItarSpeedLimitExceeded) {
+ return;
+ }
- for (int i = 0; i < mNavigationMessageCallbacks.length; i++) {
- mNavigationMessageCallbacks[i].onReportNavigationMessage(event);
- }
+ for (int i = 0; i < mNavigationMessageCallbacks.length; i++) {
+ mNavigationMessageCallbacks[i].onReportNavigationMessage(event);
+ }
+ });
}
@NativeEntryPoint
@@ -1061,15 +1078,17 @@ public class GnssNative {
private void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
GnssCapabilities newCapabilities) {
- if (newCapabilities.equals(oldCapabilities)) {
- return;
- }
+ Binder.withCleanCallingIdentity(() -> {
+ if (newCapabilities.equals(oldCapabilities)) {
+ return;
+ }
- Log.i(TAG, "gnss capabilities changed to " + newCapabilities);
+ Log.i(TAG, "gnss capabilities changed to " + newCapabilities);
- for (int i = 0; i < mBaseCallbacks.length; i++) {
- mBaseCallbacks[i].onCapabilitiesChanged(oldCapabilities, newCapabilities);
- }
+ for (int i = 0; i < mBaseCallbacks.length; i++) {
+ mBaseCallbacks[i].onCapabilitiesChanged(oldCapabilities, newCapabilities);
+ }
+ });
}
@NativeEntryPoint
@@ -1089,88 +1108,103 @@ public class GnssNative {
@NativeEntryPoint
void reportLocationBatch(Location[] locations) {
- for (int i = 0; i < mLocationCallbacks.length; i++) {
- mLocationCallbacks[i].onReportLocations(locations);
- }
+ Binder.withCleanCallingIdentity(() -> {
+ for (int i = 0; i < mLocationCallbacks.length; i++) {
+ mLocationCallbacks[i].onReportLocations(locations);
+ }
+ });
}
@NativeEntryPoint
void psdsDownloadRequest(int psdsType) {
- mPsdsCallbacks.onRequestPsdsDownload(psdsType);
+ Binder.withCleanCallingIdentity(() -> mPsdsCallbacks.onRequestPsdsDownload(psdsType));
}
@NativeEntryPoint
void reportGeofenceTransition(int geofenceId, Location location, int transition,
long transitionTimestamp) {
- mGeofenceCallbacks.onReportGeofenceTransition(geofenceId, location, transition,
- transitionTimestamp);
+ Binder.withCleanCallingIdentity(
+ () -> mGeofenceCallbacks.onReportGeofenceTransition(geofenceId, location,
+ transition, transitionTimestamp));
}
@NativeEntryPoint
void reportGeofenceStatus(int status, Location location) {
- mGeofenceCallbacks.onReportGeofenceStatus(status, location);
+ Binder.withCleanCallingIdentity(
+ () -> mGeofenceCallbacks.onReportGeofenceStatus(status, location));
}
@NativeEntryPoint
void reportGeofenceAddStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
- mGeofenceCallbacks.onReportGeofenceAddStatus(geofenceId, status);
+ Binder.withCleanCallingIdentity(
+ () -> mGeofenceCallbacks.onReportGeofenceAddStatus(geofenceId, status));
}
@NativeEntryPoint
void reportGeofenceRemoveStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
- mGeofenceCallbacks.onReportGeofenceRemoveStatus(geofenceId, status);
+ Binder.withCleanCallingIdentity(
+ () -> mGeofenceCallbacks.onReportGeofenceRemoveStatus(geofenceId, status));
}
@NativeEntryPoint
void reportGeofencePauseStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
- mGeofenceCallbacks.onReportGeofencePauseStatus(geofenceId, status);
+ Binder.withCleanCallingIdentity(
+ () -> mGeofenceCallbacks.onReportGeofencePauseStatus(geofenceId, status));
}
@NativeEntryPoint
void reportGeofenceResumeStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
- mGeofenceCallbacks.onReportGeofenceResumeStatus(geofenceId, status);
+ Binder.withCleanCallingIdentity(
+ () -> mGeofenceCallbacks.onReportGeofenceResumeStatus(geofenceId, status));
}
@NativeEntryPoint
void reportNiNotification(int notificationId, int niType, int notifyFlags,
int timeout, int defaultResponse, String requestorId, String text,
int requestorIdEncoding, int textEncoding) {
- mNotificationCallbacks.onReportNiNotification(notificationId, niType, notifyFlags, timeout,
- defaultResponse, requestorId, text, requestorIdEncoding, textEncoding);
+ Binder.withCleanCallingIdentity(
+ () -> mNotificationCallbacks.onReportNiNotification(notificationId, niType,
+ notifyFlags, timeout, defaultResponse, requestorId, text,
+ requestorIdEncoding, textEncoding));
}
@NativeEntryPoint
void requestSetID(int flags) {
- mAGpsCallbacks.onRequestSetID(flags);
+ Binder.withCleanCallingIdentity(() -> mAGpsCallbacks.onRequestSetID(flags));
}
@NativeEntryPoint
void requestLocation(boolean independentFromGnss, boolean isUserEmergency) {
- mLocationRequestCallbacks.onRequestLocation(independentFromGnss, isUserEmergency);
+ Binder.withCleanCallingIdentity(
+ () -> mLocationRequestCallbacks.onRequestLocation(independentFromGnss,
+ isUserEmergency));
}
@NativeEntryPoint
void requestUtcTime() {
- mTimeCallbacks.onRequestUtcTime();
+ Binder.withCleanCallingIdentity(() -> mTimeCallbacks.onRequestUtcTime());
}
@NativeEntryPoint
void requestRefLocation() {
- mLocationRequestCallbacks.onRequestRefLocation();
+ Binder.withCleanCallingIdentity(
+ () -> mLocationRequestCallbacks.onRequestRefLocation());
}
@NativeEntryPoint
void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
String otherProtocolStackName, byte requestor, String requestorId,
byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
- mNotificationCallbacks.onReportNfwNotification(proxyAppPackageName, protocolStack,
- otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
- isCachedLocation);
+ Binder.withCleanCallingIdentity(
+ () -> mNotificationCallbacks.onReportNfwNotification(proxyAppPackageName,
+ protocolStack, otherProtocolStackName, requestor, requestorId, responseType,
+ inEmergencyMode, isCachedLocation));
}
@NativeEntryPoint
boolean isInEmergencySession() {
- return mEmergencyHelper.isInEmergency(mConfiguration.getEsExtensionSec());
+ return Binder.withCleanCallingIdentity(
+ () -> mEmergencyHelper.isInEmergency(mConfiguration.getEsExtensionSec()));
}
/**
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 1acbabda9e19..ca8202f5f94b 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -105,6 +105,12 @@ public class AppsFilter implements Watchable, Snappable {
private final SparseSetArray<Integer> mQueriesViaComponent = new SparseSetArray<>();
/**
+ * A mapping from the set of App IDs that query other App IDs via library name to the
+ * list of packages that they can see.
+ */
+ private final SparseSetArray<Integer> mQueryableViaUsesLibrary = new SparseSetArray<>();
+
+ /**
* Executor for running reasonably short background tasks such as building the initial
* visibility cache.
*/
@@ -239,6 +245,7 @@ public class AppsFilter implements Watchable, Snappable {
Snapshots.copy(mImplicitlyQueryable, orig.mImplicitlyQueryable);
Snapshots.copy(mQueriesViaPackage, orig.mQueriesViaPackage);
Snapshots.copy(mQueriesViaComponent, orig.mQueriesViaComponent);
+ Snapshots.copy(mQueryableViaUsesLibrary, orig.mQueryableViaUsesLibrary);
mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute;
mForceQueryable.addAll(orig.mForceQueryable);
mForceQueryableByDevicePackageNames = orig.mForceQueryableByDevicePackageNames;
@@ -508,6 +515,22 @@ public class AppsFilter implements Watchable, Snappable {
return false;
}
+ private static boolean canQueryViaUsesLibrary(AndroidPackage querying,
+ AndroidPackage potentialTarget) {
+ if (potentialTarget.getLibraryNames().isEmpty()) {
+ return false;
+ }
+ final List<String> libNames = potentialTarget.getLibraryNames();
+ for (int i = 0, size = libNames.size(); i < size; i++) {
+ final String libName = libNames.get(i);
+ if (querying.getUsesLibraries().contains(libName)
+ || querying.getUsesOptionalLibraries().contains(libName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static boolean matchesProviders(
Set<String> queriesAuthorities, AndroidPackage potentialTarget) {
for (int p = ArrayUtils.size(potentialTarget.getProviders()) - 1; p >= 0; p--) {
@@ -707,6 +730,9 @@ public class AppsFilter implements Watchable, Snappable {
|| canQueryAsInstaller(existingSetting, newPkg)) {
mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
}
+ if (canQueryViaUsesLibrary(existingPkg, newPkg)) {
+ mQueryableViaUsesLibrary.add(existingSetting.appId, newPkgSetting.appId);
+ }
}
// now we'll evaluate our new package's ability to see existing packages
if (!mForceQueryable.contains(existingSetting.appId)) {
@@ -718,6 +744,9 @@ public class AppsFilter implements Watchable, Snappable {
|| canQueryAsInstaller(newPkgSetting, existingPkg)) {
mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
}
+ if (canQueryViaUsesLibrary(newPkg, existingPkg)) {
+ mQueryableViaUsesLibrary.add(newPkgSetting.appId, existingSetting.appId);
+ }
}
// if either package instruments the other, mark both as visible to one another
if (newPkgSetting.pkg != null && existingSetting.pkg != null
@@ -1035,6 +1064,10 @@ public class AppsFilter implements Watchable, Snappable {
for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId);
}
+ mQueryableViaUsesLibrary.remove(setting.appId);
+ for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) {
+ mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), setting.appId);
+ }
mForceQueryable.remove(setting.appId);
@@ -1315,6 +1348,18 @@ public class AppsFilter implements Watchable, Snappable {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary");
+ if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) {
+ if (DEBUG_LOGGING) {
+ log(callingSetting, targetPkgSetting, "queryable for library users");
+ }
+ return false;
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
return true;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -1394,6 +1439,8 @@ public class AppsFilter implements Watchable, Snappable {
filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId),
mImplicitlyQueryable, " ", expandPackages);
}
+ pw.println(" queryable via uses-library:");
+ dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", expandPackages);
}
private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId,
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index b34611b9cd6f..29322e2553e9 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -180,6 +180,8 @@ public class DataLoaderManagerService extends SystemService {
mId = id;
mListener = listener;
mDataLoader = null;
+
+ callListener(IDataLoaderStatusListener.DATA_LOADER_BINDING);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 82b12aac0a11..bafe987cb546 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1011,8 +1011,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"DataLoader installation of APEX modules is not allowed.");
}
- if (this.params.dataLoaderParams.getComponentName().getPackageName()
- == SYSTEM_DATA_LOADER_PACKAGE && mContext.checkCallingOrSelfPermission(
+ boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals(
+ this.params.dataLoaderParams.getComponentName().getPackageName());
+ if (systemDataLoader && mContext.checkCallingOrSelfPermission(
Manifest.permission.USE_SYSTEM_DATA_LOADERS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("You need the "
@@ -3653,6 +3654,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public void onStatusChanged(int dataLoaderId, int status) {
switch (status) {
+ case IDataLoaderStatusListener.DATA_LOADER_BINDING:
case IDataLoaderStatusListener.DATA_LOADER_STOPPED:
case IDataLoaderStatusListener.DATA_LOADER_DESTROYED:
return;
@@ -3763,8 +3765,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
- final boolean systemDataLoader =
- params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE;
+ final boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals(
+ params.getComponentName().getPackageName());
final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f828a119f988..dfe72b26f72a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -410,6 +410,7 @@ import com.android.server.utils.Watched;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedLongSparseArray;
import com.android.server.utils.WatchedSparseBooleanArray;
+import com.android.server.utils.WatchedSparseIntArray;
import com.android.server.utils.Watcher;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -892,7 +893,7 @@ public class PackageManagerService extends IPackageManager.Stub
// that created the isolated process.
@Watched
@GuardedBy("mLock")
- final SparseIntArray mIsolatedOwners = new SparseIntArray();
+ final WatchedSparseIntArray mIsolatedOwners = new WatchedSparseIntArray();
/**
* Tracks new system packages [received in an OTA] that we expect to
@@ -1795,7 +1796,7 @@ public class PackageManagerService extends IPackageManager.Stub
public static final int SNAPPED = 2;
public final Settings settings;
- public final SparseIntArray isolatedOwners;
+ public final WatchedSparseIntArray isolatedOwners;
public final WatchedArrayMap<String, AndroidPackage> packages;
public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibs;
public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> staticLibs;
@@ -1814,7 +1815,7 @@ public class PackageManagerService extends IPackageManager.Stub
Snapshot(int type) {
if (type == Snapshot.SNAPPED) {
settings = mSettings.snapshot();
- isolatedOwners = mIsolatedOwners.clone();
+ isolatedOwners = mIsolatedOwners.snapshot();
packages = mPackages.snapshot();
sharedLibs = mSharedLibraries.snapshot();
staticLibs = mStaticLibsByDeclaringPackage.snapshot();
@@ -2016,7 +2017,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Cached attributes. The names in this class are the same as the
// names in PackageManagerService; see that class for documentation.
private final Settings mSettings;
- private final SparseIntArray mIsolatedOwners;
+ private final WatchedSparseIntArray mIsolatedOwners;
private final WatchedArrayMap<String, AndroidPackage> mPackages;
private final WatchedArrayMap<ComponentName, ParsedInstrumentation>
mInstrumentation;
@@ -3551,7 +3552,7 @@ public class PackageManagerService extends IPackageManager.Stub
public String getInstantAppPackageName(int callingUid) {
// If the caller is an isolated app use the owner's uid for the lookup.
if (Process.isIsolated(callingUid)) {
- callingUid = mIsolatedOwners.get(callingUid);
+ callingUid = getIsolatedOwner(callingUid);
}
final int appId = UserHandle.getAppId(callingUid);
final Object obj = mSettings.getSettingLPr(appId);
@@ -3563,6 +3564,19 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
+ /**
+ * Finds the owner for the provided isolated UID. Throws IllegalStateException if no such
+ * isolated UID is found.
+ */
+ private int getIsolatedOwner(int isolatedUid) {
+ final int ownerUid = mIsolatedOwners.get(isolatedUid, -1);
+ if (ownerUid == -1) {
+ throw new IllegalStateException(
+ "No owner UID found for isolated UID " + isolatedUid);
+ }
+ return ownerUid;
+ }
+
public String resolveExternalPackageNameLPr(AndroidPackage pkg) {
if (pkg.getStaticSharedLibName() != null) {
return pkg.getManifestPackageName();
@@ -3929,7 +3943,7 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
int callingUid) {
if (Process.isIsolated(callingUid)) {
- callingUid = mIsolatedOwners.get(callingUid);
+ callingUid = getIsolatedOwner(callingUid);
}
final PackageSetting ps = mSettings.getPackageLPr(packageName);
final boolean returnAllowed =
@@ -4083,7 +4097,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Nullable ComponentName component, @ComponentType int componentType, int userId) {
// if we're in an isolated process, get the real calling UID
if (Process.isIsolated(callingUid)) {
- callingUid = mIsolatedOwners.get(callingUid);
+ callingUid = getIsolatedOwner(callingUid);
}
final String instantAppPkgName = getInstantAppPackageName(callingUid);
final boolean callerIsInstantApp = instantAppPkgName != null;
@@ -6164,6 +6178,7 @@ public class PackageManagerService extends IPackageManager.Stub
mAppsFilter.registerObserver(mWatcher);
mInstantAppRegistry.registerObserver(mWatcher);
mSettings.registerObserver(mWatcher);
+ mIsolatedOwners.registerObserver(mWatcher);
// If neither "build" attribute is true then this may be a mockito test, and verification
// can fail as a false positive.
Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
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 0c2b4c547dae..b4bcde726173 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
@@ -80,14 +80,14 @@ public interface DomainVerificationManagerInternal {
* been preserved for migration purposes, but is otherwise ignored. Corresponds to
* {@link PackageManager#INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS}.
*/
- int APPROVAL_LEVEL_LEGACY_ALWAYS = 1;
+ int APPROVAL_LEVEL_LEGACY_ALWAYS = 2;
/**
* The app has been chosen by the user through
* {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)},
* indicating an explicit choice to use this app to open an unverified domain.
*/
- int APPROVAL_LEVEL_SELECTION = 2;
+ int APPROVAL_LEVEL_SELECTION = 3;
/**
* The app is approved through the digital asset link statement being hosted at the domain
@@ -95,7 +95,7 @@ public interface DomainVerificationManagerInternal {
* {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)} by
* the domain verification agent on device.
*/
- int APPROVAL_LEVEL_VERIFIED = 3;
+ int APPROVAL_LEVEL_VERIFIED = 4;
/**
* The app has been installed as an instant app, which grants it total authority on the domains
@@ -105,7 +105,7 @@ public interface DomainVerificationManagerInternal {
* The user is still able to disable instant app link handling through
* {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, boolean)}.
*/
- int APPROVAL_LEVEL_INSTANT_APP = 4;
+ int APPROVAL_LEVEL_INSTANT_APP = 5;
/**
* Defines the possible values for {@link #approvalLevelForDomain(PackageSetting, Intent, int)}
diff --git a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
new file mode 100644
index 000000000000..c8c828f10ad3
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
@@ -0,0 +1,344 @@
+/*
+ * 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.server.timezonedetector;
+
+import static libcore.io.IoUtils.closeQuietly;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A class that provides time zone detector state information for metrics.
+ *
+ * <p>
+ * Regarding time zone ID ordinals:
+ * <p>
+ * We don't want to leak user location information by reporting time zone IDs. Instead, time zone
+ * IDs are consistently identified within a given instance of this class by a numeric ID. This
+ * allows comparison of IDs without revealing what those IDs are.
+ */
+public final class MetricsTimeZoneDetectorState {
+
+ @IntDef(prefix = "DETECTION_MODE_",
+ value = { DETECTION_MODE_MANUAL, DETECTION_MODE_GEO, DETECTION_MODE_TELEPHONY})
+ @interface DetectionMode {};
+
+ @DetectionMode
+ public static final int DETECTION_MODE_MANUAL = 0;
+ @DetectionMode
+ public static final int DETECTION_MODE_GEO = 1;
+ @DetectionMode
+ public static final int DETECTION_MODE_TELEPHONY = 2;
+
+ @NonNull
+ private final ConfigurationInternal mConfigurationInternal;
+ @NonNull
+ private final int mDeviceTimeZoneIdOrdinal;
+ @Nullable
+ private final MetricsTimeZoneSuggestion mLatestManualSuggestion;
+ @Nullable
+ private final MetricsTimeZoneSuggestion mLatestTelephonySuggestion;
+ @Nullable
+ private final MetricsTimeZoneSuggestion mLatestGeolocationSuggestion;
+
+ private MetricsTimeZoneDetectorState(
+ @NonNull ConfigurationInternal configurationInternal,
+ int deviceTimeZoneIdOrdinal,
+ @Nullable MetricsTimeZoneSuggestion latestManualSuggestion,
+ @Nullable MetricsTimeZoneSuggestion latestTelephonySuggestion,
+ @Nullable MetricsTimeZoneSuggestion latestGeolocationSuggestion) {
+ mConfigurationInternal = Objects.requireNonNull(configurationInternal);
+ mDeviceTimeZoneIdOrdinal = deviceTimeZoneIdOrdinal;
+ mLatestManualSuggestion = latestManualSuggestion;
+ mLatestTelephonySuggestion = latestTelephonySuggestion;
+ mLatestGeolocationSuggestion = latestGeolocationSuggestion;
+ }
+
+ /**
+ * Creates {@link MetricsTimeZoneDetectorState} from the supplied parameters, using the {@link
+ * OrdinalGenerator} to generate time zone ID ordinals.
+ */
+ public static MetricsTimeZoneDetectorState create(
+ @NonNull OrdinalGenerator<String> tzIdOrdinalGenerator,
+ @NonNull ConfigurationInternal configurationInternal,
+ @NonNull String deviceTimeZoneId,
+ @Nullable ManualTimeZoneSuggestion latestManualSuggestion,
+ @Nullable TelephonyTimeZoneSuggestion latestTelephonySuggestion,
+ @Nullable GeolocationTimeZoneSuggestion latestGeolocationSuggestion) {
+
+ // TODO(b/172934905) Add logic to canonicalize the time zone IDs to Android's preferred IDs
+ // so that the ordinals will match even when the ID is not identical, just equivalent.
+ int deviceTimeZoneIdOrdinal =
+ tzIdOrdinalGenerator.ordinal(Objects.requireNonNull(deviceTimeZoneId));
+ MetricsTimeZoneSuggestion latestObfuscatedManualSuggestion =
+ createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestManualSuggestion);
+ MetricsTimeZoneSuggestion latestObfuscatedTelephonySuggestion =
+ createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestTelephonySuggestion);
+ MetricsTimeZoneSuggestion latestObfuscatedGeolocationSuggestion =
+ createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestGeolocationSuggestion);
+
+ return new MetricsTimeZoneDetectorState(
+ configurationInternal, deviceTimeZoneIdOrdinal, latestObfuscatedManualSuggestion,
+ latestObfuscatedTelephonySuggestion, latestObfuscatedGeolocationSuggestion);
+ }
+
+ /** Returns true if the device supports telephony time zone detection. */
+ public boolean isTelephonyDetectionSupported() {
+ return mConfigurationInternal.isTelephonyDetectionSupported();
+ }
+
+ /** Returns true if the device supports geolocation time zone detection. */
+ public boolean isGeoDetectionSupported() {
+ return mConfigurationInternal.isGeoDetectionSupported();
+ }
+
+ /** Returns true if user's location can be used generally. */
+ public boolean isUserLocationEnabled() {
+ return mConfigurationInternal.isLocationEnabled();
+ }
+
+ /** Returns the value of the geolocation time zone detection enabled setting. */
+ public boolean getGeoDetectionEnabledSetting() {
+ return mConfigurationInternal.getGeoDetectionEnabledSetting();
+ }
+
+ /** Returns the value of the auto time zone detection enabled setting. */
+ public boolean getAutoDetectionEnabledSetting() {
+ return mConfigurationInternal.getAutoDetectionEnabledSetting();
+ }
+
+ /**
+ * Returns the detection mode the device is currently using, which can be influenced by various
+ * things besides the user's setting.
+ */
+ @DetectionMode
+ public int getDetectionMode() {
+ if (!mConfigurationInternal.getAutoDetectionEnabledBehavior()) {
+ return DETECTION_MODE_MANUAL;
+ } else if (mConfigurationInternal.getGeoDetectionEnabledBehavior()) {
+ return DETECTION_MODE_GEO;
+ } else {
+ return DETECTION_MODE_TELEPHONY;
+ }
+ }
+
+ /**
+ * Returns the ordinal for the device's currently set time zone ID.
+ * See {@link MetricsTimeZoneDetectorState} for information about ordinals.
+ */
+ @NonNull
+ public int getDeviceTimeZoneIdOrdinal() {
+ return mDeviceTimeZoneIdOrdinal;
+ }
+
+ /**
+ * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last manual
+ * suggestion received.
+ */
+ @Nullable
+ public byte[] getLatestManualSuggestionProtoBytes() {
+ return suggestionProtoBytes(mLatestManualSuggestion);
+ }
+
+ /**
+ * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last, best
+ * telephony suggestion received.
+ */
+ @Nullable
+ public byte[] getLatestTelephonySuggestionProtoBytes() {
+ return suggestionProtoBytes(mLatestTelephonySuggestion);
+ }
+
+ /**
+ * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last geolocation
+ * suggestion received.
+ */
+ @Nullable
+ public byte[] getLatestGeolocationSuggestionProtoBytes() {
+ return suggestionProtoBytes(mLatestGeolocationSuggestion);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ MetricsTimeZoneDetectorState that = (MetricsTimeZoneDetectorState) o;
+ return mDeviceTimeZoneIdOrdinal == that.mDeviceTimeZoneIdOrdinal
+ && mConfigurationInternal.equals(that.mConfigurationInternal)
+ && Objects.equals(mLatestManualSuggestion, that.mLatestManualSuggestion)
+ && Objects.equals(mLatestTelephonySuggestion, that.mLatestTelephonySuggestion)
+ && Objects.equals(mLatestGeolocationSuggestion, that.mLatestGeolocationSuggestion);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal,
+ mLatestManualSuggestion, mLatestTelephonySuggestion, mLatestGeolocationSuggestion);
+ }
+
+ @Override
+ public String toString() {
+ return "MetricsTimeZoneDetectorState{"
+ + "mConfigurationInternal=" + mConfigurationInternal
+ + ", mDeviceTimeZoneIdOrdinal=" + mDeviceTimeZoneIdOrdinal
+ + ", mLatestManualSuggestion=" + mLatestManualSuggestion
+ + ", mLatestTelephonySuggestion=" + mLatestTelephonySuggestion
+ + ", mLatestGeolocationSuggestion=" + mLatestGeolocationSuggestion
+ + '}';
+ }
+
+ private static byte[] suggestionProtoBytes(
+ @Nullable MetricsTimeZoneSuggestion suggestion) {
+ if (suggestion == null) {
+ return null;
+ }
+ return suggestion.toBytes();
+ }
+
+ @Nullable
+ private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
+ @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
+ @NonNull ManualTimeZoneSuggestion manualSuggestion) {
+ if (manualSuggestion == null) {
+ return null;
+ }
+
+ int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(manualSuggestion.getZoneId());
+ return MetricsTimeZoneSuggestion.createCertain(
+ new int[] { zoneIdOrdinal });
+ }
+
+ @Nullable
+ private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
+ @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
+ @NonNull TelephonyTimeZoneSuggestion telephonySuggestion) {
+ if (telephonySuggestion == null) {
+ return null;
+ }
+ if (telephonySuggestion.getZoneId() == null) {
+ return MetricsTimeZoneSuggestion.createUncertain();
+ }
+ int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(telephonySuggestion.getZoneId());
+ return MetricsTimeZoneSuggestion.createCertain(new int[] { zoneIdOrdinal });
+ }
+
+ @Nullable
+ private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
+ @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
+ @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion) {
+ if (geolocationSuggestion == null) {
+ return null;
+ }
+
+ List<String> zoneIds = geolocationSuggestion.getZoneIds();
+ if (zoneIds == null) {
+ return MetricsTimeZoneSuggestion.createUncertain();
+ }
+ return MetricsTimeZoneSuggestion.createCertain(zoneIdOrdinalGenerator.ordinals(zoneIds));
+ }
+
+ /**
+ * A Java class that closely matches the android.app.time.MetricsTimeZoneSuggestion
+ * proto definition.
+ */
+ private static final class MetricsTimeZoneSuggestion {
+ @Nullable
+ private final int[] mZoneIdOrdinals;
+
+ MetricsTimeZoneSuggestion(@Nullable int[] zoneIdOrdinals) {
+ mZoneIdOrdinals = zoneIdOrdinals;
+ }
+
+ @NonNull
+ static MetricsTimeZoneSuggestion createUncertain() {
+ return new MetricsTimeZoneSuggestion(null);
+ }
+
+ public static MetricsTimeZoneSuggestion createCertain(
+ @NonNull int[] zoneIdOrdinals) {
+ return new MetricsTimeZoneSuggestion(zoneIdOrdinals);
+ }
+
+ boolean isCertain() {
+ return mZoneIdOrdinals != null;
+ }
+
+ @Nullable
+ int[] getZoneIdOrdinals() {
+ return mZoneIdOrdinals;
+ }
+
+ byte[] toBytes() {
+ // We don't get access to the atoms.proto definition for nested proto fields, so we use
+ // an identically specified proto.
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ ProtoOutputStream protoOutputStream = new ProtoOutputStream(byteArrayOutputStream);
+ int typeProtoValue = isCertain()
+ ? android.app.time.MetricsTimeZoneSuggestion.CERTAIN
+ : android.app.time.MetricsTimeZoneSuggestion.UNCERTAIN;
+ protoOutputStream.write(android.app.time.MetricsTimeZoneSuggestion.TYPE,
+ typeProtoValue);
+ if (isCertain()) {
+ for (int zoneIdOrdinal : getZoneIdOrdinals()) {
+ protoOutputStream.write(
+ android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_ORDINALS,
+ zoneIdOrdinal);
+ }
+ }
+ protoOutputStream.flush();
+ closeQuietly(byteArrayOutputStream);
+ return byteArrayOutputStream.toByteArray();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ MetricsTimeZoneSuggestion that = (MetricsTimeZoneSuggestion) o;
+ return Arrays.equals(mZoneIdOrdinals, that.mZoneIdOrdinals);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(mZoneIdOrdinals);
+ }
+
+ @Override
+ public String toString() {
+ return "MetricsTimeZoneSuggestion{"
+ + "mZoneIdOrdinals=" + Arrays.toString(mZoneIdOrdinals)
+ + '}';
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java b/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java
new file mode 100644
index 000000000000..a448773c40d5
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java
@@ -0,0 +1,49 @@
+/*
+ * 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.server.timezonedetector;
+
+import android.util.ArraySet;
+
+import java.util.List;
+
+/**
+ * A helper class that turns a set of objects into ordinal values, i.e. each object is offered
+ * up via {@link #ordinal(Object)} or similar method, and a number will be returned. If the
+ * object has been seen before by the instance then the same number will be returned. Intended
+ * for situations where it is useful to know if values from some finite set are the same or
+ * different, but the value is either large or may reveal PII. This class relies on {@link
+ * Object#equals(Object)} and {@link Object#hashCode()}.
+ */
+class OrdinalGenerator<T> {
+ private final ArraySet<T> mKnownIds = new ArraySet<>();
+
+ int ordinal(T object) {
+ int ordinal = mKnownIds.indexOf(object);
+ if (ordinal < 0) {
+ ordinal = mKnownIds.size();
+ mKnownIds.add(object);
+ }
+ return ordinal;
+ }
+
+ int[] ordinals(List<T> objects) {
+ int[] ordinals = new int[objects.size()];
+ for (int i = 0; i < ordinals.length; i++) {
+ ordinals[i] = ordinal(objects.get(i));
+ }
+ return ordinals;
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index cd220b164851..d429b8762a7c 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -50,4 +50,8 @@ public interface TimeZoneDetectorInternal extends Dumpable.Container {
* available, and so on. This method may be implemented asynchronously.
*/
void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion);
+
+ /** Generates a state snapshot for metrics. */
+ @NonNull
+ MetricsTimeZoneDetectorState generateMetricsState();
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index 7ba20eee0392..4e78f5aa444c 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -90,4 +90,10 @@ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInter
mHandler.post(
() -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion));
}
+
+ @Override
+ @NonNull
+ public MetricsTimeZoneDetectorState generateMetricsState() {
+ return mTimeZoneDetectorStrategy.generateMetricsState();
+ }
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 8266f121822e..e3f31b6aa326 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -66,9 +66,10 @@ import android.util.IndentingPrintWriter;
* <p>Threading:
*
* <p>Suggestion calls with a void return type may be handed off to a separate thread and handled
- * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()}, and debug
- * calls like {@link #dump(IndentingPrintWriter, String[])}, may be called on a different thread
- * concurrently with other operations.
+ * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()},
+ * {@link #generateMetricsState()} and debug calls like {@link
+ * #dump(IndentingPrintWriter, String[])}, may be called on a different thread concurrently with
+ * other operations.
*
* @hide
*/
@@ -123,4 +124,8 @@ public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container {
* suggestion.
*/
void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion);
+
+ /** Generates a state snapshot for metrics. */
+ @NonNull
+ MetricsTimeZoneDetectorState generateMetricsState();
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index 2e96a1065af4..5d34dd7daffb 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -371,6 +371,28 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
}
}
+ @Override
+ @NonNull
+ public synchronized MetricsTimeZoneDetectorState generateMetricsState() {
+ int currentUserId = mEnvironment.getCurrentUserId();
+ // Just capture one telephony suggestion: the one that would be used right now if telephony
+ // detection is in use.
+ QualifiedTelephonyTimeZoneSuggestion bestQualifiedTelephonySuggestion =
+ findBestTelephonySuggestion();
+ TelephonyTimeZoneSuggestion telephonySuggestion =
+ bestQualifiedTelephonySuggestion == null
+ ? null : bestQualifiedTelephonySuggestion.suggestion;
+ // A new generator is created each time: we don't want / require consistency.
+ OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>();
+ return MetricsTimeZoneDetectorState.create(
+ tzIdOrdinalGenerator,
+ getConfigurationInternal(currentUserId),
+ mEnvironment.getDeviceTimeZone(),
+ getLatestManualSuggestion(),
+ telephonySuggestion,
+ getLatestGeolocationSuggestion());
+ }
+
private static int scoreTelephonySuggestion(@NonNull TelephonyTimeZoneSuggestion suggestion) {
int score;
if (suggestion.getZoneId() == null) {
diff --git a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
index 417a6368be4c..4fa920e5b7d2 100644
--- a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
@@ -42,10 +42,12 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
@NonNull private final LocationTimeZoneProviderProxy mProxy;
BinderLocationTimeZoneProvider(
+ @NonNull ProviderMetricsLogger providerMetricsLogger,
@NonNull ThreadingDomain threadingDomain,
@NonNull String providerName,
@NonNull LocationTimeZoneProviderProxy proxy) {
- super(threadingDomain, providerName, new ZoneInfoDbTimeZoneIdValidator());
+ super(providerMetricsLogger, threadingDomain, providerName,
+ new ZoneInfoDbTimeZoneIdValidator());
mProxy = Objects.requireNonNull(proxy);
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index 588382158bc9..ca4a6408cfbb 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -45,6 +45,7 @@ import com.android.server.FgThread;
import com.android.server.SystemService;
import com.android.server.timezonedetector.ServiceConfigAccessor;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -297,7 +298,9 @@ public class LocationTimeZoneManagerService extends Binder {
R.string.config_primaryLocationTimeZoneProviderPackageName
);
}
- return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
+ ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(0);
+ return new BinderLocationTimeZoneProvider(
+ providerMetricsLogger, mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
}
@NonNull
@@ -317,7 +320,9 @@ public class LocationTimeZoneManagerService extends Binder {
R.string.config_secondaryLocationTimeZoneProviderPackageName
);
}
- return new BinderLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
+ ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(1);
+ return new BinderLocationTimeZoneProvider(
+ providerMetricsLogger, mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
}
/** Used for bug triage and in tests to simulate provider events. */
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
index b97c838017eb..cc815dc61886 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
@@ -98,6 +98,14 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
/**
+ * Listener interface used to log provider events for metrics.
+ */
+ interface ProviderMetricsLogger {
+ /** Logs that a provider changed state. */
+ void onProviderStateChanged(@ProviderStateEnum int stateEnum);
+ }
+
+ /**
* Information about the provider's current state.
*/
static class ProviderState {
@@ -349,6 +357,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
}
+ @NonNull private final ProviderMetricsLogger mProviderMetricsLogger;
@NonNull final ThreadingDomain mThreadingDomain;
@NonNull final Object mSharedLock;
@NonNull final String mProviderName;
@@ -380,10 +389,12 @@ abstract class LocationTimeZoneProvider implements Dumpable {
@NonNull private TimeZoneIdValidator mTimeZoneIdValidator;
/** Creates the instance. */
- LocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
+ LocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger,
+ @NonNull ThreadingDomain threadingDomain,
@NonNull String providerName,
@NonNull TimeZoneIdValidator timeZoneIdValidator) {
mThreadingDomain = Objects.requireNonNull(threadingDomain);
+ mProviderMetricsLogger = Objects.requireNonNull(providerMetricsLogger);
mInitializationTimeoutQueue = threadingDomain.createSingleRunnableQueue();
mSharedLock = threadingDomain.getLockObject();
mProviderName = Objects.requireNonNull(providerName);
@@ -485,6 +496,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
mCurrentState.set(newState);
onSetCurrentState(newState);
if (!Objects.equals(newState, oldState)) {
+ mProviderMetricsLogger.onProviderStateChanged(newState.stateEnum);
if (mStateChangeRecording) {
mRecordedStates.add(newState);
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
new file mode 100644
index 000000000000..dfff6f2dd5ae
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
@@ -0,0 +1,42 @@
+/*
+ * 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.server.timezonedetector.location;
+
+import android.annotation.IntRange;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
+
+/**
+ * The real implementation of {@link ProviderMetricsLogger} which logs using
+ * {@link FrameworkStatsLog}.
+ */
+public class RealProviderMetricsLogger implements ProviderMetricsLogger {
+
+ @IntRange(from = 0, to = 1)
+ private final int mProviderIndex;
+
+ public RealProviderMetricsLogger(@IntRange(from = 0, to = 1) int providerIndex) {
+ mProviderIndex = providerIndex;
+ }
+
+ @Override
+ public void onProviderStateChanged(@ProviderStateEnum int stateEnum) {
+ // TODO(b/172934905): Implement once the atom has landed.
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index eaba083c551c..e3dc70b41c0d 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -54,50 +54,6 @@ final class VibratorController {
void onComplete(int vibratorId, long vibrationId);
}
- /**
- * Initializes the native part of this controller, creating a global reference to given
- * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This
- * wrapper is responsible for deleting this pointer by calling the method pointed
- * by {@link #vibratorGetFinalizer()}.
- *
- * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener}
- * do not hold any strong reference to the instance responsible for deleting the returned
- * pointer, to avoid creating a cyclic GC root reference.
- */
- static native long vibratorInit(int vibratorId, OnVibrationCompleteListener listener);
-
- /**
- * Returns pointer to native function responsible for cleaning up the native pointer allocated
- * and returned by {@link #vibratorInit(int, OnVibrationCompleteListener)}.
- */
- static native long vibratorGetFinalizer();
-
- static native boolean vibratorIsAvailable(long nativePtr);
-
- static native void vibratorOn(long nativePtr, long milliseconds, long vibrationId);
-
- static native void vibratorOff(long nativePtr);
-
- static native void vibratorSetAmplitude(long nativePtr, int amplitude);
-
- static native int[] vibratorGetSupportedEffects(long nativePtr);
-
- static native int[] vibratorGetSupportedPrimitives(long nativePtr);
-
- static native long vibratorPerformEffect(
- long nativePtr, long effect, long strength, long vibrationId);
-
- static native long vibratorPerformComposedEffect(
- long nativePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId);
-
- static native void vibratorSetExternalControl(long nativePtr, boolean enabled);
-
- static native long vibratorGetCapabilities(long nativePtr);
-
- static native void vibratorAlwaysOnEnable(long nativePtr, long id, long effect, long strength);
-
- static native void vibratorAlwaysOnDisable(long nativePtr, long id);
-
VibratorController(int vibratorId, OnVibrationCompleteListener listener) {
this(vibratorId, listener, new NativeWrapper());
}
@@ -109,7 +65,8 @@ final class VibratorController {
mNativeWrapper.init(vibratorId, listener);
mVibratorInfo = new VibratorInfo(vibratorId, nativeWrapper.getCapabilities(),
- nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives());
+ nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives(),
+ nativeWrapper.getResonantFrequency(), nativeWrapper.getQFactor());
}
/** Register state listener for this vibrator. */
@@ -336,13 +293,47 @@ final class VibratorController {
/** Wrapper around the static-native methods of {@link VibratorController} for tests. */
@VisibleForTesting
public static class NativeWrapper {
+ /**
+ * Initializes the native part of this controller, creating a global reference to given
+ * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This
+ * wrapper is responsible for deleting this pointer by calling the method pointed
+ * by {@link #getNativeFinalizer()}.
+ *
+ * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener}
+ * do not hold any strong reference to the instance responsible for deleting the returned
+ * pointer, to avoid creating a cyclic GC root reference.
+ */
+ private static native long nativeInit(int vibratorId, OnVibrationCompleteListener listener);
+
+ /**
+ * Returns pointer to native function responsible for cleaning up the native pointer
+ * allocated and returned by {@link #nativeInit(int, OnVibrationCompleteListener)}.
+ */
+ private static native long getNativeFinalizer();
+ private static native boolean isAvailable(long nativePtr);
+ private static native void on(long nativePtr, long milliseconds, long vibrationId);
+ private static native void off(long nativePtr);
+ private static native void setAmplitude(long nativePtr, int amplitude);
+ private static native int[] getSupportedEffects(long nativePtr);
+ private static native int[] getSupportedPrimitives(long nativePtr);
+ private static native long performEffect(
+ long nativePtr, long effect, long strength, long vibrationId);
+ private static native long performComposedEffect(long nativePtr,
+ VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId);
+ private static native void setExternalControl(long nativePtr, boolean enabled);
+ private static native long getCapabilities(long nativePtr);
+ private static native void alwaysOnEnable(long nativePtr, long id, long effect,
+ long strength);
+ private static native void alwaysOnDisable(long nativePtr, long id);
+ private static native float getResonantFrequency(long nativePtr);
+ private static native float getQFactor(long nativePtr);
private long mNativePtr = 0;
/** Initializes native controller and allocation registry to destroy native instances. */
public void init(int vibratorId, OnVibrationCompleteListener listener) {
- mNativePtr = VibratorController.vibratorInit(vibratorId, listener);
- long finalizerPtr = VibratorController.vibratorGetFinalizer();
+ mNativePtr = nativeInit(vibratorId, listener);
+ long finalizerPtr = getNativeFinalizer();
if (finalizerPtr != 0) {
NativeAllocationRegistry registry =
@@ -354,65 +345,73 @@ final class VibratorController {
/** Check if the vibrator is currently available. */
public boolean isAvailable() {
- return VibratorController.vibratorIsAvailable(mNativePtr);
+ return isAvailable(mNativePtr);
}
/** Turns vibrator on for given time. */
public void on(long milliseconds, long vibrationId) {
- VibratorController.vibratorOn(mNativePtr, milliseconds, vibrationId);
+ on(mNativePtr, milliseconds, vibrationId);
}
/** Turns vibrator off. */
public void off() {
- VibratorController.vibratorOff(mNativePtr);
+ off(mNativePtr);
}
/** Sets the amplitude for the vibrator to run. */
public void setAmplitude(int amplitude) {
- VibratorController.vibratorSetAmplitude(mNativePtr, amplitude);
+ setAmplitude(mNativePtr, amplitude);
}
/** Returns all predefined effects supported by the device vibrator. */
public int[] getSupportedEffects() {
- return VibratorController.vibratorGetSupportedEffects(mNativePtr);
+ return getSupportedEffects(mNativePtr);
}
/** Returns all compose primitives supported by the device vibrator. */
public int[] getSupportedPrimitives() {
- return VibratorController.vibratorGetSupportedPrimitives(mNativePtr);
+ return getSupportedPrimitives(mNativePtr);
}
/** Turns vibrator on to perform one of the supported effects. */
public long perform(long effect, long strength, long vibrationId) {
- return VibratorController.vibratorPerformEffect(
- mNativePtr, effect, strength, vibrationId);
+ return performEffect(mNativePtr, effect, strength, vibrationId);
}
/** Turns vibrator on to perform one of the supported composed effects. */
public long compose(
VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) {
- return VibratorController.vibratorPerformComposedEffect(mNativePtr, effect,
- vibrationId);
+ return performComposedEffect(mNativePtr, effect, vibrationId);
}
/** Enabled the device vibrator to be controlled by another service. */
public void setExternalControl(boolean enabled) {
- VibratorController.vibratorSetExternalControl(mNativePtr, enabled);
+ setExternalControl(mNativePtr, enabled);
}
/** Returns all capabilities of the device vibrator. */
public long getCapabilities() {
- return VibratorController.vibratorGetCapabilities(mNativePtr);
+ return getCapabilities(mNativePtr);
}
/** Enable always-on vibration with given id and effect. */
public void alwaysOnEnable(long id, long effect, long strength) {
- VibratorController.vibratorAlwaysOnEnable(mNativePtr, id, effect, strength);
+ alwaysOnEnable(mNativePtr, id, effect, strength);
}
/** Disable always-on vibration for given id. */
public void alwaysOnDisable(long id) {
- VibratorController.vibratorAlwaysOnDisable(mNativePtr, id);
+ alwaysOnDisable(mNativePtr, id);
+ }
+
+ /** Gets the vibrator's resonant frequency (F0) */
+ public float getResonantFrequency() {
+ return getResonantFrequency(mNativePtr);
+ }
+
+ /** Gets the vibrator's Q factor */
+ public float getQFactor() {
+ return getQFactor(mNativePtr);
}
}
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 385dc79567fa..5d22f8fde057 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -884,6 +884,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mPendingTaskEvents.remove(pending);
}
mPendingTaskEvents.add(pending);
+ mService.mWindowManager.mWindowPlacerLocked.requestTraversal();
return true;
}
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index ef2d0baff031..f60b35499013 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -32,8 +32,6 @@
#include "com_android_server_vibrator_VibratorManagerService.h"
namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
namespace V1_3 = android::hardware::vibrator::V1_3;
namespace aidl = android::hardware::vibrator;
@@ -85,10 +83,11 @@ static std::shared_ptr<vibrator::HalController> findVibrator(int32_t vibratorId)
class VibratorControllerWrapper {
public:
VibratorControllerWrapper(JNIEnv* env, int32_t vibratorId, jobject callbackListener)
- : mHal(std::move(findVibrator(vibratorId))),
+ : mHal(findVibrator(vibratorId)),
mVibratorId(vibratorId),
mCallbackListener(env->NewGlobalRef(callbackListener)) {
- LOG_ALWAYS_FATAL_IF(mHal == nullptr, "Unable to find reference to vibrator hal");
+ LOG_ALWAYS_FATAL_IF(mHal == nullptr,
+ "Failed to connect to vibrator HAL, or vibratorId is invalid");
LOG_ALWAYS_FATAL_IF(mCallbackListener == nullptr,
"Unable to create global reference to vibration callback handler");
}
@@ -130,15 +129,15 @@ static void destroyNativeWrapper(void* ptr) {
}
}
-static jlong vibratorInit(JNIEnv* env, jclass /* clazz */, jint vibratorId,
- jobject callbackListener) {
+static jlong vibratorNativeInit(JNIEnv* env, jclass /* clazz */, jint vibratorId,
+ jobject callbackListener) {
std::unique_ptr<VibratorControllerWrapper> wrapper =
std::make_unique<VibratorControllerWrapper>(env, vibratorId, callbackListener);
wrapper->hal()->init();
return reinterpret_cast<jlong>(wrapper.release());
}
-static jlong vibratorGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
+static jlong vibratorGetNativeFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeWrapper));
}
@@ -286,25 +285,46 @@ static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr,
wrapper->hal()->alwaysOnDisable(static_cast<int32_t>(id));
}
+static float vibratorGetResonantFrequency(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+ VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
+ if (wrapper == nullptr) {
+ ALOGE("vibratorGetResonantFrequency failed because native wrapper was not initialized");
+ return NAN;
+ }
+ auto result = wrapper->hal()->getResonantFrequency();
+ return result.isOk() ? static_cast<jfloat>(result.value()) : NAN;
+}
+
+static float vibratorGetQFactor(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+ VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
+ if (wrapper == nullptr) {
+ ALOGE("vibratorGetQFactor failed because native wrapper was not initialized");
+ return NAN;
+ }
+ auto result = wrapper->hal()->getQFactor();
+ return result.isOk() ? static_cast<jfloat>(result.value()) : NAN;
+}
+
static const JNINativeMethod method_table[] = {
- {"vibratorInit",
+ {"nativeInit",
"(ILcom/android/server/vibrator/VibratorController$OnVibrationCompleteListener;)J",
- (void*)vibratorInit},
- {"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer},
- {"vibratorIsAvailable", "(J)Z", (void*)vibratorIsAvailable},
- {"vibratorOn", "(JJJ)V", (void*)vibratorOn},
- {"vibratorOff", "(J)V", (void*)vibratorOff},
- {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
- {"vibratorPerformEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
- {"vibratorPerformComposedEffect",
- "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)J",
+ (void*)vibratorNativeInit},
+ {"getNativeFinalizer", "()J", (void*)vibratorGetNativeFinalizer},
+ {"isAvailable", "(J)Z", (void*)vibratorIsAvailable},
+ {"on", "(JJJ)V", (void*)vibratorOn},
+ {"off", "(J)V", (void*)vibratorOff},
+ {"setAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
+ {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
+ {"performComposedEffect", "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)J",
(void*)vibratorPerformComposedEffect},
- {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
- {"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
- {"vibratorSetExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
- {"vibratorGetCapabilities", "(J)J", (void*)vibratorGetCapabilities},
- {"vibratorAlwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
- {"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
+ {"getSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
+ {"getSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
+ {"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
+ {"getCapabilities", "(J)J", (void*)vibratorGetCapabilities},
+ {"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
+ {"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
+ {"getResonantFrequency", "(J)F", (void*)vibratorGetResonantFrequency},
+ {"getQFactor", "(J)F", (void*)vibratorGetQFactor},
};
int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) {
@@ -320,7 +340,8 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env
sPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F");
sPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I");
- return jniRegisterNativeMethods(env, "com/android/server/vibrator/VibratorController",
+ return jniRegisterNativeMethods(env,
+ "com/android/server/vibrator/VibratorController$NativeWrapper",
method_table, NELEM(method_table));
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 48ae8d672fb1..aed13b263a7f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -19,6 +19,8 @@ package com.android.server.devicepolicy;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.TEXT;
@@ -38,7 +40,6 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
-import android.util.Log;
import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -455,8 +456,7 @@ class ActiveAdmin {
try {
trustAgentInfo.options.saveToXml(out);
} catch (XmlPullParserException e) {
- Log.e(DevicePolicyManagerService.LOG_TAG,
- "Failed to save TrustAgent options", e);
+ Slog.e(LOG_TAG, e, "Failed to save TrustAgent options");
}
out.endTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
}
@@ -629,8 +629,7 @@ class ActiveAdmin {
String tag = parser.getName();
if (TAG_POLICIES.equals(tag)) {
if (shouldOverridePolicies) {
- Log.d(DevicePolicyManagerService.LOG_TAG,
- "Overriding device admin policies from XML.");
+ Slog.d(LOG_TAG, "Overriding device admin policies from XML.");
info.readPoliciesFromXml(parser);
}
} else if (TAG_PASSWORD_QUALITY.equals(tag)) {
@@ -726,16 +725,14 @@ class ActiveAdmin {
if (type == TypedXmlPullParser.TEXT) {
shortSupportMessage = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading short support message");
+ Slog.w(LOG_TAG, "Missing text when loading short support message");
}
} else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) {
type = parser.next();
if (type == TypedXmlPullParser.TEXT) {
longSupportMessage = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading long support message");
+ Slog.w(LOG_TAG, "Missing text when loading long support message");
}
} else if (TAG_PARENT_ADMIN.equals(tag)) {
Preconditions.checkState(!isParent);
@@ -748,8 +745,7 @@ class ActiveAdmin {
if (type == TypedXmlPullParser.TEXT) {
organizationName = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading organization name");
+ Slog.w(LOG_TAG, "Missing text when loading organization name");
}
} else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
isLogoutEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false);
@@ -758,16 +754,14 @@ class ActiveAdmin {
if (type == TypedXmlPullParser.TEXT) {
startUserSessionMessage = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading start session message");
+ Slog.w(LOG_TAG, "Missing text when loading start session message");
}
} else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) {
type = parser.next();
if (type == TypedXmlPullParser.TEXT) {
endUserSessionMessage = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading end session message");
+ Slog.w(LOG_TAG, "Missing text when loading end session message");
}
} else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
mCrossProfileCalendarPackages = readPackageList(parser, tag);
@@ -802,16 +796,14 @@ class ActiveAdmin {
if (type == TypedXmlPullParser.TEXT) {
mOrganizationId = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing Organization ID.");
+ Slog.w(LOG_TAG, "Missing Organization ID.");
}
} else if (TAG_ENROLLMENT_SPECIFIC_ID.equals(tag)) {
type = parser.next();
if (type == TypedXmlPullParser.TEXT) {
mEnrollmentSpecificId = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing Enrollment-specific ID.");
+ Slog.w(LOG_TAG, "Missing Enrollment-specific ID.");
}
} else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) {
mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE,
@@ -820,7 +812,7 @@ class ActiveAdmin {
mUsbDataSignalingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE,
USB_DATA_SIGNALING_ENABLED_DEFAULT);
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag);
+ Slog.w(LOG_TAG, "Unknown admin tag: %s", tag);
XmlUtils.skipCurrentTag(parser);
}
}
@@ -842,12 +834,10 @@ class ActiveAdmin {
if (packageName != null) {
result.add(packageName);
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG,
- "Package name missing under " + outerTag);
+ Slog.w(LOG_TAG, "Package name missing under %s", outerTag);
}
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG,
- "Unknown tag under " + tag + ": " + outerTag);
+ Slog.w(LOG_TAG, "Unknown tag under %s: ", tag, outerTag);
}
}
return result;
@@ -868,8 +858,7 @@ class ActiveAdmin {
if (tag.equals(tagDAM)) {
result.add(parser.getAttributeValue(null, ATTR_VALUE));
} else {
- Slog.e(DevicePolicyManagerService.LOG_TAG,
- "Expected tag " + tag + " but found " + tagDAM);
+ Slog.e(LOG_TAG, "Expected tag %s but found %s", tag, tagDAM);
}
}
}
@@ -891,8 +880,7 @@ class ActiveAdmin {
final TrustAgentInfo trustAgentInfo = getTrustAgentInfo(parser, tag);
result.put(component, trustAgentInfo);
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG,
- "Unknown tag under " + tag + ": " + tagDAM);
+ Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
}
}
return result;
@@ -912,8 +900,7 @@ class ActiveAdmin {
if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) {
result.options = PersistableBundle.restoreFromXml(parser);
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG,
- "Unknown tag under " + tag + ": " + tagDAM);
+ Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
}
}
return result;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
index d812b8f7fadb..e0c5e328f8c7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
@@ -16,6 +16,8 @@
package com.android.server.devicepolicy;
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -33,7 +35,7 @@ import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
-import android.util.Log;
+import android.util.Slog;
import com.android.internal.R;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -47,7 +49,6 @@ import java.security.cert.X509Certificate;
import java.util.List;
public class CertificateMonitor {
- protected static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
protected static final int MONITORING_CERT_NOTIFICATION_ID = SystemMessage.NOTE_SSL_CERT_INFO;
private final DevicePolicyManagerService mService;
@@ -78,16 +79,16 @@ public class CertificateMonitor {
X509Certificate cert = parseCert(certBuffer);
pemCert = Credentials.convertToPem(cert);
} catch (CertificateException | IOException ce) {
- Log.e(LOG_TAG, "Problem converting cert", ce);
+ Slog.e(LOG_TAG, ce, "Problem converting cert");
return null;
}
try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) {
return keyChainConnection.getService().installCaCertificate(pemCert);
} catch (RemoteException e) {
- Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
+ Slog.e(LOG_TAG, e, "installCaCertsToKeyChain(): ");
} catch (InterruptedException e1) {
- Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
+ Slog.w(LOG_TAG, e1, "installCaCertsToKeyChain(): ");
Thread.currentThread().interrupt();
}
return null;
@@ -99,9 +100,9 @@ public class CertificateMonitor {
keyChainConnection.getService().deleteCaCertificate(aliases[i]);
}
} catch (RemoteException e) {
- Log.e(LOG_TAG, "from CaCertUninstaller: ", e);
+ Slog.e(LOG_TAG, e, "from CaCertUninstaller: ");
} catch (InterruptedException ie) {
- Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
+ Slog.w(LOG_TAG, ie, "CaCertUninstaller: ");
Thread.currentThread().interrupt();
}
}
@@ -137,7 +138,8 @@ public class CertificateMonitor {
};
private void updateInstalledCertificates(final UserHandle userHandle) {
- if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) {
+ final int userId = userHandle.getIdentifier();
+ if (!mInjector.getUserManager().isUserUnlocked(userId)) {
return;
}
@@ -145,7 +147,8 @@ public class CertificateMonitor {
try {
installedCerts = getInstalledCaCertificates(userHandle);
} catch (RemoteException | RuntimeException e) {
- Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
+ Slog.e(LOG_TAG, e, "Could not retrieve certificates from KeyChain service for user %d",
+ userId);
return;
}
mService.onInstalledCertificatesChanged(userHandle, installedCerts);
@@ -167,7 +170,7 @@ public class CertificateMonitor {
try {
userContext = mInjector.createContextAsUser(userHandle);
} catch (PackageManager.NameNotFoundException e) {
- Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
+ Slog.e(LOG_TAG, e, "Create context as %s failed", userHandle);
return null;
}
@@ -183,7 +186,6 @@ public class CertificateMonitor {
smallIconId = R.drawable.stat_sys_certificate_info;
parentUserId = mService.getProfileParentId(userHandle.getIdentifier());
} else if (mService.getDeviceOwnerUserId() == userHandle.getIdentifier()) {
- final String ownerName = mService.getDeviceOwnerName();
contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
mService.getDeviceOwnerName());
smallIconId = R.drawable.stat_sys_certificate_info;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index 3067d4507162..00e0292d404f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
@@ -46,19 +46,11 @@ public class DeviceAdminServiceController {
final Object mLock = new Object();
final Context mContext;
- private final DevicePolicyManagerService mService;
private final DevicePolicyManagerService.Injector mInjector;
private final DevicePolicyConstants mConstants;
private final Handler mHandler; // needed?
- static void debug(String format, Object... args) {
- if (!DEBUG) {
- return;
- }
- Slog.d(TAG, String.format(format, args));
- }
-
private class DevicePolicyServiceConnection
extends PersistentConnection<IDeviceAdminService> {
public DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName) {
@@ -88,7 +80,6 @@ public class DeviceAdminServiceController {
public DeviceAdminServiceController(DevicePolicyManagerService service,
DevicePolicyConstants constants) {
- mService = service;
mInjector = service.mInjector;
mContext = mInjector.mContext;
mHandler = new Handler(BackgroundThread.get().getLooper());
@@ -122,8 +113,9 @@ public class DeviceAdminServiceController {
synchronized (mLock) {
final ServiceInfo service = findService(packageName, userId);
if (service == null) {
- debug("Owner package %s on u%d has no service.",
- packageName, userId);
+ if (DEBUG) {
+ Slog.d(TAG, "Owner package %s on u%d has no service.", packageName, userId);
+ }
disconnectServiceOnUserLocked(userId, actionForLog);
return;
}
@@ -134,14 +126,17 @@ public class DeviceAdminServiceController {
// Note even when we're already connected to the same service, the binding
// would have died at this point due to a package update. So we disconnect
// anyway and re-connect.
- debug("Disconnecting from existing service connection.",
- packageName, userId);
+ if (DEBUG) {
+ Slog.d("Disconnecting from existing service connection.", packageName,
+ userId);
+ }
disconnectServiceOnUserLocked(userId, actionForLog);
}
- debug("Owner package %s on u%d has service %s for %s",
- packageName, userId,
+ if (DEBUG) {
+ Slog.d("Owner package %s on u%d has service %s for %s", packageName, userId,
service.getComponentName().flattenToShortString(), actionForLog);
+ }
final DevicePolicyServiceConnection conn =
new DevicePolicyServiceConnection(
@@ -172,8 +167,10 @@ public class DeviceAdminServiceController {
private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) {
final DevicePolicyServiceConnection conn = mConnections.get(userId);
if (conn != null) {
- debug("Stopping service for u%d if already running for %s.",
- userId, actionForLog);
+ if (DEBUG) {
+ Slog.d(TAG, "Stopping service for u%d if already running for %s.", userId,
+ actionForLog);
+ }
conn.unbind();
mConnections.remove(userId);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 464d6f5ea835..84e6da0d9851 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -88,7 +88,7 @@ public class DevicePolicyConstants {
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
- Slog.e(TAG, "Bad device policy settings: " + settings);
+ Slog.e(TAG, "Bad device policy settings: %s", settings);
}
long dasDiedServiceReconnectBackoffSec = parser.getLong(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index c0b2ed4cc955..52cdce6ee7b3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -179,11 +179,11 @@ class DevicePolicyData {
*/
static boolean store(DevicePolicyData policyData, JournaledFile file, boolean isFdeDevice) {
FileOutputStream stream = null;
+ File chooseForWrite = null;
try {
- File chooseForWrite = file.chooseForWrite();
+ chooseForWrite = file.chooseForWrite();
if (VERBOSE_LOG) {
- Slog.v(TAG, "Storing data for user " + policyData.mUserId + " on "
- + chooseForWrite);
+ Slog.v(TAG, "Storing data for user %d on %s ", policyData.mUserId, chooseForWrite);
}
stream = new FileOutputStream(chooseForWrite, false);
TypedXmlSerializer out = Xml.resolveSerializer(stream);
@@ -195,7 +195,7 @@ class DevicePolicyData {
policyData.mRestrictionsProvider.flattenToString());
}
if (policyData.mUserSetupComplete) {
- if (VERBOSE_LOG) Slog.v(TAG, "setting " + ATTR_SETUP_COMPLETE + " to true");
+ if (VERBOSE_LOG) Slog.v(TAG, "setting %s to true", ATTR_SETUP_COMPLETE);
out.attributeBoolean(null, ATTR_SETUP_COMPLETE, true);
}
if (policyData.mPaired) {
@@ -216,8 +216,8 @@ class DevicePolicyData {
if (policyData.mFactoryResetFlags != 0) {
if (VERBOSE_LOG) {
- Slog.v(TAG, "Storing factory reset flags for user " + policyData.mUserId + ": "
- + factoryResetFlagsToString(policyData.mFactoryResetFlags));
+ Slog.v(TAG, "Storing factory reset flags for user %d: %s", policyData.mUserId,
+ factoryResetFlagsToString(policyData.mFactoryResetFlags));
}
out.attributeInt(null, ATTR_FACTORY_RESET_FLAGS, policyData.mFactoryResetFlags);
}
@@ -382,7 +382,7 @@ class DevicePolicyData {
file.commit();
return true;
} catch (XmlPullParserException | IOException e) {
- Slog.w(TAG, "failed writing file", e);
+ Slog.w(TAG, e, "failed writing file %s", chooseForWrite);
try {
if (stream != null) {
stream.close();
@@ -404,10 +404,8 @@ class DevicePolicyData {
ComponentName ownerComponent) {
FileInputStream stream = null;
File file = journaledFile.chooseForRead();
- if (VERBOSE_LOG) {
- Slog.v(TAG, "Loading data for user " + policy.mUserId + " from " + file);
- }
-
+ if (VERBOSE_LOG) Slog.v(TAG, "Loading data for user %d from %s", policy.mUserId, file);
+ boolean needsRewrite = false;
try {
stream = new FileInputStream(file);
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
@@ -454,8 +452,8 @@ class DevicePolicyData {
policy.mFactoryResetFlags = parser.getAttributeInt(null, ATTR_FACTORY_RESET_FLAGS, 0);
if (VERBOSE_LOG) {
- Slog.v(TAG, "Restored factory reset flags for user " + policy.mUserId + ": "
- + factoryResetFlagsToString(policy.mFactoryResetFlags));
+ Slog.v(TAG, "Restored factory reset flags for user %d: %s", policy.mUserId,
+ factoryResetFlagsToString(policy.mFactoryResetFlags));
}
policy.mFactoryResetReason = parser.getAttributeValue(null, ATTR_FACTORY_RESET_REASON);
@@ -488,7 +486,7 @@ class DevicePolicyData {
policy.mAdminMap.put(ap.info.getComponent(), ap);
}
} catch (RuntimeException e) {
- Slog.w(TAG, "Failed loading admin " + name, e);
+ Slog.w(TAG, e, "Failed loading admin %s", name);
}
} else if ("delegation".equals(tag)) {
// Parse delegation info.
@@ -560,7 +558,7 @@ class DevicePolicyData {
policy.mAppsSuspended =
parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else {
- Slog.w(TAG, "Unknown tag: " + tag);
+ Slog.w(TAG, "Unknown tag: %s", tag);
XmlUtils.skipCurrentTag(parser);
}
}
@@ -568,7 +566,7 @@ class DevicePolicyData {
// Don't be noisy, this is normal if we haven't defined any policies.
} catch (NullPointerException | NumberFormatException | XmlPullParserException | IOException
| IndexOutOfBoundsException e) {
- Slog.w(TAG, "failed parsing " + file, e);
+ Slog.w(TAG, e, "failed parsing %s", file);
}
try {
if (stream != null) {
@@ -592,8 +590,8 @@ class DevicePolicyData {
}
}
if (!haveOwner) {
- Slog.w(TAG, "Previous password owner " + mPasswordOwner
- + " no longer active; disabling");
+ Slog.w(TAG, "Previous password owner %s no longer active; disabling",
+ mPasswordOwner);
mPasswordOwner = -1;
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6e63454385c5..577f3f5a1cb4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1106,7 +1106,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
* Used by {@code setDevicePolicySafetyChecker()} above and {@link OneTimeSafetyChecker}.
*/
void setDevicePolicySafetyCheckerUnchecked(DevicePolicySafetyChecker safetyChecker) {
- Slog.i(LOG_TAG, String.format("Setting DevicePolicySafetyChecker as %s", safetyChecker));
+ Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as %s", safetyChecker);
mSafetyChecker = safetyChecker;
mInjector.setDevicePolicySafetyChecker(safetyChecker);
}
@@ -1588,7 +1588,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
CryptoTestHelper.runAndLogSelfTest();
}
- public String[] getPersonalAppsForSuspension(int userId) {
+ public String[] getPersonalAppsForSuspension(@UserIdInt int userId) {
return PersonalAppsSuspensionHelper.forUser(mContext, userId)
.getPersonalAppsForSuspension();
}
@@ -10620,6 +10620,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
+ public List<String> listPolicyExemptApps() {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+
+ // TODO(b/181238156): decide whether it should only list the apps set by the resources,
+ // or also the "critical" apps defined by PersonalAppsSuspensionHelper (like SMS app).
+ // If it's the latter, refactor PersonalAppsSuspensionHelper so it (or a superclass) takes
+ // the resources on constructor.
+ String[] core = mContext.getResources().getStringArray(R.array.policy_exempt_apps);
+ String[] vendor = mContext.getResources().getStringArray(R.array.vendor_policy_exempt_apps);
+
+ int size = core.length + vendor.length;
+ Set<String> apps = new ArraySet<>(size);
+ for (String app : core) {
+ apps.add(app);
+ }
+ for (String app : vendor) {
+ apps.add(app);
+ }
+
+ return new ArrayList<>(apps);
+ }
+
+ @Override
public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner,
boolean parent) {
Objects.requireNonNull(who, "ComponentName is null");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 5484a148b0b6..8e31029769d0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -21,6 +21,7 @@ import android.os.ShellCommand;
import com.android.server.devicepolicy.Owners.OwnerDto;
import java.io.PrintWriter;
+import java.util.Collection;
import java.util.List;
import java.util.Objects;
@@ -30,6 +31,7 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
private static final String CMD_IS_SAFE_OPERATION_BY_REASON = "is-operation-safe-by-reason";
private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
private static final String CMD_LIST_OWNERS = "list-owners";
+ private static final String CMD_LIST_POLICY_EXEMPT_APPS = "list-policy-exempt-apps";
private final DevicePolicyManagerService mService;
@@ -60,6 +62,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
return runSetSafeOperation(pw);
case CMD_LIST_OWNERS:
return runListOwners(pw);
+ case CMD_LIST_POLICY_EXEMPT_APPS:
+ return runListPolicyExemptApps(pw);
default:
return onInvalidCommand(pw, cmd);
}
@@ -88,6 +92,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
+ " \n\n");
pw.printf(" %s\n", CMD_LIST_OWNERS);
pw.printf(" Lists the device / profile owners per user \n\n");
+ pw.printf(" %s\n", CMD_LIST_POLICY_EXEMPT_APPS);
+ pw.printf(" Lists the apps that are exempt from policies\n\n");
}
private int runIsSafeOperation(PrintWriter pw) {
@@ -119,18 +125,20 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
return 0;
}
- private int runListOwners(PrintWriter pw) {
- List<OwnerDto> owners = mService.listAllOwners();
- if (owners.isEmpty()) {
- pw.println("none");
+ private int printAndGetSize(PrintWriter pw, Collection<?> collection, String nameOnSingular) {
+ if (collection.isEmpty()) {
+ pw.printf("no %ss\n", nameOnSingular);
return 0;
}
- int size = owners.size();
- if (size == 1) {
- pw.println("1 owner:");
- } else {
- pw.printf("%d owners:\n", size);
- }
+ int size = collection.size();
+ pw.printf("%d %s%s:\n", size, nameOnSingular, (size == 1 ? "" : "s"));
+ return size;
+ }
+
+ private int runListOwners(PrintWriter pw) {
+ List<OwnerDto> owners = mService.listAllOwners();
+ int size = printAndGetSize(pw, owners, "owner");
+ if (size == 0) return 0;
for (int i = 0; i < size; i++) {
OwnerDto owner = owners.get(i);
@@ -150,4 +158,17 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
return 0;
}
+
+ private int runListPolicyExemptApps(PrintWriter pw) {
+ List<String> apps = mService.listPolicyExemptApps();
+ int size = printAndGetSize(pw, apps, "policy exempt app");
+
+ if (size == 0) return 0;
+
+ for (int i = 0; i < size; i++) {
+ String app = apps.get(i);
+ pw.printf(" %d: %s\n", i, app);
+ }
+ return 0;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
index 457255bd9a73..28a6987bb846 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
@@ -68,17 +68,16 @@ public final class FactoryResetter {
IResultReceiver receiver = new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) throws RemoteException {
- Slog.i(TAG, String.format("Factory reset confirmed by %s, proceeding",
- mSafetyChecker));
+ Slog.i(TAG, "Factory reset confirmed by %s, proceeding", mSafetyChecker);
try {
factoryResetInternalUnchecked();
} catch (IOException e) {
// Shouldn't happen
- Slog.wtf(TAG, "IOException calling underlying systems", e);
+ Slog.wtf(TAG, e, "IOException calling underlying systems");
}
}
};
- Slog.i(TAG, String.format("Delaying factory reset until %s confirms", mSafetyChecker));
+ Slog.i(TAG, "Delaying factory reset until %s confirms", mSafetyChecker);
mSafetyChecker.onFactoryReset(receiver);
return false;
}
@@ -113,9 +112,9 @@ public final class FactoryResetter {
}
private void factoryResetInternalUnchecked() throws IOException {
- Slog.i(TAG, String.format("factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
+ Slog.i(TAG, "factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
+ "wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc,
- mWipeAdoptableStorage, mWipeFactoryResetProtection));
+ mWipeAdoptableStorage, mWipeFactoryResetProtection);
UserManager um = mContext.getSystemService(UserManager.class);
if (!mForce && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index 37dbfc170aff..0b9ece466df4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -18,6 +18,8 @@ package com.android.server.devicepolicy;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -36,6 +38,7 @@ import android.provider.Telephony;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
+import android.util.Log;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
@@ -105,7 +108,9 @@ public final class PersonalAppsSuspensionHelper {
result.remove(pkg);
}
- Slog.i(LOG_TAG, "Packages subject to suspension: " + String.join(",", result));
+ if (Log.isLoggable(LOG_TAG, Log.INFO)) {
+ Slog.i(LOG_TAG, "Packages subject to suspension: %s", String.join(",", result));
+ }
return result.toArray(new String[0]);
}
@@ -118,7 +123,7 @@ public final class PersonalAppsSuspensionHelper {
for (final ResolveInfo resolveInfo : matchingActivities) {
if (resolveInfo.activityInfo == null
|| TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) {
- Slog.wtf(LOG_TAG, "Could not find package name for launcher app" + resolveInfo);
+ Slog.wtf(LOG_TAG, "Could not find package name for launcher app %s", resolveInfo);
continue;
}
final String packageName = resolveInfo.activityInfo.packageName;
@@ -129,7 +134,8 @@ public final class PersonalAppsSuspensionHelper {
result.add(packageName);
}
} catch (PackageManager.NameNotFoundException e) {
- Slog.e(LOG_TAG, "Could not find application info for launcher app: " + packageName);
+ Slog.e(LOG_TAG, "Could not find application info for launcher app: %s",
+ packageName);
}
}
return result;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
index 543f3815454e..2959c10d5508 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
@@ -25,6 +25,8 @@ import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEP
import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED;
import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED;
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
import android.annotation.IntDef;
import android.app.Notification;
import android.app.PendingIntent;
@@ -58,7 +60,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
* Class managing bugreport collection upon device owner's request.
*/
public class RemoteBugreportManager {
- private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
@@ -206,7 +207,7 @@ public class RemoteBugreportManager {
return true;
} catch (RemoteException re) {
// should never happen
- Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re);
+ Slog.e(LOG_TAG, re, "Failed to make remote calls to start bugreportremote service");
return false;
} finally {
mInjector.binderRestoreCallingIdentity(callingIdentity);
@@ -220,7 +221,7 @@ public class RemoteBugreportManager {
mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
} catch (IntentFilter.MalformedMimeTypeException e) {
// should never happen, as setting a constant
- Slog.w(LOG_TAG, "Failed to set type " + BUGREPORT_MIMETYPE, e);
+ Slog.w(LOG_TAG, e, "Failed to set type %s", BUGREPORT_MIMETYPE);
}
final IntentFilter filterConsent = new IntentFilter();
filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED);
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 1fcc2843bd43..c38d0b3cc7db 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -74,6 +74,13 @@ struct Constants {
// If DL was up and not crashing for 10mins, we consider it healthy and reset all delays.
static constexpr auto healthyDataLoaderUptime = 10min;
+
+ // For healthy DLs, we'll retry every ~5secs for ~10min
+ static constexpr auto bindRetryInterval = 5s;
+ static constexpr auto bindGracePeriod = 10min;
+
+ static constexpr auto bindingTimeout = 1min;
+
// 10s, 100s (~2min), 1000s (~15min), 10000s (~3hrs)
static constexpr auto minBindDelay = 10s;
static constexpr auto maxBindDelay = 10000s;
@@ -293,6 +300,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v
mTimedQueue(sm.getTimedQueue()),
mProgressUpdateJobQueue(sm.getProgressUpdateJobQueue()),
mFs(sm.getFs()),
+ mClock(sm.getClock()),
mIncrementalDir(rootDir) {
CHECK(mVold) << "Vold service is unavailable";
CHECK(mDataLoaderManager) << "DataLoaderManagerService is unavailable";
@@ -302,6 +310,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v
CHECK(mTimedQueue) << "TimedQueue is unavailable";
CHECK(mProgressUpdateJobQueue) << "mProgressUpdateJobQueue is unavailable";
CHECK(mFs) << "Fs is unavailable";
+ CHECK(mClock) << "Clock is unavailable";
mJobQueue.reserve(16);
mJobProcessor = std::thread([this]() {
@@ -2241,17 +2250,44 @@ void IncrementalService::DataLoaderStub::setTargetStatusLocked(int status) {
<< status << " (current " << mCurrentStatus << ")";
}
-Milliseconds IncrementalService::DataLoaderStub::updateBindDelay() {
+std::optional<Milliseconds> IncrementalService::DataLoaderStub::needToBind() {
std::unique_lock lock(mMutex);
+
+ const auto now = mService.mClock->now();
+ const bool healthy = (mPreviousBindDelay == 0ms);
+
+ if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_BINDING &&
+ now - mCurrentStatusTs <= Constants::bindingTimeout) {
+ LOG(INFO) << "Binding still in progress. "
+ << (healthy ? "The DL is healthy/freshly bound, ok to retry for a few times."
+ : "Already unhealthy, don't do anything.");
+ // Binding still in progress.
+ if (!healthy) {
+ // Already unhealthy, don't do anything.
+ return {};
+ }
+ // The DL is healthy/freshly bound, ok to retry for a few times.
+ if (now - mPreviousBindTs <= Constants::bindGracePeriod) {
+ // Still within grace period.
+ if (now - mCurrentStatusTs >= Constants::bindRetryInterval) {
+ // Retry interval passed, retrying.
+ mCurrentStatusTs = now;
+ mPreviousBindDelay = 0ms;
+ return 0ms;
+ }
+ return {};
+ }
+ // fallthrough, mark as unhealthy, and retry with delay
+ }
+
const auto previousBindTs = mPreviousBindTs;
- const auto now = Clock::now();
mPreviousBindTs = now;
const auto nonCrashingInterval = std::max(castToMs(now - previousBindTs), 100ms);
if (previousBindTs.time_since_epoch() == Clock::duration::zero() ||
nonCrashingInterval > Constants::healthyDataLoaderUptime) {
mPreviousBindDelay = 0ms;
- return mPreviousBindDelay;
+ return 0ms;
}
constexpr auto minBindDelayMs = castToMs(Constants::minBindDelay);
@@ -2264,12 +2300,16 @@ Milliseconds IncrementalService::DataLoaderStub::updateBindDelay() {
const auto bindDelayJitterRangeMs = bindDelayMs / Constants::bindDelayJitterDivider;
const auto bindDelayJitterMs = rand() % (bindDelayJitterRangeMs * 2) - bindDelayJitterRangeMs;
mPreviousBindDelay = std::chrono::milliseconds(bindDelayMs + bindDelayJitterMs);
-
return mPreviousBindDelay;
}
bool IncrementalService::DataLoaderStub::bind() {
- const auto bindDelay = updateBindDelay();
+ const auto maybeBindDelay = needToBind();
+ if (!maybeBindDelay) {
+ LOG(DEBUG) << "Skipping bind to " << mParams.packageName << " because of pending bind.";
+ return true;
+ }
+ const auto bindDelay = *maybeBindDelay;
if (bindDelay > 1s) {
LOG(INFO) << "Delaying bind to " << mParams.packageName << " by "
<< bindDelay.count() / 1000 << "s";
@@ -2279,7 +2319,21 @@ bool IncrementalService::DataLoaderStub::bind() {
auto status = mService.mDataLoaderManager->bindToDataLoader(id(), mParams, bindDelay.count(),
this, &result);
if (!status.isOk() || !result) {
- LOG(ERROR) << "Failed to bind a data loader for mount " << id();
+ const bool healthy = (bindDelay == 0ms);
+ LOG(ERROR) << "Failed to bind a data loader for mount " << id()
+ << (healthy ? ", retrying." : "");
+
+ // Internal error, retry for healthy/new DLs.
+ // Let needToBind migrate it to unhealthy after too many retries.
+ if (healthy) {
+ if (mService.addTimedJob(*mService.mTimedQueue, id(), Constants::bindRetryInterval,
+ [this]() { fsmStep(); })) {
+ // Mark as binding so that we know it's not the DL's fault.
+ setCurrentStatus(IDataLoaderStatusListener::DATA_LOADER_BINDING);
+ return true;
+ }
+ }
+
return false;
}
return true;
@@ -2339,7 +2393,14 @@ bool IncrementalService::DataLoaderStub::fsmStep() {
// Do nothing, this is a reset state.
break;
case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: {
- return destroy();
+ switch (currentStatus) {
+ case IDataLoaderStatusListener::DATA_LOADER_BINDING:
+ setCurrentStatus(IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
+ return true;
+ default:
+ return destroy();
+ }
+ break;
}
case IDataLoaderStatusListener::DATA_LOADER_STARTED: {
switch (currentStatus) {
@@ -2353,6 +2414,7 @@ bool IncrementalService::DataLoaderStub::fsmStep() {
switch (currentStatus) {
case IDataLoaderStatusListener::DATA_LOADER_DESTROYED:
case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE:
+ case IDataLoaderStatusListener::DATA_LOADER_BINDING:
return bind();
case IDataLoaderStatusListener::DATA_LOADER_BOUND:
return create();
@@ -2372,7 +2434,8 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount
fromServiceSpecificError(-EINVAL, "onStatusChange came to invalid DataLoaderStub");
}
if (id() != mountId) {
- LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId;
+ LOG(ERROR) << "onStatusChanged: mount ID mismatch: expected " << id()
+ << ", but got: " << mountId;
return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
}
if (newStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
@@ -2396,11 +2459,13 @@ void IncrementalService::DataLoaderStub::setCurrentStatus(int newStatus) {
}
oldStatus = mCurrentStatus;
- mCurrentStatus = newStatus;
targetStatus = mTargetStatus;
-
listener = mStatusListener;
+ // Change the status.
+ mCurrentStatus = newStatus;
+ mCurrentStatusTs = mService.mClock->now();
+
if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE ||
mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
// For unavailable, unbind from DataLoader to ensure proper re-commit.
@@ -2428,7 +2493,8 @@ binder::Status IncrementalService::DataLoaderStub::reportStreamHealth(MountId mo
"reportStreamHealth came to invalid DataLoaderStub");
}
if (id() != mountId) {
- LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId;
+ LOG(ERROR) << "reportStreamHealth: mount ID mismatch: expected " << id()
+ << ", but got: " << mountId;
return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
}
{
@@ -2694,6 +2760,8 @@ static std::string toHexString(const RawMetadata& metadata) {
void IncrementalService::DataLoaderStub::onDump(int fd) {
dprintf(fd, " dataLoader: {\n");
dprintf(fd, " currentStatus: %d\n", mCurrentStatus);
+ dprintf(fd, " currentStatusTs: %lldmcs\n",
+ (long long)(elapsedMcs(mCurrentStatusTs, Clock::now())));
dprintf(fd, " targetStatus: %d\n", mTargetStatus);
dprintf(fd, " targetStatusTs: %lldmcs\n",
(long long)(elapsedMcs(mTargetStatusTs, Clock::now())));
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 14e5a7734172..4eb513808342 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -267,7 +267,10 @@ private:
BootClockTsUs getOldestTsFromLastPendingReads();
Milliseconds elapsedMsSinceKernelTs(TimePoint now, BootClockTsUs kernelTsUs);
- Milliseconds updateBindDelay();
+ // If the stub has to bind to the DL.
+ // Returns {} if bind operation is already in progress.
+ // Or bind delay in ms.
+ std::optional<Milliseconds> needToBind();
void registerForPendingReads();
void unregisterFromPendingReads();
@@ -283,6 +286,7 @@ private:
std::condition_variable mStatusCondition;
int mCurrentStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
+ TimePoint mCurrentStatusTs = {};
int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
TimePoint mTargetStatusTs = {};
@@ -443,6 +447,7 @@ private:
const std::unique_ptr<TimedQueueWrapper> mTimedQueue;
const std::unique_ptr<TimedQueueWrapper> mProgressUpdateJobQueue;
const std::unique_ptr<FsWrapper> mFs;
+ const std::unique_ptr<ClockWrapper> mClock;
const std::string mIncrementalDir;
mutable std::mutex mLock;
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index d61328942e5c..80f409ff1c61 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -329,6 +329,14 @@ public:
}
};
+class RealClockWrapper final : public ClockWrapper {
+public:
+ RealClockWrapper() = default;
+ ~RealClockWrapper() = default;
+
+ TimePoint now() const final { return Clock::now(); }
+};
+
RealServiceManager::RealServiceManager(sp<IServiceManager> serviceManager, JNIEnv* env)
: mServiceManager(std::move(serviceManager)), mJvm(RealJniWrapper::getJvm(env)) {}
@@ -388,6 +396,10 @@ std::unique_ptr<FsWrapper> RealServiceManager::getFs() {
return std::make_unique<RealFsWrapper>();
}
+std::unique_ptr<ClockWrapper> RealServiceManager::getClock() {
+ return std::make_unique<RealClockWrapper>();
+}
+
static JavaVM* getJavaVm(JNIEnv* env) {
CHECK(env);
JavaVM* jvm = nullptr;
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 245bb3105be5..d113f992de71 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -158,6 +158,12 @@ public:
virtual void listFilesRecursive(std::string_view directoryPath, FileCallback onFile) const = 0;
};
+class ClockWrapper {
+public:
+ virtual ~ClockWrapper() = default;
+ virtual TimePoint now() const = 0;
+};
+
class ServiceManagerWrapper {
public:
virtual ~ServiceManagerWrapper() = default;
@@ -170,6 +176,7 @@ public:
virtual std::unique_ptr<TimedQueueWrapper> getTimedQueue() = 0;
virtual std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() = 0;
virtual std::unique_ptr<FsWrapper> getFs() = 0;
+ virtual std::unique_ptr<ClockWrapper> getClock() = 0;
};
// --- Real stuff ---
@@ -187,6 +194,7 @@ public:
std::unique_ptr<TimedQueueWrapper> getTimedQueue() final;
std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() final;
std::unique_ptr<FsWrapper> getFs() final;
+ std::unique_ptr<ClockWrapper> getClock() final;
private:
template <class INTERFACE>
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 5236983c83ff..25b34b5669b8 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -248,6 +248,27 @@ public:
}
return binder::Status::ok();
}
+ binder::Status bindToDataLoaderNotOkWithNoDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(bindDelayMs == 0) << bindDelayMs;
+ *_aidl_return = false;
+ return binder::Status::ok();
+ }
+ binder::Status bindToDataLoaderBindingWithNoDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(bindDelayMs == 0) << bindDelayMs;
+ *_aidl_return = true;
+ if (listener) {
+ listener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_BINDING);
+ }
+ return binder::Status::ok();
+ }
binder::Status bindToDataLoaderOkWith10sDelay(int32_t mountId,
const DataLoaderParamsParcel& params,
int bindDelayMs,
@@ -557,6 +578,21 @@ public:
}
};
+class MockClockWrapper : public ClockWrapper {
+public:
+ MOCK_CONST_METHOD0(now, TimePoint());
+
+ void start() { ON_CALL(*this, now()).WillByDefault(Invoke(this, &MockClockWrapper::getClock)); }
+ template <class Delta>
+ void advance(Delta delta) {
+ mClock += delta;
+ }
+
+ TimePoint getClock() const { return mClock; }
+
+ TimePoint mClock = Clock::now();
+};
+
class MockStorageHealthListener : public os::incremental::BnStorageHealthListener {
public:
MOCK_METHOD2(onHealthStatus, binder::Status(int32_t storageId, int32_t status));
@@ -594,7 +630,7 @@ public:
std::unique_ptr<MockLooperWrapper> looper,
std::unique_ptr<MockTimedQueueWrapper> timedQueue,
std::unique_ptr<MockTimedQueueWrapper> progressUpdateJobQueue,
- std::unique_ptr<MockFsWrapper> fs)
+ std::unique_ptr<MockFsWrapper> fs, std::unique_ptr<MockClockWrapper> clock)
: mVold(std::move(vold)),
mDataLoaderManager(std::move(dataLoaderManager)),
mIncFs(std::move(incfs)),
@@ -603,7 +639,8 @@ public:
mLooper(std::move(looper)),
mTimedQueue(std::move(timedQueue)),
mProgressUpdateJobQueue(std::move(progressUpdateJobQueue)),
- mFs(std::move(fs)) {}
+ mFs(std::move(fs)),
+ mClock(std::move(clock)) {}
std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); }
std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final {
return std::move(mDataLoaderManager);
@@ -619,6 +656,7 @@ public:
return std::move(mProgressUpdateJobQueue);
}
std::unique_ptr<FsWrapper> getFs() final { return std::move(mFs); }
+ std::unique_ptr<ClockWrapper> getClock() final { return std::move(mClock); }
private:
std::unique_ptr<MockVoldService> mVold;
@@ -630,6 +668,7 @@ private:
std::unique_ptr<MockTimedQueueWrapper> mTimedQueue;
std::unique_ptr<MockTimedQueueWrapper> mProgressUpdateJobQueue;
std::unique_ptr<MockFsWrapper> mFs;
+ std::unique_ptr<MockClockWrapper> mClock;
};
// --- IncrementalServiceTest ---
@@ -657,6 +696,8 @@ public:
mProgressUpdateJobQueue = progressUpdateJobQueue.get();
auto fs = std::make_unique<NiceMock<MockFsWrapper>>();
mFs = fs.get();
+ auto clock = std::make_unique<NiceMock<MockClockWrapper>>();
+ mClock = clock.get();
mIncrementalService = std::make_unique<
IncrementalService>(MockServiceManager(std::move(vold),
std::move(dataloaderManager),
@@ -664,12 +705,13 @@ public:
std::move(jni), std::move(looper),
std::move(timedQueue),
std::move(progressUpdateJobQueue),
- std::move(fs)),
+ std::move(fs), std::move(clock)),
mRootDir.path);
mDataLoaderParcel.packageName = "com.test";
mDataLoaderParcel.arguments = "uri";
mDataLoaderManager->unbindFromDataLoaderSuccess();
mIncrementalService->onSystemReady();
+ mClock->start();
setupSuccess();
}
@@ -724,6 +766,7 @@ protected:
NiceMock<MockTimedQueueWrapper>* mTimedQueue = nullptr;
NiceMock<MockTimedQueueWrapper>* mProgressUpdateJobQueue = nullptr;
NiceMock<MockFsWrapper>* mFs = nullptr;
+ NiceMock<MockClockWrapper>* mClock = nullptr;
NiceMock<MockDataLoader>* mDataLoader = nullptr;
std::unique_ptr<IncrementalService> mIncrementalService;
TemporaryDir mRootDir;
@@ -853,6 +896,119 @@ TEST_F(IncrementalServiceTest, testDataLoaderDestroyedAndDelayed) {
mDataLoaderManager->setDataLoaderStatusDestroyed();
}
+TEST_F(IncrementalServiceTest, testDataLoaderOnRestart) {
+ mIncFs->waitForPendingReadsSuccess();
+ mIncFs->openMountSuccess();
+
+ constexpr auto bindRetryInterval = 5s;
+
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(10);
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(6);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(6);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2);
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+
+ // First binds to DataLoader fails... because it's restart.
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderNotOkWithNoDelay));
+
+ // Request DL start.
+ mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {});
+
+ // Retry callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, bindRetryInterval);
+ auto retryCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // Expecting the same bindToDataLoaderNotOkWithNoDelay call.
+ mClock->advance(5s);
+
+ retryCallback();
+ // Retry callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, bindRetryInterval);
+ retryCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // Returning "binding" so that we can retry.
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderBindingWithNoDelay));
+
+ // Expecting bindToDataLoaderBindingWithNoDelay call.
+ mClock->advance(5s);
+
+ retryCallback();
+ // No retry callback.
+ ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+ ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+ // Should not change the bindToDataLoader call count
+ ASSERT_NE(nullptr, mLooper->mCallback);
+ ASSERT_NE(nullptr, mLooper->mCallbackData);
+ auto looperCb = mLooper->mCallback;
+ auto looperCbData = mLooper->mCallbackData;
+ looperCb(-1, -1, looperCbData);
+
+ // Expecting the same bindToDataLoaderBindingWithNoDelay call.
+ mClock->advance(5s);
+
+ // Use pending reads callback to trigger binding.
+ looperCb(-1, -1, looperCbData);
+
+ // No retry callback.
+ ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+ ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+ // Now we are out of 10m "retry" budget, let's finally bind.
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager, &MockDataLoaderManager::bindToDataLoaderOk));
+ mClock->advance(11min);
+
+ // Use pending reads callback to trigger binding.
+ looperCb(-1, -1, looperCbData);
+
+ // No retry callback.
+ ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+ ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+ // And test the rest of the backoff.
+ // Simulated crash/other connection breakage.
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith10sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith100sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith1000sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+}
+
TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) {
mDataLoader->initializeCreateOkNoStatus();
EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
index 169b85eb7e06..b07fe19393b2 100644
--- a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
@@ -19,6 +19,7 @@ package com.android.server.smartspace;
import static android.Manifest.permission.MANAGE_SMARTSPACE;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.content.Context.SMARTSPACE_SERVICE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -161,11 +162,13 @@ public class SmartspaceManagerService extends
Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
}
- if (!(mServiceNameResolver.isTemporary(userId)
+ Context ctx = getContext();
+ if (!(ctx.checkCallingPermission(MANAGE_SMARTSPACE) == PERMISSION_GRANTED
+ || mServiceNameResolver.isTemporary(userId)
|| mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
- String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid();
+ String msg = "Permission Denial: Cannot call " + func + " from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
index f00edcc85404..fcd6b842426a 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -120,6 +120,11 @@ class CompatConfigBuilder {
return this;
}
+ CompatConfigBuilder addEnabledSinceApexChangeWithId(int sdk, long id) {
+ mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "", false));
+ return this;
+ }
+
CompatConfig build() {
CompatConfig config = new CompatConfig(mBuildClassifier, mContext);
config.forceNonDebuggableFinalForTest(false);
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index b6b6932c4a93..bd774056aef8 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -86,6 +86,7 @@ public class CompatConfigTest {
// Assume userdebug/eng non-final build
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+ when(mBuildClassifier.platformTargetSdk()).thenReturn(30);
ChangeIdStateCache.disable();
when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
.thenThrow(new NameNotFoundException());
@@ -567,6 +568,34 @@ public class CompatConfigTest {
}
@Test
+ public void testReadApexConfig() throws IOException {
+ String configXml = "<config>"
+ + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
+ + "<compat-change id=\"1235\" name=\"MY_CHANGE2\" disabled=\"true\" />"
+ + "<compat-change id=\"1236\" name=\"MY_CHANGE3\" />"
+ + "<compat-change id=\"1237\" name=\"MY_CHANGE4\" enableSinceTargetSdk=\"31\" />"
+ + "</config>";
+
+ File dir = createTempDir();
+ writeToFile(dir, "platform_compat_config.xml", configXml);
+ CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+ compatConfig.forceNonDebuggableFinalForTest(false);
+
+ compatConfig.initConfigFromLib(dir);
+
+ assertThat(compatConfig.isChangeEnabled(1234L,
+ ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse();
+ assertThat(compatConfig.isChangeEnabled(1234L,
+ ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue();
+ assertThat(compatConfig.isChangeEnabled(1235L,
+ ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse();
+ assertThat(compatConfig.isChangeEnabled(1236L,
+ ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
+ assertThat(compatConfig.isChangeEnabled(1237L,
+ ApplicationInfoBuilder.create().withTargetSdk(31).build())).isTrue();
+ }
+
+ @Test
public void testReadConfigMultipleFiles() throws IOException {
String configXml1 = "<config>"
+ "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
index 0fd6445fbeeb..57fdcd340a02 100644
--- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -22,6 +22,7 @@ import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARG
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
+import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
import static com.google.common.truth.Truth.assertThat;
@@ -52,6 +53,7 @@ public class OverrideValidatorImplTest {
private static final int TARGET_SDK = 10;
private static final int TARGET_SDK_BEFORE = 9;
private static final int TARGET_SDK_AFTER = 11;
+ private static final int PLATFORM_SDK_VERSION = 30;
@Mock
private PackageManager mPackageManager;
@@ -61,6 +63,7 @@ public class OverrideValidatorImplTest {
private AndroidBuildClassifier debuggableBuild() {
AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
when(buildClassifier.isDebuggableBuild()).thenReturn(true);
+ when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
return buildClassifier;
}
@@ -68,6 +71,7 @@ public class OverrideValidatorImplTest {
AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
when(buildClassifier.isDebuggableBuild()).thenReturn(false);
when(buildClassifier.isFinalBuild()).thenReturn(false);
+ when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
return buildClassifier;
}
@@ -75,6 +79,7 @@ public class OverrideValidatorImplTest {
AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
when(buildClassifier.isDebuggableBuild()).thenReturn(false);
when(buildClassifier.isFinalBuild()).thenReturn(true);
+ when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
return buildClassifier;
}
@@ -333,6 +338,26 @@ public class OverrideValidatorImplTest {
}
@Test
+ public void getOverrideAllowedState_targetSdkChangeGreaterThanOsVersion_rejectOverride()
+ throws Exception {
+ final AndroidBuildClassifier buildClassifier = finalBuild();
+ CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+ .addEnabledSinceApexChangeWithId(PLATFORM_SDK_VERSION + 1, 1).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .debuggable()
+ .build());
+
+ OverrideAllowedState stateTargetSdkLessChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+ assertThat(stateTargetSdkLessChange).isEqualTo(
+ new OverrideAllowedState(PLATFORM_TOO_OLD, -1,
+ PLATFORM_SDK_VERSION));
+ }
+
+ @Test
public void getOverrideAllowedState_finalBuildEnabledChangeDebugApp_rejectOverride()
throws Exception {
CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 799b06734b54..3fc6e9918382 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -78,11 +78,12 @@ public class PlatformCompatTest {
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+ mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
// Assume userdebug/eng non-final build
mCompatConfig.forceNonDebuggableFinalForTest(false);
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+ when(mBuildClassifier.platformTargetSdk()).thenReturn(30);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
}
@@ -99,7 +100,7 @@ public class PlatformCompatTest {
.addLoggingOnlyChangeWithId(7L)
.addOverridableChangeWithId(8L)
.build();
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+ mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -125,8 +126,9 @@ public class PlatformCompatTest {
.addEnableSinceSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
.addEnableSinceSdkChangeWithId(Build.VERSION_CODES.R, 6L)
.addLoggingOnlyChangeWithId(7L)
+ .addEnableSinceSdkChangeWithId(31, 8L)
.build();
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+ mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -144,7 +146,7 @@ public class PlatformCompatTest {
.addEnableAfterSdkChangeWithId(Build.VERSION_CODES.O, 3L)
.build();
mCompatConfig.forceNonDebuggableFinalForTest(true);
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+ mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
// Before adding overrides.
assertThat(mPlatformCompat.isChangeEnabledByPackageName(1, PACKAGE_NAME, 0)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 89435e9a8862..77a39d8ac762 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -107,6 +107,7 @@ import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.test.MoreAsserts; // TODO(b/171932723): replace by Truth
import android.util.ArraySet;
+import android.util.Log;
import android.util.Pair;
import androidx.test.filters.SmallTest;
@@ -154,6 +155,9 @@ import java.util.concurrent.TimeUnit;
@SmallTest
@Presubmit
public class DevicePolicyManagerTest extends DpmTestBase {
+
+ private static final String TAG = DevicePolicyManagerTest.class.getSimpleName();
+
private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList(
permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL);
@@ -7187,6 +7191,47 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertThat(dpm.isUsbDataSignalingEnabled()).isEqualTo(enabled);
}
+ @Test
+ public void testGetPolicyExemptApps_noPermission() {
+ assertThrows(SecurityException.class, () -> dpm.getPolicyExemptApps());
+ }
+
+ @Test
+ public void testGetPolicyExemptApps_empty() {
+ grantManageDeviceAdmins();
+ mockPolicyExemptApps();
+ mockVendorPolicyExemptApps();
+
+ assertThat(dpm.getPolicyExemptApps()).isEmpty();
+ }
+
+ @Test
+ public void testGetPolicyExemptApps_baseOnly() {
+ grantManageDeviceAdmins();
+ mockPolicyExemptApps("foo");
+ mockVendorPolicyExemptApps();
+
+ assertThat(dpm.getPolicyExemptApps()).containsExactly("foo");
+ }
+
+ @Test
+ public void testGetPolicyExemptApps_vendorOnly() {
+ grantManageDeviceAdmins();
+ mockPolicyExemptApps();
+ mockVendorPolicyExemptApps("bar");
+
+ assertThat(dpm.getPolicyExemptApps()).containsExactly("bar");
+ }
+
+ @Test
+ public void testGetPolicyExemptApps_baseAndVendor() {
+ grantManageDeviceAdmins();
+ mockPolicyExemptApps("4", "23", "15", "42", "8");
+ mockVendorPolicyExemptApps("16", "15", "4");
+
+ assertThat(dpm.getPolicyExemptApps()).containsExactly("4", "8", "15", "16", "23", "42");
+ }
+
private void setUserUnlocked(int userHandle, boolean unlocked) {
when(getServices().userManager.isUserUnlocked(eq(userHandle))).thenReturn(unlocked);
}
@@ -7408,4 +7453,18 @@ public class DevicePolicyManagerTest extends DpmTestBase {
return new StringParceledListSlice(Arrays.asList(s));
}
+ private void grantManageDeviceAdmins() {
+ Log.d(TAG, "Granting " + permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ }
+
+ private void mockPolicyExemptApps(String... apps) {
+ Log.d(TAG, "Mocking R.array.policy_exempt_apps to return " + Arrays.toString(apps));
+ when(mContext.resources.getStringArray(R.array.policy_exempt_apps)).thenReturn(apps);
+ }
+
+ private void mockVendorPolicyExemptApps(String... apps) {
+ Log.d(TAG, "Mocking R.array.vendor_policy_exempt_apps to return " + Arrays.toString(apps));
+ when(mContext.resources.getStringArray(R.array.vendor_policy_exempt_apps)).thenReturn(apps);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 9a52643b57f2..9f428c7cbded 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -31,6 +31,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.content.pm.parsing.ParsingPackage;
@@ -141,6 +142,10 @@ public class AppsFilterTest {
return pkg(packageName).addReceiver(receiver);
}
+ private static ParsingPackage pkgWithSharedLibrary(String packageName, String libName) {
+ return pkg(packageName).addLibraryName(libName);
+ }
+
private static ParsedActivity createActivity(String packageName, IntentFilter[] filters) {
ParsedActivity activity = new ParsedActivity();
activity.setPackageName(packageName);
@@ -413,6 +418,118 @@ public class AppsFilterTest {
}
@Test
+ public void testNoUsesLibrary_Filters() throws Exception {
+ final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+ new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+ mMockExecutor);
+
+ simulateAddBasicAndroid(appsFilter);
+ appsFilter.onSystemReady();
+
+ final Signature mockSignature = Mockito.mock(Signature.class);
+ final SigningDetails mockSigningDetails = new SigningDetails(
+ new Signature[]{mockSignature},
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+ final PackageSetting target = simulateAddPackage(appsFilter,
+ pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+ DUMMY_TARGET_APPID,
+ setting -> setting.setSigningDetails(mockSigningDetails)
+ .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+ final PackageSetting calling = simulateAddPackage(appsFilter,
+ pkg("com.some.other.package"), DUMMY_CALLING_APPID);
+
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
+ }
+
+ @Test
+ public void testUsesLibrary_DoesntFilter() throws Exception {
+ final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+ new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+ mMockExecutor);
+
+ simulateAddBasicAndroid(appsFilter);
+ appsFilter.onSystemReady();
+
+ final Signature mockSignature = Mockito.mock(Signature.class);
+ final SigningDetails mockSigningDetails = new SigningDetails(
+ new Signature[]{mockSignature},
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+ final PackageSetting target = simulateAddPackage(appsFilter,
+ pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+ DUMMY_TARGET_APPID,
+ setting -> setting.setSigningDetails(mockSigningDetails)
+ .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+ final PackageSetting calling = simulateAddPackage(appsFilter,
+ pkg("com.some.other.package").addUsesLibrary("com.some.shared_library"),
+ DUMMY_CALLING_APPID);
+
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
+ }
+
+ @Test
+ public void testUsesOptionalLibrary_DoesntFilter() throws Exception {
+ final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+ new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+ mMockExecutor);
+
+ simulateAddBasicAndroid(appsFilter);
+ appsFilter.onSystemReady();
+
+ final Signature mockSignature = Mockito.mock(Signature.class);
+ final SigningDetails mockSigningDetails = new SigningDetails(
+ new Signature[]{mockSignature},
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+ final PackageSetting target = simulateAddPackage(appsFilter,
+ pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+ DUMMY_TARGET_APPID,
+ setting -> setting.setSigningDetails(mockSigningDetails)
+ .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+ final PackageSetting calling = simulateAddPackage(appsFilter,
+ pkg("com.some.other.package").addUsesOptionalLibrary("com.some.shared_library"),
+ DUMMY_CALLING_APPID);
+
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
+ }
+
+ @Test
+ public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception {
+ final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+ new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+ mMockExecutor);
+
+ simulateAddBasicAndroid(appsFilter);
+ appsFilter.onSystemReady();
+
+ final Signature mockSignature = Mockito.mock(Signature.class);
+ final SigningDetails mockSigningDetails = new SigningDetails(
+ new Signature[]{mockSignature},
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+ final PackageSetting target = simulateAddPackage(appsFilter,
+ pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+ DUMMY_TARGET_APPID,
+ setting -> setting.setSigningDetails(mockSigningDetails)
+ .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+ final PackageSetting calling = simulateAddPackage(appsFilter,
+ pkg("com.some.other.package_a").setSharedUserId("com.some.uid"),
+ DUMMY_CALLING_APPID);
+ simulateAddPackage(appsFilter, pkg("com.some.other.package_b")
+ .setSharedUserId("com.some.uid").addUsesLibrary("com.some.shared_library"),
+ DUMMY_CALLING_APPID);
+
+ // Although package_a doesn't use library, it should be granted visibility. It's because
+ // package_a shares userId with package_b, and package_b uses that shared library.
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
+ }
+
+ @Test
public void testForceQueryable_SystemDoesntFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index bad380acf4b3..51f627ab415c 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -118,6 +118,11 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
}
@Override
+ public MetricsTimeZoneDetectorState generateMetricsState() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void addDumpable(Dumpable dumpable) {
mDumpables.add(dumpable);
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
new file mode 100644
index 000000000000..af954d599334
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.server.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class OrdinalGeneratorTest {
+
+ @Test
+ public void testOrdinal() {
+ OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>();
+ int oneOrd = ordinalGenerator.ordinal("One");
+ int twoOrd = ordinalGenerator.ordinal("Two");
+ assertNotEquals(oneOrd, twoOrd);
+
+ assertEquals(oneOrd, ordinalGenerator.ordinal("One"));
+ assertEquals(twoOrd, ordinalGenerator.ordinal("Two"));
+
+ int threeOrd = ordinalGenerator.ordinal("Three");
+ assertNotEquals(oneOrd, threeOrd);
+ assertNotEquals(twoOrd, threeOrd);
+ }
+
+ @Test
+ public void testOrdinals() {
+ OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>();
+ int[] oneTwoOrds = ordinalGenerator.ordinals(Arrays.asList("One", "Two"));
+ int[] twoThreeOrds = ordinalGenerator.ordinals(Arrays.asList("Two", "Three"));
+ assertEquals(oneTwoOrds[0], ordinalGenerator.ordinal("One"));
+ assertEquals(oneTwoOrds[1], ordinalGenerator.ordinal("Two"));
+ assertEquals(twoThreeOrds[0], ordinalGenerator.ordinal("Two"));
+ assertEquals(twoThreeOrds[1], ordinalGenerator.ordinal("Three"));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index b0341d7d67d5..f91ce87e8f08 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -925,6 +925,106 @@ public class TimeZoneDetectorStrategyImplTest {
assertTrue(dumpCalled.get());
}
+ @Test
+ public void testGenerateMetricsState() {
+ ConfigurationInternal expectedInternalConfig = CONFIG_INT_AUTO_DISABLED_GEO_DISABLED;
+ String expectedDeviceTimeZoneId = "InitialZoneId";
+
+ Script script = new Script()
+ .initializeConfig(expectedInternalConfig)
+ .initializeTimeZoneSetting(expectedDeviceTimeZoneId);
+
+ assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId, null, null,
+ null, MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
+
+ // Make sure the manual suggestion is recorded.
+ ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Zone1");
+ script.simulateManualTimeZoneSuggestion(USER_ID, manualSuggestion,
+ true /* expectedResult */)
+ .verifyTimeZoneChangedAndReset(manualSuggestion);
+ expectedDeviceTimeZoneId = manualSuggestion.getZoneId();
+ assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+ manualSuggestion, null, null,
+ MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
+
+ // With time zone auto detection off, telephony suggestions will be recorded, but geo
+ // suggestions won't out of an abundance of caution around respecting user privacy when
+ // geo detection is off.
+ TelephonyTimeZoneSuggestion telephonySuggestion =
+ createTelephonySuggestion(0 /* slotIndex */, MATCH_TYPE_NETWORK_COUNTRY_ONLY,
+ QUALITY_SINGLE_ZONE, "Zone2");
+ GeolocationTimeZoneSuggestion geolocationTimeZoneSuggestion =
+ createGeoLocationSuggestion(Arrays.asList("Zone3", "Zone2"));
+ script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
+ .verifyTimeZoneNotChanged()
+ .simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion)
+ .verifyTimeZoneNotChanged();
+
+ assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+ manualSuggestion, telephonySuggestion, null /* expectedGeoSuggestion */,
+ MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
+
+ // Update the config and confirm that the config metrics state updates also.
+ TimeZoneConfiguration configUpdate =
+ createConfig(true /* autoDetection */, true /* geoDetection */);
+ expectedInternalConfig = new ConfigurationInternal.Builder(expectedInternalConfig)
+ .setAutoDetectionEnabled(true)
+ .setGeoDetectionEnabled(true)
+ .build();
+ script.simulateUpdateConfiguration(USER_ID, configUpdate, true /* expectedResult */)
+ .verifyConfigurationChangedAndReset(expectedInternalConfig)
+ .verifyTimeZoneNotChanged();
+ assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+ manualSuggestion, telephonySuggestion, null /* expectedGeoSuggestion */,
+ MetricsTimeZoneDetectorState.DETECTION_MODE_GEO);
+
+ // Now simulate a geo suggestion and confirm it is used and reported in the metrics too.
+ expectedDeviceTimeZoneId = geolocationTimeZoneSuggestion.getZoneIds().get(0);
+ script.simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion)
+ .verifyTimeZoneChangedAndReset(expectedDeviceTimeZoneId);
+ assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+ manualSuggestion, telephonySuggestion, geolocationTimeZoneSuggestion,
+ MetricsTimeZoneDetectorState.DETECTION_MODE_GEO);
+ }
+
+ /**
+ * Asserts that the information returned by {@link
+ * TimeZoneDetectorStrategy#generateMetricsState()} matches expectations.
+ */
+ private void assertMetricsState(
+ ConfigurationInternal expectedInternalConfig,
+ String expectedDeviceTimeZoneId, ManualTimeZoneSuggestion expectedManualSuggestion,
+ TelephonyTimeZoneSuggestion expectedTelephonySuggestion,
+ GeolocationTimeZoneSuggestion expectedGeolocationTimeZoneSuggestion,
+ int expectedDetectionMode) {
+
+ MetricsTimeZoneDetectorState actualState = mTimeZoneDetectorStrategy.generateMetricsState();
+
+ // Check the various feature state values are what we expect.
+ assertFeatureStateMatchesConfig(expectedInternalConfig, actualState, expectedDetectionMode);
+
+ OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>();
+ MetricsTimeZoneDetectorState expectedState =
+ MetricsTimeZoneDetectorState.create(
+ tzIdOrdinalGenerator, expectedInternalConfig, expectedDeviceTimeZoneId,
+ expectedManualSuggestion, expectedTelephonySuggestion,
+ expectedGeolocationTimeZoneSuggestion);
+ // Rely on MetricsTimeZoneDetectorState.equals() for time zone ID ordinal comparisons.
+ assertEquals(expectedState, actualState);
+ }
+
+ private static void assertFeatureStateMatchesConfig(ConfigurationInternal config,
+ MetricsTimeZoneDetectorState actualState, int expectedDetectionMode) {
+ assertEquals(config.isTelephonyDetectionSupported(),
+ actualState.isTelephonyDetectionSupported());
+ assertEquals(config.isGeoDetectionSupported(), actualState.isGeoDetectionSupported());
+ assertEquals(config.getAutoDetectionEnabledSetting(),
+ actualState.getAutoDetectionEnabledSetting());
+ assertEquals(config.getGeoDetectionEnabledSetting(),
+ actualState.getGeoDetectionEnabledSetting());
+ assertEquals(expectedDetectionMode, actualState.getDetectionMode());
+ }
+
private static ManualTimeZoneSuggestion createManualSuggestion(String zoneId) {
return new ManualTimeZoneSuggestion(zoneId);
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
index 3daa7f0483c6..5a100a297cfc 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
@@ -79,15 +79,18 @@ public class ControllerImplTest {
// For simplicity, the TestThreadingDomain uses the test's main thread. To execute posted
// runnables, the test must call methods on mTestThreadingDomain otherwise those runnables
// will never get a chance to execute.
+ LocationTimeZoneProvider.ProviderMetricsLogger stubbedProviderMetricsLogger = stateEnum -> {
+ // Stubbed.
+ };
mTestThreadingDomain = new TestThreadingDomain();
mTestCallback = new TestCallback(mTestThreadingDomain);
mTimeZoneAvailabilityChecker = new FakeTimeZoneIdValidator();
- mTestPrimaryLocationTimeZoneProvider =
- new TestLocationTimeZoneProvider(
- mTestThreadingDomain, "primary", mTimeZoneAvailabilityChecker);
- mTestSecondaryLocationTimeZoneProvider =
- new TestLocationTimeZoneProvider(
- mTestThreadingDomain, "secondary", mTimeZoneAvailabilityChecker);
+ mTestPrimaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(
+ stubbedProviderMetricsLogger, mTestThreadingDomain, "primary",
+ mTimeZoneAvailabilityChecker);
+ mTestSecondaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(
+ stubbedProviderMetricsLogger, mTestThreadingDomain, "secondary",
+ mTimeZoneAvailabilityChecker);
}
@Test
@@ -1181,10 +1184,11 @@ public class ControllerImplTest {
/**
* Creates the instance.
*/
- TestLocationTimeZoneProvider(ThreadingDomain threadingDomain,
- String providerName,
+ TestLocationTimeZoneProvider(ProviderMetricsLogger providerMetricsLogger,
+ ThreadingDomain threadingDomain, String providerName,
TimeZoneIdValidator timeZoneIdValidator) {
- super(threadingDomain, providerName, timeZoneIdValidator);
+ super(providerMetricsLogger, threadingDomain, providerName,
+ timeZoneIdValidator);
}
public void setFailDuringInitialization(boolean failInitialization) {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
index 278fdaff260f..d13a04e13406 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
@@ -53,6 +53,7 @@ import org.junit.Test;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
@@ -79,19 +80,18 @@ public class LocationTimeZoneProviderTest {
@Test
public void lifecycle() {
String providerName = "arbitrary";
- TestLocationTimeZoneProvider provider =
- new TestLocationTimeZoneProvider(
- mTestThreadingDomain,
- providerName,
- mTimeZoneAvailabilityChecker);
+ RecordingProviderMetricsLogger providerMetricsLogger = new RecordingProviderMetricsLogger();
+ TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+ providerMetricsLogger, mTestThreadingDomain, providerName,
+ mTimeZoneAvailabilityChecker);
mTimeZoneAvailabilityChecker.validIds("Europe/London");
// initialize()
provider.initialize(mProviderListener);
provider.assertOnInitializeCalled();
- ProviderState currentState = provider.getCurrentState();
- assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
+ ProviderState currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STOPPED);
assertNull(currentState.currentUserConfiguration);
assertSame(provider, currentState.provider);
mTestThreadingDomain.assertQueueEmpty();
@@ -105,9 +105,9 @@ public class LocationTimeZoneProviderTest {
provider.assertOnStartCalled(arbitraryInitializationTimeout);
- currentState = provider.getCurrentState();
+ currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STARTED_INITIALIZING);
assertSame(provider, currentState.provider);
- assertEquals(PROVIDER_STATE_STARTED_INITIALIZING, currentState.stateEnum);
assertEquals(config, currentState.currentUserConfiguration);
assertNull(currentState.event);
// The initialization timeout should be queued.
@@ -129,9 +129,9 @@ public class LocationTimeZoneProviderTest {
TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(suggestion);
provider.simulateProviderEventReceived(event);
- currentState = provider.getCurrentState();
+ currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STARTED_CERTAIN);
assertSame(provider, currentState.provider);
- assertEquals(PROVIDER_STATE_STARTED_CERTAIN, currentState.stateEnum);
assertEquals(event, currentState.event);
assertEquals(config, currentState.currentUserConfiguration);
mTestThreadingDomain.assertQueueEmpty();
@@ -141,9 +141,9 @@ public class LocationTimeZoneProviderTest {
event = TimeZoneProviderEvent.createUncertainEvent();
provider.simulateProviderEventReceived(event);
- currentState = provider.getCurrentState();
+ currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STARTED_UNCERTAIN);
assertSame(provider, currentState.provider);
- assertEquals(PROVIDER_STATE_STARTED_UNCERTAIN, currentState.stateEnum);
assertEquals(event, currentState.event);
assertEquals(config, currentState.currentUserConfiguration);
mTestThreadingDomain.assertQueueEmpty();
@@ -153,7 +153,8 @@ public class LocationTimeZoneProviderTest {
provider.stopUpdates();
provider.assertOnStopUpdatesCalled();
- currentState = provider.getCurrentState();
+ currentState = assertAndReturnProviderState(
+ provider, providerMetricsLogger, PROVIDER_STATE_STOPPED);
assertSame(provider, currentState.provider);
assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
assertNull(currentState.event);
@@ -171,11 +172,10 @@ public class LocationTimeZoneProviderTest {
@Test
public void defaultHandleTestCommandImpl() {
String providerName = "primary";
- TestLocationTimeZoneProvider provider =
- new TestLocationTimeZoneProvider(
- mTestThreadingDomain,
- providerName,
- mTimeZoneAvailabilityChecker);
+ StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
+ TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+ providerMetricsLogger, mTestThreadingDomain, providerName,
+ mTimeZoneAvailabilityChecker);
TestCommand testCommand = TestCommand.createForTests("test", new Bundle());
AtomicReference<Bundle> resultReference = new AtomicReference<>();
@@ -191,11 +191,10 @@ public class LocationTimeZoneProviderTest {
@Test
public void stateRecording() {
String providerName = "primary";
- TestLocationTimeZoneProvider provider =
- new TestLocationTimeZoneProvider(
- mTestThreadingDomain,
- providerName,
- mTimeZoneAvailabilityChecker);
+ StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
+ TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+ providerMetricsLogger, mTestThreadingDomain, providerName,
+ mTimeZoneAvailabilityChecker);
provider.setStateChangeRecordingEnabled(true);
mTimeZoneAvailabilityChecker.validIds("Europe/London");
@@ -237,11 +236,10 @@ public class LocationTimeZoneProviderTest {
@Test
public void considerSuggestionWithInvalidTimeZoneIdsAsUncertain() {
String providerName = "primary";
- TestLocationTimeZoneProvider provider =
- new TestLocationTimeZoneProvider(
- mTestThreadingDomain,
- providerName,
- mTimeZoneAvailabilityChecker);
+ StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
+ TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+ providerMetricsLogger, mTestThreadingDomain, providerName,
+ mTimeZoneAvailabilityChecker);
provider.setStateChangeRecordingEnabled(true);
provider.initialize(mProviderListener);
@@ -285,6 +283,20 @@ public class LocationTimeZoneProviderTest {
}
}
+ /**
+ * Returns the provider's state after asserting that the current state matches what is expected.
+ * This also asserts that the metrics logger was informed of the state change.
+ */
+ private static ProviderState assertAndReturnProviderState(
+ TestLocationTimeZoneProvider provider,
+ RecordingProviderMetricsLogger providerMetricsLogger, int expectedStateEnum) {
+ ProviderState currentState = provider.getCurrentState();
+ assertEquals(expectedStateEnum, currentState.stateEnum);
+ providerMetricsLogger.assertChangeLoggedAndRemove(expectedStateEnum);
+ providerMetricsLogger.assertNoMoreLogEntries();
+ return currentState;
+ }
+
private static class TestLocationTimeZoneProvider extends LocationTimeZoneProvider {
private boolean mOnInitializeCalled;
@@ -294,10 +306,11 @@ public class LocationTimeZoneProviderTest {
private boolean mOnStopUpdatesCalled;
/** Creates the instance. */
- TestLocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
+ TestLocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger,
+ @NonNull ThreadingDomain threadingDomain,
@NonNull String providerName,
@NonNull TimeZoneIdValidator timeZoneIdValidator) {
- super(threadingDomain, providerName, timeZoneIdValidator);
+ super(providerMetricsLogger, threadingDomain, providerName, timeZoneIdValidator);
}
@Override
@@ -366,6 +379,36 @@ public class LocationTimeZoneProviderTest {
public void validIds(String... timeZoneIdss) {
mValidTimeZoneIds.addAll(asList(timeZoneIdss));
}
+ }
+
+ private static class StubbedProviderMetricsLogger implements
+ LocationTimeZoneProvider.ProviderMetricsLogger {
+ @Override
+ public void onProviderStateChanged(int stateEnum) {
+ // Stubbed
+ }
+ }
+
+ private static class RecordingProviderMetricsLogger implements
+ LocationTimeZoneProvider.ProviderMetricsLogger {
+
+ private LinkedList<Integer> mStates = new LinkedList<>();
+
+ @Override
+ public void onProviderStateChanged(int stateEnum) {
+ mStates.add(stateEnum);
+ }
+
+ public void assertChangeLoggedAndRemove(int expectedLoggedState) {
+ assertEquals("expected loggedState=" + expectedLoggedState
+ + " but states logged were=" + mStates,
+ (Integer) expectedLoggedState, mStates.peekFirst());
+ mStates.removeFirst();
+ }
+
+ public void assertNoMoreLogEntries() {
+ assertTrue(mStates.isEmpty());
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 2a3c2c46ce4e..b54b6969e7df 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -49,6 +49,8 @@ final class FakeVibratorControllerProvider {
private int mCapabilities;
private int[] mSupportedEffects;
private int[] mSupportedPrimitives;
+ private float mResonantFrequency;
+ private float mQFactor;
private final class FakeNativeWrapper extends VibratorController.NativeWrapper {
public int vibratorId;
@@ -89,6 +91,14 @@ final class FakeVibratorControllerProvider {
return mSupportedPrimitives;
}
+ public float getResonantFrequency() {
+ return mResonantFrequency;
+ }
+
+ public float getQFactor() {
+ return mQFactor;
+ }
+
public long perform(long effect, long strength, long vibrationId) {
if (mSupportedEffects == null
|| Arrays.binarySearch(mSupportedEffects, (int) effect) < 0) {
@@ -198,6 +208,16 @@ final class FakeVibratorControllerProvider {
mSupportedPrimitives = primitives;
}
+ /** Set the resonant frequency of the fake vibrator hardware. */
+ public void setResonantFrequency(float resonantFrequency) {
+ mResonantFrequency = resonantFrequency;
+ }
+
+ /** Set the Q factor of the fake vibrator hardware. */
+ public void setQFactor(float qFactor) {
+ mQFactor = qFactor;
+ }
+
/**
* Return the amplitudes set by this controller, including zeroes for each time the vibrator was
* turned off.
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index a28d18fb74d3..ce6639c6b4aa 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -266,6 +266,8 @@ public class VibratorManagerServiceTest {
vibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL);
vibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
vibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
+ vibrator.setResonantFrequency(123.f);
+ vibrator.setQFactor(Float.NaN);
VibratorInfo info = createSystemReadyService().getVibratorInfo(1);
assertNotNull(info);
@@ -279,6 +281,8 @@ public class VibratorManagerServiceTest {
info.isEffectSupported(VibrationEffect.EFFECT_TICK));
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
+ assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
+ assertTrue(Float.isNaN(info.getQFactor()));
}
@Test
diff --git a/telecomm/java/android/telecom/CallDiagnosticService.java b/telecomm/java/android/telecom/CallDiagnosticService.java
index 201c5db74e16..809f2bc1bb7d 100644
--- a/telecomm/java/android/telecom/CallDiagnosticService.java
+++ b/telecomm/java/android/telecom/CallDiagnosticService.java
@@ -19,9 +19,12 @@ package android.telecom;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -30,6 +33,7 @@ import com.android.internal.telecom.ICallDiagnosticService;
import com.android.internal.telecom.ICallDiagnosticServiceAdapter;
import java.util.Map;
+import java.util.concurrent.Executor;
/**
* The platform supports a single OEM provided {@link CallDiagnosticService}, as defined by the
@@ -51,6 +55,11 @@ import java.util.Map;
* </service>
* }
* </pre>
+ * <p>
+ * <h2>Threading Model</h2>
+ * By default, all incoming IPC from Telecom in this service and in the {@link DiagnosticCall}
+ * instances will take place on the main thread. You can override {@link #getExecutor()} in your
+ * implementation to provide your own {@link Executor}.
* @hide
*/
@SystemApi
@@ -83,7 +92,7 @@ public abstract class CallDiagnosticService extends Service {
@Override
public void updateCallAudioState(CallAudioState callAudioState) throws RemoteException {
- onCallAudioStateChanged(callAudioState);
+ getExecutor().execute(() -> onCallAudioStateChanged(callAudioState));
}
@Override
@@ -133,8 +142,18 @@ public abstract class CallDiagnosticService extends Service {
*/
private final Map<String, Call.Details> mCallByTelecomCallId = new ArrayMap<>();
private final Map<String, DiagnosticCall> mDiagnosticCallByTelecomCallId = new ArrayMap<>();
+ private final Object mLock = new Object();
private ICallDiagnosticServiceAdapter mAdapter;
+ /**
+ * Handles binding to the {@link CallDiagnosticService}.
+ *
+ * @param intent The Intent that was used to bind to this service,
+ * as given to {@link android.content.Context#bindService
+ * Context.bindService}. Note that any extras that were included with
+ * the Intent at that point will <em>not</em> be seen here.
+ * @return
+ */
@Nullable
@Override
public IBinder onBind(@NonNull Intent intent) {
@@ -143,11 +162,29 @@ public abstract class CallDiagnosticService extends Service {
}
/**
+ * Returns the {@link Executor} to use for incoming IPS from Telecom into your service
+ * implementation.
+ * <p>
+ * Override this method in your {@link CallDiagnosticService} implementation to provide the
+ * executor you want to use for incoming IPC.
+ *
+ * @return the {@link Executor} to use for incoming IPC from Telecom to
+ * {@link CallDiagnosticService} and {@link DiagnosticCall}.
+ */
+ @SuppressLint("OnNameExpected")
+ @NonNull public Executor getExecutor() {
+ return new HandlerExecutor(Handler.createAsync(getMainLooper()));
+ }
+
+ /**
* Telecom calls this method on the {@link CallDiagnosticService} with details about a new call
* which was added to Telecom.
* <p>
* The {@link CallDiagnosticService} returns an implementation of {@link DiagnosticCall} to be
* used for the lifespan of this call.
+ * <p>
+ * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+ * {@link CallDiagnosticService#getExecutor()} for more information.
*
* @param call The details of the new call.
* @return An instance of {@link DiagnosticCall} which the {@link CallDiagnosticService}
@@ -160,6 +197,10 @@ public abstract class CallDiagnosticService extends Service {
/**
* Telecom calls this method when a previous created {@link DiagnosticCall} is no longer needed.
* This happens when Telecom is no longer tracking the call in question.
+ * <p>
+ * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+ * {@link CallDiagnosticService#getExecutor()} for more information.
+ *
* @param call The diagnostic call which is no longer tracked by Telecom.
*/
public abstract void onRemoveDiagnosticCall(@NonNull DiagnosticCall call);
@@ -169,6 +210,9 @@ public abstract class CallDiagnosticService extends Service {
* changes.
* <p>
* Audio state is common to all calls.
+ * <p>
+ * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+ * {@link CallDiagnosticService#getExecutor()} for more information.
*
* @param audioState The new audio state.
*/
@@ -178,6 +222,10 @@ public abstract class CallDiagnosticService extends Service {
/**
* Telecom calls this method when a {@link BluetoothCallQualityReport} is received from the
* bluetooth stack.
+ * <p>
+ * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
+ * {@link CallDiagnosticService#getExecutor()} for more information.
+ *
* @param qualityReport the {@link BluetoothCallQualityReport}.
*/
public abstract void onBluetoothCallQualityReportReceived(
@@ -199,15 +247,22 @@ public abstract class CallDiagnosticService extends Service {
String telecomCallId = parcelableCall.getId();
Log.i(this, "handleCallAdded: callId=%s - added", telecomCallId);
Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall);
- mCallByTelecomCallId.put(telecomCallId, newCallDetails);
-
- DiagnosticCall diagnosticCall = onInitializeDiagnosticCall(newCallDetails);
- if (diagnosticCall == null) {
- throw new IllegalArgumentException("A valid DiagnosticCall instance was not provided.");
+ synchronized (mLock) {
+ mCallByTelecomCallId.put(telecomCallId, newCallDetails);
}
- diagnosticCall.setListener(mDiagnosticCallListener);
- diagnosticCall.setCallId(telecomCallId);
- mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall);
+
+ getExecutor().execute(() -> {
+ DiagnosticCall diagnosticCall = onInitializeDiagnosticCall(newCallDetails);
+ if (diagnosticCall == null) {
+ throw new IllegalArgumentException(
+ "A valid DiagnosticCall instance was not provided.");
+ }
+ synchronized (mLock) {
+ diagnosticCall.setListener(mDiagnosticCallListener);
+ diagnosticCall.setCallId(telecomCallId);
+ mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall);
+ }
+ });
}
/**
@@ -220,10 +275,12 @@ public abstract class CallDiagnosticService extends Service {
String telecomCallId = parcelableCall.getId();
Log.i(this, "handleCallUpdated: callId=%s - updated", telecomCallId);
Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall);
-
- DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(telecomCallId);
- mCallByTelecomCallId.put(telecomCallId, newCallDetails);
- diagnosticCall.handleCallUpdated(newCallDetails);
+ DiagnosticCall diagnosticCall;
+ synchronized (mLock) {
+ diagnosticCall = mDiagnosticCallByTelecomCallId.get(telecomCallId);
+ mCallByTelecomCallId.put(telecomCallId, newCallDetails);
+ }
+ getExecutor().execute(() -> diagnosticCall.handleCallUpdated(newCallDetails));
}
/**
@@ -236,10 +293,19 @@ public abstract class CallDiagnosticService extends Service {
if (mCallByTelecomCallId.containsKey(telecomCallId)) {
mCallByTelecomCallId.remove(telecomCallId);
}
- if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) {
- DiagnosticCall call = mDiagnosticCallByTelecomCallId.remove(telecomCallId);
- // Inform the service of the removed call.
- onRemoveDiagnosticCall(call);
+
+ DiagnosticCall diagnosticCall;
+ synchronized (mLock) {
+ if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) {
+ diagnosticCall = mDiagnosticCallByTelecomCallId.remove(telecomCallId);
+ } else {
+ diagnosticCall = null;
+ }
+ }
+
+ // Inform the service of the removed call.
+ if (diagnosticCall != null) {
+ getExecutor().execute(() -> onRemoveDiagnosticCall(diagnosticCall));
}
}
@@ -252,8 +318,14 @@ public abstract class CallDiagnosticService extends Service {
*/
private void handleReceivedD2DMessage(@NonNull String callId, int message, int value) {
Log.i(this, "handleReceivedD2DMessage: callId=%s, msg=%d/%d", callId, message, value);
- DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId);
- diagnosticCall.onReceiveDeviceToDeviceMessage(message, value);
+ DiagnosticCall diagnosticCall;
+ synchronized (mLock) {
+ diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId);
+ }
+ if (diagnosticCall != null) {
+ getExecutor().execute(
+ () -> diagnosticCall.onReceiveDeviceToDeviceMessage(message, value));
+ }
}
/**
@@ -265,7 +337,7 @@ public abstract class CallDiagnosticService extends Service {
private void handleBluetoothCallQualityReport(@NonNull BluetoothCallQualityReport
qualityReport) {
Log.i(this, "handleBluetoothCallQualityReport; report=%s", qualityReport);
- onBluetoothCallQualityReportReceived(qualityReport);
+ getExecutor().execute(() -> onBluetoothCallQualityReportReceived(qualityReport));
}
/**
diff --git a/telecomm/java/android/telecom/DiagnosticCall.java b/telecomm/java/android/telecom/DiagnosticCall.java
index a4952899eb46..af46b7759fb5 100644
--- a/telecomm/java/android/telecom/DiagnosticCall.java
+++ b/telecomm/java/android/telecom/DiagnosticCall.java
@@ -26,15 +26,27 @@ import android.telephony.ims.ImsReasonInfo;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
/**
* A {@link DiagnosticCall} provides a way for a {@link CallDiagnosticService} to receive diagnostic
- * information about a mobile call on the device. The {@link CallDiagnosticService} can generate
- * mid-call diagnostic messages using the {@link #displayDiagnosticMessage(int, CharSequence)} API
- * which provides the user with valuable information about conditions impacting their call and
- * corrective actions. For example, if the {@link CallDiagnosticService} determines that conditions
- * on the call are degrading, it can inform the user that the call may soon drop and that they
- * can try using a different calling method (e.g. VOIP or WIFI).
+ * information about a mobile call on the device. A {@link DiagnosticCall} is similar to a
+ * {@link Call}, however it does not expose call control capabilities and exposes extra diagnostic
+ * and messaging capabilities not present on a {@link Call}. The {@link CallDiagnosticService}
+ * creates a {@link DiagnosticCall} for each {@link Call} on the device. This means that for each
+ * in progress call on the device, the {@link CallDiagnosticService} will create an instance of
+ * {@link DiagnosticCall}.
+ * <p>
+ * The {@link CallDiagnosticService} can generate mid-call diagnostic messages using the
+ * {@link #displayDiagnosticMessage(int, CharSequence)} API which provides the user with valuable
+ * information about conditions impacting their call and corrective actions. For example, if the
+ * {@link CallDiagnosticService} determines that conditions on the call are degrading, it can inform
+ * the user that the call may soon drop and that they can try using a different calling method
+ * (e.g. VOIP or WIFI).
+ * <h2>Threading Model</h2>
+ * All incoming IPC from Telecom in this class will use the same {@link Executor} as the
+ * {@link CallDiagnosticService}. See {@link CallDiagnosticService#setExecutor(Executor)} for more
+ * information.
* @hide
*/
@SystemApi
@@ -53,15 +65,19 @@ public abstract class DiagnosticCall {
/**
* Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
* {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type
- * used for the current call. Based loosely on the
- * {@link android.telephony.TelephonyManager#getNetworkType(int)} for the call, provides a
- * high level summary of the call radio access type.
+ * used for the current call. The call network type communicated here is an intentional
+ * simplification of the {@link android.telephony.TelephonyManager#getNetworkType(int)} which
+ * removes some of the resolution inherent in those values; the
+ * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE_CA} value, for example is
+ * collapsed into the {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE} value for
+ * efficiency of transport. For a discussion on the necessity of this simplification, see
+ * {@link #sendDeviceToDeviceMessage(int, int)}.
* <p>
- * Valid values:
+ * Valid values are below:
* <UL>
- * <LI>{@link #NETWORK_TYPE_LTE}</LI>
- * <LI>{@link #NETWORK_TYPE_IWLAN}</LI>
- * <LI>{@link #NETWORK_TYPE_NR}</LI>
+ * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}</LI>
+ * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_IWLAN}</LI>
+ * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_NR}</LI>
* </UL>
*/
public static final int MESSAGE_CALL_NETWORK_TYPE = 1;
@@ -69,14 +85,21 @@ public abstract class DiagnosticCall {
/**
* Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
* {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec
- * used for the current call. Based loosely on the {@link Connection#EXTRA_AUDIO_CODEC} for a
- * call.
+ * used for the current call.
+ * <p>
+ * The audio codec communicated here is an intentional simplification of the
+ * {@link Connection#EXTRA_AUDIO_CODEC} for a call and focuses on communicating the most common
+ * variants of these audio codecs. Other variants of these codecs are reported as the next
+ * closest variant. For example, the {@link Connection#AUDIO_CODEC_EVS_FB} full band codec
+ * is reported via device to device communication as {@link Connection#AUDIO_CODEC_EVS_WB}.
+ * For a discussion on the necessity of this simplification, see
+ * {@link #sendDeviceToDeviceMessage(int, int)}.
* <p>
* Valid values:
* <UL>
- * <LI>{@link #AUDIO_CODEC_EVS}</LI>
- * <LI>{@link #AUDIO_CODEC_AMR_WB}</LI>
- * <LI>{@link #AUDIO_CODEC_AMR_NB}</LI>
+ * <LI>{@link Connection#AUDIO_CODEC_EVS_WB}</LI>
+ * <LI>{@link Connection#AUDIO_CODEC_AMR_WB}</LI>
+ * <LI>{@link Connection#AUDIO_CODEC_AMR}</LI>
* </UL>
*/
public static final int MESSAGE_CALL_AUDIO_CODEC = 2;
@@ -122,41 +145,6 @@ public abstract class DiagnosticCall {
public @interface MessageType {}
/**
- * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate an LTE network is being used for the
- * call.
- */
- public static final int NETWORK_TYPE_LTE = 1;
-
- /**
- * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate WIFI calling is in use for the call.
- */
- public static final int NETWORK_TYPE_IWLAN = 2;
-
- /**
- * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate a 5G NR (new radio) network is in
- * used for the call.
- */
- public static final int NETWORK_TYPE_NR = 3;
-
- /**
- * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the
- * Enhanced Voice Services (EVS) codec for the call.
- */
- public static final int AUDIO_CODEC_EVS = 1;
-
- /**
- * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the AMR
- * (adaptive multi-rate) WB (wide band) audio codec.
- */
- public static final int AUDIO_CODEC_AMR_WB = 2;
-
- /**
- * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the AMR
- * (adaptive multi-rate) NB (narrow band) audio codec.
- */
- public static final int AUDIO_CODEC_AMR_NB = 3;
-
- /**
* Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low.
*/
public static final int BATTERY_STATE_LOW = 1;
@@ -183,7 +171,6 @@ public abstract class DiagnosticCall {
private Listener mListener;
private String mCallId;
- private Call.Details mCallDetails;
/**
* @hide
@@ -210,16 +197,10 @@ public abstract class DiagnosticCall {
}
/**
- * Returns the latest {@link Call.Details} associated with this {@link DiagnosticCall} as
- * reported by {@link #onCallDetailsChanged(Call.Details)}.
- * @return The latest {@link Call.Details}.
- */
- public @NonNull Call.Details getCallDetails() {
- return mCallDetails;
- }
-
- /**
* Telecom calls this method when the details of a call changes.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
*/
public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details);
@@ -234,6 +215,9 @@ public abstract class DiagnosticCall {
* devices communicating are using a different version of the protocol, messages the recipient
* are not aware of are silently discarded. This means an older client talking to a new client
* will not receive newer messages and values sent by the new client.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
*/
public abstract void onReceiveDeviceToDeviceMessage(
@MessageType int message,
@@ -253,39 +237,19 @@ public abstract class DiagnosticCall {
* platform due to the extreme bandwidth constraints inherent with underlying device to device
* communication transports used by the telephony framework. Device to device communication is
* either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets
- * for a call, or using the DTMF digits A-D as a communication pathway. Signalling requirements
- * for DTMF digits place a significant limitation on the amount of information which can be
- * communicated during a call.
+ * for a call, or using the DTMF digits A-D as a communication pathway. RTP header extension
+ * packets ride alongside a the audio for a call, and are thus limited to roughly a byte for
+ * a message. Signalling requirements for DTMF digits place even more significant limitations
+ * on the amount of information which can be communicated during a call, offering only a few
+ * bits of potential information per message. The messages and values are constrained in order
+ * to meet the limited bandwidth inherent with DTMF signalling.
* <p>
- * Allowed message types and values are:
+ * Allowed message types are:
* <ul>
- * <li>{@link #MESSAGE_CALL_NETWORK_TYPE}
- * <ul>
- * <li>{@link #NETWORK_TYPE_LTE}</li>
- * <li>{@link #NETWORK_TYPE_IWLAN}</li>
- * <li>{@link #NETWORK_TYPE_NR}</li>
- * </ul>
- * </li>
- * <li>{@link #MESSAGE_CALL_AUDIO_CODEC}
- * <ul>
- * <li>{@link #AUDIO_CODEC_EVS}</li>
- * <li>{@link #AUDIO_CODEC_AMR_WB}</li>
- * <li>{@link #AUDIO_CODEC_AMR_NB}</li>
- * </ul>
- * </li>
- * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}
- * <ul>
- * <li>{@link #BATTERY_STATE_LOW}</li>
- * <li>{@link #BATTERY_STATE_GOOD}</li>
- * <li>{@link #BATTERY_STATE_CHARGING}</li>
- * </ul>
- * </li>
- * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}
- * <ul>
- * <li>{@link #COVERAGE_POOR}</li>
- * <li>{@link #COVERAGE_GOOD}</li>
- * </ul>
- * </li>
+ * <li>{@link #MESSAGE_CALL_NETWORK_TYPE}</LI>
+ * <li>{@link #MESSAGE_CALL_AUDIO_CODEC}</LI>
+ * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}</LI>
+ * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}</LI>
* </ul>
* @param message The message type to send.
* @param value The message value corresponding to the type.
@@ -307,6 +271,9 @@ public abstract class DiagnosticCall {
* @param preciseDisconnectCause the precise disconnect cause for the call.
* @return the disconnect message to use in place of the default Telephony message, or
* {@code null} if the default message will not be overridden.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
*/
// TODO: Wire in Telephony support for this.
public abstract @Nullable CharSequence onCallDisconnected(
@@ -323,6 +290,9 @@ public abstract class DiagnosticCall {
* @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection.
* @return A user-readable call disconnect message to use in place of the platform-generated
* disconnect message, or {@code null} if the disconnect message should not be overridden.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
*/
// TODO: Wire in Telephony support for this.
public abstract @Nullable CharSequence onCallDisconnected(
@@ -332,6 +302,9 @@ public abstract class DiagnosticCall {
* Telecom calls this method when a {@link CallQuality} report is received from the telephony
* stack for a call.
* @param callQuality The call quality report for this call.
+ * <p>
+ * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+ * see {@link CallDiagnosticService#getExecutor()} for more information.
*/
public abstract void onCallQualityReceived(@NonNull CallQuality callQuality);
@@ -375,7 +348,6 @@ public abstract class DiagnosticCall {
* @hide
*/
public void handleCallUpdated(@NonNull Call.Details newDetails) {
- mCallDetails = newDetails;
onCallDetailsChanged(newDetails);
}
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 17749e8b0a8f..1677c8c1f08e 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -314,8 +314,8 @@ public class TelecomManager {
public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
/**
- * A URI representing the picture that was downloaded when a call is received or uploaded
- * when a call is placed.
+ * A {@link Uri} representing the picture that was downloaded when a call is received or
+ * uploaded when a call is placed.
*
* This is a content URI within the call log provider which can be used to open a file
* descriptor. This could be set a short time after a call is added to the Dialer app if the
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2d5f5fb58306..f7580d77186d 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -602,6 +602,43 @@ public class SubscriptionManager {
public @interface SimDisplayNameSource {}
/**
+ * Device status is not shared to a remote party.
+ */
+ public static final int D2D_SHARING_DISABLED = 0;
+
+ /**
+ * Device status is shared with all numbers in the user's contacts.
+ */
+ public static final int D2D_SHARING_ALL_CONTACTS = 1;
+
+ /**
+ * Device status is shared with all starred contacts.
+ */
+ public static final int D2D_SHARING_STARRED_CONTACTS = 2;
+
+ /**
+ * Device status is shared whenever possible.
+ */
+ public static final int D2D_SHARING_ALL = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"D2D_SHARING_"},
+ value = {
+ D2D_SHARING_DISABLED,
+ D2D_SHARING_ALL_CONTACTS,
+ D2D_SHARING_STARRED_CONTACTS,
+ D2D_SHARING_ALL
+ })
+ public @interface DeviceToDeviceStatusSharing {}
+
+ /**
+ * TelephonyProvider column name for device to device sharing status.
+ * <P>Type: INTEGER (int)</P>
+ */
+ public static final String D2D_STATUS_SHARING = SimInfo.COLUMN_D2D_STATUS_SHARING;
+
+ /**
* TelephonyProvider column name for the color of a SIM.
* <P>Type: INTEGER (int)</P>
*/
@@ -3374,6 +3411,36 @@ public class SubscriptionManager {
}
/**
+ * Set the device to device status sharing user preference for a subscription ID. The setting
+ * app uses this method to indicate with whom they wish to share device to device status
+ * information.
+ * @param sharing the status sharing preference
+ * @param subId the unique Subscription ID in database
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDeviceToDeviceStatusSharing(@DeviceToDeviceStatusSharing int sharing,
+ int subId) {
+ if (VDBG) {
+ logd("[setDeviceToDeviceStatusSharing] + sharing: " + sharing + " subId: " + subId);
+ }
+ setSubscriptionPropertyHelper(subId, "setDeviceToDeviceSharingStatus",
+ (iSub)->iSub.setDeviceToDeviceStatusSharing(sharing, subId));
+ }
+
+ /**
+ * Returns the user-chosen device to device status sharing preference
+ * @param subId Subscription id of subscription
+ * @return The device to device status sharing preference
+ */
+ public @DeviceToDeviceStatusSharing int getDeviceToDeviceStatusSharing(int subId) {
+ if (VDBG) {
+ logd("[getDeviceToDeviceStatusSharing] + subId: " + subId);
+ }
+ return getIntegerSubscriptionProperty(subId, D2D_STATUS_SHARING, D2D_SHARING_DISABLED,
+ mContext);
+ }
+
+ /**
* DO NOT USE.
* This API is designed for features that are not finished at this point. Do not call this API.
* @hide
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 3cb72b5e0c0d..4dc6c7ce35cf 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -139,6 +139,8 @@ import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
/**
* Provides access to information about the telephony services on
@@ -3147,6 +3149,10 @@ public class TelephonyManager {
return NETWORK_TYPE_BITMASK_LTE_CA;
case NETWORK_TYPE_NR:
return NETWORK_TYPE_BITMASK_NR;
+ case NETWORK_TYPE_IWLAN:
+ return NETWORK_TYPE_BITMASK_IWLAN;
+ case NETWORK_TYPE_IDEN:
+ return (1 << (NETWORK_TYPE_IDEN - 1));
default:
return NETWORK_TYPE_BITMASK_UNKNOWN;
}
@@ -8642,8 +8648,8 @@ public class TelephonyManager {
public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3;
/**
- * Set the allowed network types of the device and
- * provide the reason triggering the allowed network change.
+ * Set the allowed network types of the device and provide the reason triggering the allowed
+ * network change.
* This can be called for following reasons
* <ol>
* <li>Allowed network types control by USER {@link #ALLOWED_NETWORK_TYPES_REASON_USER}
@@ -8655,10 +8661,15 @@ public class TelephonyManager {
* </ol>
* This API will result in allowing an intersection of allowed network types for all reasons,
* including the configuration done through other reasons.
+ *
+ * The functionality of this API with the parameter
+ * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER} is the same as the API
+ * {@link TelephonyManager#setAllowedNetworkTypes}. Use this API instead of
+ * {@link TelephonyManager#setAllowedNetworkTypes}.
* <p>
* If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
* ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
- * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise,
+ * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise,
* setPreferredNetworkTypesBitmap is used instead.
*
* @param reason the reason the allowed network type change is taking place
@@ -8698,21 +8709,17 @@ public class TelephonyManager {
* {@link #getAllowedNetworkTypesForReason} returns allowed network type for a
* specific reason.
*
- * <p>Requires Permission:
- * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
- * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
- *
* @param reason the reason the allowed network type change is taking place
* @return the allowed network type bitmask
* @throws IllegalStateException if the Telephony process is not currently available.
* @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(
enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED)
+ @SystemApi
public @NetworkTypeBitMask long getAllowedNetworkTypesForReason(
@AllowedNetworkTypesReason int reason) {
if (!isValidAllowedNetworkTypesReason(reason)) {
@@ -8757,6 +8764,25 @@ public class TelephonyManager {
}
/**
+ * Returns a string representation of the allowed network types{@link NetworkTypeBitMask}.
+ *
+ * @param networkTypeBitmask The bitmask of allowed network types.
+ * @return the name of the allowed network types
+ * @hide
+ */
+ public static String convertNetworkTypeBitmaskToString(
+ @NetworkTypeBitMask long networkTypeBitmask) {
+ String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NR)
+ .filter(x -> {
+ return (networkTypeBitmask & getBitMaskForNetworkType(x))
+ == getBitMaskForNetworkType(x);
+ })
+ .mapToObj(x -> getNetworkTypeName(x))
+ .collect(Collectors.joining("|"));
+ return TextUtils.isEmpty(networkTypeName) ? "UNKNOWN" : networkTypeName;
+ }
+
+ /**
* Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
*
* <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -9135,9 +9161,7 @@ public class TelephonyManager {
/**
* Set the user-set status for enriched calling with call composer.
*
- * @param status user-set status for enriched calling with call composer;
- * it must be either {@link #CALL_COMPOSER_STATUS_ON} or
- * {@link #CALL_COMPOSER_STATUS_OFF}.
+ * @param status user-set status for enriched calling with call composer.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
@@ -12579,6 +12603,7 @@ public class TelephonyManager {
NETWORK_TYPE_BITMASK_LTE,
NETWORK_TYPE_BITMASK_LTE_CA,
NETWORK_TYPE_BITMASK_NR,
+ NETWORK_TYPE_BITMASK_IWLAN
})
public @interface NetworkTypeBitMask {}
@@ -15218,13 +15243,17 @@ public class TelephonyManager {
* </ul>
* @param appType icc application type, like {@link #APPTYPE_USIM} or {@link
* #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN}
- * @param nafId Network Application Function(NAF) fully qualified domain name and
- * the selected GBA mode. It shall contain two parts delimited by "@" sign. The first
- * part is the constant string "3GPP-bootstrapping" (GBA_ME),
- * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest),
- * and the latter part shall be the FQDN of the NAF (e.g.
- * "3GPP-bootstrapping@naf1.operator.com" or "3GPP-bootstrapping-uicc@naf1.operator.com",
- * or "3GPP-bootstrapping-digest@naf1.operator.com").
+ * @param nafId A URI to specify Network Application Function(NAF) fully qualified domain
+ * name (FQDN) and the selected GBA mode. The authority of the URI must contain two parts
+ * delimited by "@" sign. The first part is the constant string "3GPP-bootstrapping" (GBA_ME),
+ * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest).
+ * The second part shall be the FQDN of the NAF. The scheme of the URI is not actually used
+ * for the authentication, which may be set the same as the resource that the application is
+ * going to access. For example, the nafId can be
+ * "https://3GPP-bootstrapping@naf1.operator.com",
+ * "https://3GPP-bootstrapping-uicc@naf1.operator.com",
+ * "https://3GPP-bootstrapping-digest@naf1.operator.com",
+ * "ftps://3GPP-bootstrapping-digest@naf1.operator.com".
* @param securityProtocol Security protocol identifier between UE and NAF.  See
* 3GPP TS 33.220 Annex H. Application can use
   * {@link UaSecurityProtocolIdentifier#createDefaultUaSpId},
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index a76422977cb6..ffe5399e406b 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -18,6 +18,7 @@
package android.telephony.data;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -29,6 +30,7 @@ import android.telephony.DataFailCause;
import android.telephony.data.ApnSetting.ProtocolType;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -812,11 +814,19 @@ public final class DataCallResponse implements Parcelable {
/**
* Set pdu session id.
+ * <p/>
+ * The id must be between 1 and 15 when linked to a pdu session. If no pdu session
+ * exists for the current data call, the id must be set to {@link PDU_SESSION_ID_NOT_SET}.
*
* @param pduSessionId Pdu Session Id of the data call.
* @return The same instance of the builder.
*/
- public @NonNull Builder setPduSessionId(int pduSessionId) {
+ public @NonNull Builder setPduSessionId(
+ @IntRange(from = PDU_SESSION_ID_NOT_SET, to = 15) int pduSessionId) {
+ Preconditions.checkArgument(pduSessionId >= PDU_SESSION_ID_NOT_SET,
+ "pduSessionId must be greater than or equal to" + PDU_SESSION_ID_NOT_SET);
+ Preconditions.checkArgument(pduSessionId <= 15,
+ "pduSessionId must be less than or equal to 15.");
mPduSessionId = pduSessionId;
return this;
}
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index f5f29c65b7cd..048b3297a1b4 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -41,6 +41,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Base class of data service. Services that extend DataService must register the service in
@@ -284,11 +285,11 @@ public abstract class DataService extends Service {
*
* Any resources being transferred cannot be released while a
* handover is underway.
- *
+ * <p/>
* If a handover was unsuccessful, then the framework calls
* {@link DataService#cancelHandover}. The target transport retains ownership over any of
* the resources being transferred.
- *
+ * <p/>
* If a handover was successful, the framework calls {@link DataService#deactivateDataCall}
* with reason {@link DataService.REQUEST_REASON_HANDOVER}. The target transport now owns
* the transferred resources and is responsible for releasing them.
@@ -299,21 +300,27 @@ public abstract class DataService extends Service {
* @hide
*/
public void startHandover(int cid, @NonNull DataServiceCallback callback) {
+ Objects.requireNonNull(callback, "callback cannot be null");
// The default implementation is to return unsupported.
- if (callback != null) {
- Log.d(TAG, "startHandover: " + cid);
- callback.onHandoverStarted(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
- } else {
- Log.e(TAG, "startHandover: " + cid + ", callback is null");
- }
+ Log.d(TAG, "startHandover: " + cid);
+ callback.onHandoverStarted(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
}
/**
* Indicates that a handover was cancelled after a call to
* {@link DataService#startHandover}. This is called on the source transport.
- *
+ * <p/>
* Since the handover was unsuccessful, the source transport retains ownership over any of
* the resources being transferred and is still responsible for releasing them.
+ * <p/>
+ * The handover can be cancelled up until either:
+ * <ul><li>
+ * The handover was successful after receiving a successful response from
+ * {@link DataService#setupDataCall} on the target transport.
+ * </li><li>
+ * The data call on the source transport was lost.
+ * </li>
+ * </ul>
*
* @param cid The identifier of the data call which is provided in {@link DataCallResponse}
* @param callback The result callback for this request.
@@ -321,13 +328,10 @@ public abstract class DataService extends Service {
* @hide
*/
public void cancelHandover(int cid, @NonNull DataServiceCallback callback) {
+ Objects.requireNonNull(callback, "callback cannot be null");
// The default implementation is to return unsupported.
- if (callback != null) {
- Log.d(TAG, "cancelHandover: " + cid);
- callback.onHandoverCancelled(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
- } else {
- Log.e(TAG, "cancelHandover: " + cid + ", callback is null");
- }
+ Log.d(TAG, "cancelHandover: " + cid);
+ callback.onHandoverCancelled(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
}
/**
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 9bb4db8edf79..486f74632ca2 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -277,6 +277,10 @@ public final class ImsCallProfile implements Parcelable {
* server infrastructure to get the picture. It can be set via
* {@link #setCallExtra(String, String)}.
*
+ * Note that this URL is not intended to be parsed by the IMS stack -- it should be sent
+ * directly to the network for consumption by the called party or forwarded directly from the
+ * network to the platform for caching and download.
+ *
* Reference: RCC.20 Section 2.4.3.2
*/
public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL";
@@ -729,6 +733,10 @@ public final class ImsCallProfile implements Parcelable {
/**
* Set the call extra value (Parcelable), given the call extra name.
+ *
+ * Note that the {@link Parcelable} provided must be a class defined in the Android API surface,
+ * as opposed to a class defined by your app.
+ *
* @param name call extra name
* @param parcelable call extra value
*/
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 85cd81bb4eb5..abc5606e6743 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -1010,6 +1010,16 @@ public class ProvisioningManager {
}
}
+ @Override
+ public void onPreProvisioningReceived(byte[] configXml) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mLocalCallback.onPreProvisioningReceived(configXml));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
private void setExecutor(Executor executor) {
mExecutor = executor;
}
@@ -1022,7 +1032,7 @@ public class ProvisioningManager {
* due to various triggers defined in GSMA RCC.14 for ACS(auto configuration
* server) or other operator defined triggers. If RCS provisioning is already
* completed at the time of callback registration, then this method shall be
- * invoked with the current configuration
+ * invoked with the current configuration.
* @param configXml The RCS configuration XML received by OTA. It is defined
* by GSMA RCC.07.
*/
@@ -1055,6 +1065,20 @@ public class ProvisioningManager {
*/
public void onRemoved() {}
+ /**
+ * Some carriers using ACS (auto configuration server) may send a carrier-specific
+ * pre-provisioning configuration XML if the user has not been provisioned for RCS
+ * services yet. When this provisioning XML is received, the framework will move
+ * into a "not provisioned" state for RCS. In order for provisioning to proceed,
+ * the application must parse this configuration XML and perform the carrier specific
+ * opt-in flow for RCS services. If the user accepts, {@link #triggerRcsReconfiguration}
+ * must be called in order for the device to move out of this state and try to fetch
+ * the RCS provisioning information.
+ *
+ * @param configXml the pre-provisioning config in carrier specified format.
+ */
+ public void onPreProvisioningReceived(@NonNull byte[] configXml) {}
+
/**@hide*/
public final IRcsConfigCallback getBinder() {
return mBinder;
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index cedf48b0b8e1..9c28c36521f5 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -25,7 +25,6 @@ import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -389,22 +388,6 @@ public final class RcsContactPresenceTuple implements Parcelable {
* The optional timestamp indicating the data and time of the status change of this tuple.
* Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
* string per RFC3339.
- * @hide
- */
- public @NonNull Builder setTimestamp(@NonNull String timestamp) {
- try {
- mPresenceTuple.mTimestamp =
- DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp, Instant::from);
- } catch (DateTimeParseException e) {
- Log.d(LOG_TAG, "Parse timestamp failed " + e);
- }
- return this;
- }
-
- /**
- * The optional timestamp indicating the data and time of the status change of this tuple.
- * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
- * string per RFC3339.
*/
public @NonNull Builder setTime(@NonNull Instant timestamp) {
mPresenceTuple.mTimestamp = timestamp;
@@ -534,14 +517,6 @@ public final class RcsContactPresenceTuple implements Parcelable {
return mContactUri;
}
- /**
- * @return the timestamp element contained in the tuple if it exists
- * @hide
- */
- public @Nullable String getTimestamp() {
- return (mTimestamp == null) ? null : mTimestamp.toString();
- }
-
/** @return the timestamp element contained in the tuple if it exists */
public @Nullable Instant getTime() {
return mTimestamp;
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 815c08d120c2..dd9102699529 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -439,8 +439,7 @@ public class RcsUceAdapter {
/**
* The pending request has resulted in an error and may need to be retried, depending on the
- * error code. The callback {@link #onCapabilitiesReceived(List)}
- * for each contacts is required to be called before {@link #onError} is called.
+ * error code.
* @param errorCode The reason for the framework being unable to process the request.
* @param retryIntervalMillis The time in milliseconds the requesting application should
* wait before retrying, if non-zero.
@@ -487,93 +486,6 @@ public class RcsUceAdapter {
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
- @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
- Manifest.permission.READ_CONTACTS})
- public void requestCapabilities(@NonNull List<Uri> contactNumbers,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull CapabilitiesCallback c) throws ImsException {
- if (c == null) {
- throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback.");
- }
- if (executor == null) {
- throw new IllegalArgumentException("Must include a non-null Executor.");
- }
- if (contactNumbers == null) {
- throw new IllegalArgumentException("Must include non-null contact number list.");
- }
-
- IImsRcsController imsRcsController = getIImsRcsController();
- if (imsRcsController == null) {
- Log.e(TAG, "requestCapabilities: IImsRcsController is null");
- throw new ImsException("Can not find remote IMS service",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
- }
-
- IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() {
- @Override
- public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
- } finally {
- restoreCallingIdentity(callingIdentity);
- }
- }
- @Override
- public void onComplete() {
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> c.onComplete());
- } finally {
- restoreCallingIdentity(callingIdentity);
- }
- }
- @Override
- public void onError(int errorCode, long retryAfterMilliseconds) {
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds));
- } finally {
- restoreCallingIdentity(callingIdentity);
- }
- }
- };
-
- try {
- imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(),
- mContext.getAttributionTag(), contactNumbers, internalCallback);
- } catch (ServiceSpecificException e) {
- throw new ImsException(e.toString(), e.errorCode);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e);
- throw new ImsException("Remote IMS Service is not available",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
- }
- }
-
- /**
- * Request the User Capability Exchange capabilities for one or more contacts.
- * <p>
- * This will return the cached capabilities of the contact and will not perform a capability
- * poll on the network unless there are contacts being queried with stale information.
- * <p>
- * Be sure to check the availability of this feature using
- * {@link ImsRcsManager#isAvailable(int, int)} and ensuring
- * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
- * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
- * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
- *
- * @param contactNumbers A list of numbers that the capabilities are being requested for.
- * @param executor The executor that will be used when the request is completed and the
- * {@link CapabilitiesCallback} is called.
- * @param c A one-time callback for when the request for capabilities completes or there is an
- * error processing the request.
- * @throws ImsException if the subscription associated with this instance of
- * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
- * available. This can happen if the ImsService has crashed, for example, or if the subscription
- * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
- * @hide
- */
@SystemApi
@RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
Manifest.permission.READ_CONTACTS})
diff --git a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
index 5a8973e37bce..d0853d1846ac 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
@@ -25,5 +25,6 @@ oneway interface IRcsConfigCallback {
void onAutoConfigurationErrorReceived(int errorCode, String errorString);
void onConfigurationReset();
void onRemoved();
+ void onPreProvisioningReceived(in byte[] config);
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 21aeb64bb417..d75da9035124 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -552,11 +552,34 @@ public class ImsConfigImplBase {
}
mRcsCallbacks.broadcastAction(c -> {
try {
- //TODO compressed by default?
c.onAutoConfigurationErrorReceived(errorCode, errorString);
} catch (RemoteException e) {
Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping.");
}
});
}
+
+ /**
+ * Notifies application that pre-provisioning config is received.
+ *
+ * <p>Some carriers using ACS (auto configuration server) may send a carrier-specific
+ * pre-provisioning configuration XML if the user has not been provisioned for RCS
+ * services yet. When such provisioning XML is received, ACS client must call this
+ * method to notify the application with the XML.
+ *
+ * @param configXml the pre-provisioning config in carrier specified format.
+ */
+ public final void notifyPreProvisioningReceived(@NonNull byte[] configXml) {
+ // can be null in testing
+ if (mRcsCallbacks == null) {
+ return;
+ }
+ mRcsCallbacks.broadcastAction(c -> {
+ try {
+ c.onPreProvisioningReceived(configXml);
+ } catch (RemoteException e) {
+ Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping.");
+ }
+ });
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 00c91681d9ea..03e17fbc2c0d 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -386,41 +386,6 @@ public class RcsCapabilityExchangeImplBase {
* {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
* framework to finish listening for NOTIFY responses.
*
- * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
- * capabilities for.
- * @param cb The callback of the subscribe request.
- * @hide
- */
- // executor used is defined in the constructor.
- @SuppressLint("ExecutorRegistration")
- public void subscribeForCapabilities(@NonNull List<Uri> uris,
- @NonNull SubscribeResponseCallback cb) {
- // Stub - to be implemented by service
- Log.w(LOG_TAG, "subscribeForCapabilities called with no implementation.");
- try {
- cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
- } catch (ImsException e) {
- // Do not do anything, this is a stub implementation.
- }
- }
-
- /**
- * The user capabilities of one or multiple contacts have been requested by the framework.
- * <p>
- * The implementer must follow up this call with an
- * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed.
- * The response from the network to the SUBSCRIBE request must be sent back to the framework
- * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}.
- * As NOTIFY requests come in from the network, the requested contact’s capabilities should be
- * sent back to the framework using
- * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and
- * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)}
- * should be called with the presence information for the contacts specified.
- * <p>
- * Once the subscription is terminated,
- * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
- * framework to finish listening for NOTIFY responses.
- *
* @param uris A {@link Collection} of the {@link Uri}s that the framework is requesting the
* UCE capabilities for.
* @param cb The callback of the subscribe request.
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 571efcee0e15..9493c76d9a57 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -300,4 +300,6 @@ interface ISub {
boolean canDisablePhysicalSubscription();
int setUiccApplicationsEnabled(boolean enabled, int subscriptionId);
+
+ int setDeviceToDeviceStatusSharing(int sharing, int subId);
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index fbf18d45afd8..c92d40cdd555 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -16,37 +16,12 @@
package com.android.server.wm.flicker.close
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
-import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
-import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import org.junit.Assume
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -59,110 +34,15 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppBackButtonTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = SimpleAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- this.setRotation(testSpec.config.startRotation)
- testApp.launchViaIntent(wmHelper)
- }
- }
+class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
transitions {
device.pressBack()
wmHelper.waitForHomeActivityVisible()
}
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- testApp.exit()
- }
- }
}
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun launcherReplacesAppWindowAsTopWindow() =
- testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
-
- @Presubmit
- @Test
- fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible()
-
- @Presubmit
- @Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
- Surface.ROTATION_0)
-
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest(bugId = 173684672)
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @FlakyTest(bugId = 173684672)
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 08d2b7c206bf..1f880f61d65e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,37 +16,12 @@
package com.android.server.wm.flicker.close
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
-import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
-import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import org.junit.Assume
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -59,110 +34,15 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppHomeButtonTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = SimpleAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- this.setRotation(testSpec.config.startRotation)
- }
- }
+class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
transitions {
device.pressHome()
wmHelper.waitForHomeActivityVisible()
}
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- testApp.exit()
- }
- }
}
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun launcherReplacesAppWindowAsTopWindow() =
- testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
-
- @Presubmit
- @Test
- fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible()
-
- @Presubmit
- @Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(
- testSpec.config.startRotation, Surface.ROTATION_0)
-
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest(bugId = 173689015)
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @FlakyTest(bugId = 173689015)
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
new file mode 100644
index 000000000000..fef49d9433a8
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -0,0 +1,157 @@
+/*
+ * 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.server.wm.flicker.close
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
+import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
+import org.junit.Assume
+import org.junit.Test
+
+abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) {
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+ protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+ setup {
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ teardown {
+ test {
+ testApp.exit()
+ }
+ }
+ }
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ transition(testSpec.config)
+ }
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarWindowIsAlwaysVisible() {
+ testSpec.navBarWindowIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarWindowIsAlwaysVisible() {
+ testSpec.statusBarWindowIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarLayerIsAlwaysVisible() {
+ testSpec.navBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerIsAlwaysVisible() {
+ testSpec.statusBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ open fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest
+ @Test
+ open fun statusBarLayerRotatesScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @FlakyTest(bugId = 173689015)
+ @Test
+ open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+
+ @FlakyTest(bugId = 173689015)
+ @Test
+ open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ @Presubmit
+ @Test
+ open fun noUncoveredRegions() {
+ testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+ }
+
+ @Presubmit
+ @Test
+ open fun launcherReplacesAppWindowAsTopWindow() {
+ testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
+ }
+
+ @Presubmit
+ @Test
+ open fun wallpaperWindowBecomesVisible() {
+ testSpec.wallpaperWindowBecomesVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun wallpaperLayerReplacesAppLayer() {
+ testSpec.wallpaperLayerReplacesAppLayer(testApp)
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 74f002d67229..56ed21b70b82 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,37 +16,14 @@
package com.android.server.wm.flicker.launch
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import org.junit.Assume
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -59,114 +36,25 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = SimpleAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
+class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
eachRun {
this.setRotation(testSpec.config.startRotation)
}
}
- transitions {
- testApp.launchViaIntent(wmHelper)
- // wmHelper.waitForFullScreenApp(testApp.component)
- }
teardown {
eachRun {
- testApp.exit()
- wmHelper.waitForAppTransitionIdle()
- this.setRotation(Surface.ROTATION_0)
+ testApp.exit(wmHelper)
}
}
+ transitions {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp.component)
+ }
}
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- fun appWindowReplacesLauncherAsTopWindow() =
- testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
-
- @Presubmit
- @Test
- fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
-
- @Presubmit
- @Test
- // During testing the launcher is always in portrait mode
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
- Surface.ROTATION_0)
-
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun appLayerReplacesWallpaperLayer() =
- testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
-
- @FlakyTest
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 18fac6a82de7..4a32a9eb3851 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,36 +16,19 @@
package com.android.server.wm.flicker.launch
-import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
-import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -61,18 +44,12 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = SimpleAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
+class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
setup {
test {
- device.wakeUpAndGoToHomeScreen()
testApp.launchViaIntent(wmHelper)
}
eachRun {
@@ -87,71 +64,50 @@ class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) {
device.reopenAppFromOverview(wmHelper)
wmHelper.waitForFullScreenApp(testApp.component)
}
- teardown {
- test {
- testApp.exit()
- }
- }
}
- }
- @Presubmit
- @Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun appWindowReplacesLauncherAsTopWindow() =
+ super.appWindowReplacesLauncherAsTopWindow()
@Test
- fun appWindowReplacesLauncherAsTopWindow() =
- testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
-
- @Test
- fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
-
- @Presubmit
- @Test
- fun appLayerReplacesWallpaperLayer() =
- testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ override fun wallpaperWindowBecomesInvisible() {
+ testSpec.wallpaperWindowBecomesInvisible()
}
@Presubmit
@Test
- fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ override fun statusBarLayerIsAlwaysVisible() {
+ Assume.assumeTrue(testSpec.isRotated)
+ super.statusBarLayerIsAlwaysVisible()
}
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() {
+ override fun navBarLayerIsAlwaysVisible() {
Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerIsAlwaysVisible()
+ super.navBarLayerIsAlwaysVisible()
}
- @Presubmit
+ @FlakyTest
@Test
- fun navBarLayerIsAlwaysVisible() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.navBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsAlwaysVisible_Flaky() {
+ Assume.assumeFalse(testSpec.isRotated)
+ super.statusBarLayerIsAlwaysVisible()
}
- @Presubmit
+ @FlakyTest
@Test
- fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+ fun navBarLayerIsAlwaysVisible_Flaky() {
+ Assume.assumeFalse(testSpec.isRotated)
+ super.navBarLayerIsAlwaysVisible()
+ }
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
Assume.assumeFalse(testSpec.isRotated)
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
}
@FlakyTest
@@ -163,9 +119,9 @@ class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
Assume.assumeFalse(testSpec.isRotated)
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
}
@FlakyTest
@@ -175,11 +131,6 @@ class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) {
testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
}
- @Presubmit
- @Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(Surface.ROTATION_0,
- testSpec.config.endRotation)
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
new file mode 100644
index 000000000000..e9f053452589
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.server.wm.flicker.launch
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
+import org.junit.Assume
+import org.junit.Test
+
+abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+
+ protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ teardown {
+ test {
+ testApp.exit()
+ }
+ }
+ }
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ transition(testSpec.config)
+ }
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarWindowIsAlwaysVisible() {
+ testSpec.navBarWindowIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarLayerIsAlwaysVisible() {
+ testSpec.navBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun navBarLayerRotatesAndScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @FlakyTest
+ @Test
+ open fun navBarLayerRotatesAndScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarWindowIsAlwaysVisible() {
+ testSpec.statusBarWindowIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerIsAlwaysVisible() {
+ testSpec.statusBarLayerIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ open fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @FlakyTest
+ @Test
+ open fun statusBarLayerRotatesScales_Flaky() {
+ Assume.assumeTrue(testSpec.isRotated)
+ testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @Presubmit
+ @Test
+ open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+
+ @FlakyTest
+ @Test
+ open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ @Presubmit
+ @Test
+ // During testing the launcher is always in portrait mode
+ open fun noUncoveredRegions() {
+ testSpec.noUncoveredRegions(Surface.ROTATION_0, testSpec.config.endRotation)
+ }
+
+ @Presubmit
+ @Test
+ open fun focusChanges() {
+ testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+ }
+
+ @Presubmit
+ @Test
+ open fun appLayerReplacesWallpaperLayer() {
+ testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
+ }
+
+ @Presubmit
+ @Test
+ open fun appWindowReplacesLauncherAsTopWindow() {
+ testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
+ }
+
+ @Presubmit
+ @Test
+ open fun wallpaperWindowBecomesInvisible() {
+ testSpec.wallpaperWindowBecomesInvisible()
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index b61310aa4bd8..a8b5ea1604ec 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,34 +16,14 @@
package com.android.server.wm.flicker.launch
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -58,20 +38,13 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppWarmTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = SimpleAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
+class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
setup {
test {
- device.wakeUpAndGoToHomeScreen()
testApp.launchViaIntent(wmHelper)
- // wmHelper.waitForFullScreenApp(testApp.component)
}
eachRun {
device.pressHome()
@@ -79,93 +52,21 @@ class OpenAppWarmTest(private val testSpec: FlickerTestParameter) {
this.setRotation(testSpec.config.startRotation)
}
}
- transitions {
- testApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp.component)
- }
teardown {
eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- testApp.exit()
+ testApp.exit(wmHelper)
}
}
+ transitions {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp.component)
+ }
}
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
- @FlakyTest
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- fun appWindowReplacesLauncherAsTopWindow() =
- testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
-
- @Presubmit
- @Test
- fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
-
- @Presubmit
- @Test
- // During testing the launcher is always in portrait mode
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
- Surface.ROTATION_0)
-
- @Presubmit
- @Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
- @Presubmit
- @Test
- fun appLayerReplacesWallpaperLayer() =
- testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
@FlakyTest
@Test
- fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun statusBarLayerRotatesScales() {
- Assume.assumeFalse(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @FlakyTest
- @Test
- fun statusBarLayerRotatesScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
-
- @Presubmit
- @Test
- fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 335c8d0127eb..eacf5b287a2e 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -9,14 +9,17 @@ package {
android_test {
name: "InputTests",
- srcs: ["src/**/*.kt"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
platform_apis: true,
certificate: "platform",
static_libs: [
- "androidx.test.ext.junit",
- "androidx.test.rules",
- "truth-prebuilt",
- "ub-uiautomator",
- ],
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "truth-prebuilt",
+ "ub-uiautomator",
+ ],
test_suites: ["device-tests"],
}
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
new file mode 100644
index 000000000000..63500774816a
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.view;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputDeviceTest {
+ private static final float DELTA = 0.01f;
+ private static final int DEVICE_ID = 1000;
+
+ private void assertMotionRangeEquals(InputDevice.MotionRange range,
+ InputDevice.MotionRange outRange) {
+ assertEquals(range.getAxis(), outRange.getAxis());
+ assertEquals(range.getSource(), outRange.getSource());
+ assertEquals(range.getMin(), outRange.getMin(), DELTA);
+ assertEquals(range.getMax(), outRange.getMax(), DELTA);
+ assertEquals(range.getFlat(), outRange.getFlat(), DELTA);
+ assertEquals(range.getFuzz(), outRange.getFuzz(), DELTA);
+ assertEquals(range.getResolution(), outRange.getResolution(), DELTA);
+ }
+
+ private void assertDeviceEquals(InputDevice device, InputDevice outDevice) {
+ assertEquals(device.getId(), outDevice.getId());
+ assertEquals(device.getGeneration(), outDevice.getGeneration());
+ assertEquals(device.getControllerNumber(), outDevice.getControllerNumber());
+ assertEquals(device.getName(), outDevice.getName());
+ assertEquals(device.getVendorId(), outDevice.getVendorId());
+ assertEquals(device.getProductId(), outDevice.getProductId());
+ assertEquals(device.getDescriptor(), outDevice.getDescriptor());
+ assertEquals(device.isExternal(), outDevice.isExternal());
+ assertEquals(device.getSources(), outDevice.getSources());
+ assertEquals(device.getKeyboardType(), outDevice.getKeyboardType());
+ assertEquals(device.getMotionRanges().size(), outDevice.getMotionRanges().size());
+
+ KeyCharacterMap keyCharacterMap = device.getKeyCharacterMap();
+ KeyCharacterMap outKeyCharacterMap = outDevice.getKeyCharacterMap();
+ assertTrue("keyCharacterMap not equal", keyCharacterMap.equals(outKeyCharacterMap));
+
+ for (int j = 0; j < device.getMotionRanges().size(); j++) {
+ assertMotionRangeEquals(device.getMotionRanges().get(j),
+ outDevice.getMotionRanges().get(j));
+ }
+ }
+
+ private void assertInputDeviceParcelUnparcel(KeyCharacterMap keyCharacterMap) {
+ final InputDevice device =
+ new InputDevice(DEVICE_ID, 0 /* generation */, 0 /* controllerNumber */, "name",
+ 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
+ 0 /* sources */, 0 /* keyboardType */, keyCharacterMap,
+ false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */,
+ true /* hasSensor */, false /* hasBattery */);
+
+ Parcel parcel = Parcel.obtain();
+ device.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ InputDevice outDevice = InputDevice.CREATOR.createFromParcel(parcel);
+ assertDeviceEquals(device, outDevice);
+ }
+
+ @Test
+ public void testParcelUnparcelInputDevice_VirtualCharacterMap() {
+ final KeyCharacterMap keyCharacterMap =
+ KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ assertInputDeviceParcelUnparcel(keyCharacterMap);
+ }
+
+ @Test
+ public void testParcelUnparcelInputDevice_EmptyCharacterMap() {
+ final KeyCharacterMap keyCharacterMap = KeyCharacterMap.obtainEmptyMap(DEVICE_ID);
+ assertInputDeviceParcelUnparcel(keyCharacterMap);
+ }
+}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 11498dec8165..a02002752c38 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -593,6 +593,16 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
}
+ @Test(expected = SecurityException.class)
+ public void testRemoveVcnUnderlyingNetworkPolicyListenerInvalidPermission() {
+ doThrow(new SecurityException())
+ .when(mMockContext)
+ .enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+ mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+ }
+
@Test
public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() {
mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);