summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-06-24 01:07:40 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-06-24 01:07:40 +0000
commit9dd561f2a032908f6c916be577f6a7136c9b03ef (patch)
tree2c836d01f4b1076f06c7eef659dc9d03f68d935c
parent7f5466cf1d7dde34bba6e2da204eba26f31c7c4e (diff)
parentaa910853fb9d0dbd07a3eebd6b221cf0259e87de (diff)
Snap for 7486544 from aa910853fb9d0dbd07a3eebd6b221cf0259e87de to sc-release
Change-Id: I265705dc813a91e8ba0f3617517246d2d6f2c64a
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java35
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java11
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java2
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java8
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java5
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java2
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java72
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java (renamed from apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java)46
-rw-r--r--apex/appsearch/synced_jetpack_changeid.txt2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java207
-rw-r--r--core/java/android/app/AppOpsManager.java50
-rw-r--r--core/java/android/app/LoadedApk.java4
-rw-r--r--core/java/android/app/WallpaperColors.java2
-rw-r--r--core/java/android/hardware/camera2/CameraExtensionCharacteristics.java4
-rw-r--r--core/java/android/net/VpnManager.java4
-rw-r--r--core/java/android/permission/PermissionManager.java35
-rw-r--r--core/java/android/permission/PermissionUsageHelper.java4
-rw-r--r--core/java/android/service/voice/HotwordDetectedResult.java71
-rw-r--r--core/java/android/service/voice/HotwordDetectionService.java9
-rw-r--r--core/java/android/view/CrossWindowBlurListeners.java6
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureEvent.java9
-rw-r--r--core/java/android/view/contentcapture/MainContentCaptureSession.java5
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java13
-rw-r--r--core/java/android/widget/TextView.java12
-rw-r--r--core/java/com/android/internal/os/BatterySipper.java3
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHelper.java3
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java3
-rw-r--r--core/java/com/android/internal/util/function/DodecConsumer.java29
-rw-r--r--core/java/com/android/internal/util/function/DodecFunction.java28
-rw-r--r--core/java/com/android/internal/util/function/DodecPredicate.java28
-rwxr-xr-xcore/java/com/android/internal/util/function/pooled/PooledLambda.java229
-rwxr-xr-xcore/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java30
-rw-r--r--core/java/com/android/internal/widget/CachingIconView.java6
-rw-r--r--core/java/com/android/internal/widget/EditableInputConnection.java6
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java5
-rw-r--r--core/java/com/android/server/AppWidgetBackupBridge.java8
-rw-r--r--core/java/com/android/server/SystemConfig.java23
-rw-r--r--core/java/com/android/server/WidgetBackupProvider.java4
-rw-r--r--core/proto/android/server/apphibernationservice.proto1
-rw-r--r--core/res/res/values-television/config.xml4
-rw-r--r--core/res/res/values/config.xml8
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java44
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java2
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml4
-rw-r--r--libs/hwui/RenderNode.h2
-rw-r--r--libs/hwui/effects/StretchEffect.cpp7
-rw-r--r--libs/hwui/effects/StretchEffect.h4
-rw-r--r--location/java/android/location/ILocationManager.aidl3
-rw-r--r--location/java/android/location/LastLocationRequest.java60
-rw-r--r--location/java/android/location/LocationManager.java63
-rw-r--r--location/java/android/location/LocationRequest.java72
-rw-r--r--location/java/android/location/provider/ProviderRequest.java53
-rwxr-xr-xmedia/java/android/mtp/MtpDatabase.java37
-rw-r--r--media/java/android/mtp/MtpDevice.java13
-rw-r--r--media/java/android/mtp/MtpDeviceInfo.java27
-rw-r--r--media/jni/android_mtp_MtpDatabase.cpp2
-rw-r--r--media/jni/android_mtp_MtpDevice.cpp61
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml1
-rw-r--r--packages/SystemUI/AndroidManifest.xml8
-rw-r--r--packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml52
-rw-r--r--packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml23
-rw-r--r--packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml23
-rw-r--r--packages/SystemUI/res/color/bottom_sheet_button_background_color.xml21
-rw-r--r--packages/SystemUI/res/color/bottom_sheet_button_text_color.xml21
-rw-r--r--packages/SystemUI/res/drawable/bottom_sheet_background.xml20
-rw-r--r--packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml20
-rw-r--r--packages/SystemUI/res/drawable/bottom_sheet_button_background.xml20
-rw-r--r--packages/SystemUI/res/layout/tv_bottom_sheet.xml87
-rw-r--r--packages/SystemUI/res/values-television/colors.xml15
-rw-r--r--packages/SystemUI/res/values-television/dimens.xml23
-rw-r--r--packages/SystemUI/res/values-television/styles.xml30
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetail.java10
-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/QuickStatusBarHeaderController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java145
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java170
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java98
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java86
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java7
-rw-r--r--packages/services/CameraExtensionsProxy/AndroidManifest.xml2
-rw-r--r--packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java (renamed from packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java)2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java22
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java145
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java8
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java4
-rw-r--r--services/core/java/com/android/server/VpnManagerService.java9
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java7
-rw-r--r--services/core/java/com/android/server/apphibernation/AppHibernationService.java21
-rw-r--r--services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java5
-rw-r--r--services/core/java/com/android/server/apphibernation/GlobalLevelState.java3
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java39
-rw-r--r--services/core/java/com/android/server/appop/DiscreteRegistry.java381
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java26
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java187
-rw-r--r--services/core/java/com/android/server/location/LocationShellCommand.java70
-rw-r--r--services/core/java/com/android/server/location/eventlog/LocationEventLog.java52
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java4
-rw-r--r--services/core/java/com/android/server/location/injector/Injector.java4
-rw-r--r--services/core/java/com/android/server/location/injector/SystemSettingsHelper.java2
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java91
-rw-r--r--services/core/java/com/android/server/location/settings/LocationSettings.java173
-rw-r--r--services/core/java/com/android/server/location/settings/LocationUserSettings.java98
-rw-r--r--services/core/java/com/android/server/location/settings/SettingsStore.java166
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java63
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java56
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java11
-rw-r--r--services/core/java/com/android/server/notification/RankingConfig.java2
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java3
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java26
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java35
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java7
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java13
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java110
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java54
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java171
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java (renamed from services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java)15
-rw-r--r--services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java38
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java93
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java19
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java11
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java15
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl2
170 files changed, 4440 insertions, 826 deletions
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index bcd341ec18a8..e688cb3c9be9 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -50,9 +50,6 @@ import java.util.Set;
public class GenericDocument {
private static final String TAG = "AppSearchGenericDocumen";
- /** The maximum number of elements in a repeatable field. */
- private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
-
/** The maximum {@link String#length} of a {@link String} field. */
private static final int MAX_STRING_LENGTH = 20_000;
@@ -1187,8 +1184,8 @@ public class GenericDocument {
* @param name the name associated with the {@code values}. Must match the name for this
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code String} values of the property.
- * @throws IllegalArgumentException if no values are provided, if provided values exceed
- * maximum repeated property length, or if a passed in {@code String} is {@code null}.
+ * @throws IllegalArgumentException if no values are provided, or if a passed in {@code
+ * String} is {@code null}.
*/
@NonNull
public BuilderType setPropertyString(@NonNull String name, @NonNull String... values) {
@@ -1206,7 +1203,6 @@ public class GenericDocument {
* @param name the name associated with the {@code values}. Must match the name for this
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code boolean} values of the property.
- * @throws IllegalArgumentException if values exceed maximum repeated property length.
*/
@NonNull
public BuilderType setPropertyBoolean(@NonNull String name, @NonNull boolean... values) {
@@ -1223,7 +1219,6 @@ public class GenericDocument {
* @param name the name associated with the {@code values}. Must match the name for this
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code long} values of the property.
- * @throws IllegalArgumentException if values exceed maximum repeated property length.
*/
@NonNull
public BuilderType setPropertyLong(@NonNull String name, @NonNull long... values) {
@@ -1240,7 +1235,6 @@ public class GenericDocument {
* @param name the name associated with the {@code values}. Must match the name for this
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code double} values of the property.
- * @throws IllegalArgumentException if values exceed maximum repeated property length.
*/
@NonNull
public BuilderType setPropertyDouble(@NonNull String name, @NonNull double... values) {
@@ -1257,8 +1251,8 @@ public class GenericDocument {
* @param name the name associated with the {@code values}. Must match the name for this
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code byte[]} of the property.
- * @throws IllegalArgumentException if no values are provided, if provided values exceed
- * maximum repeated property length, or if a passed in {@code byte[]} is {@code null}.
+ * @throws IllegalArgumentException if no values are provided, or if a passed in {@code
+ * byte[]} is {@code null}.
*/
@NonNull
public BuilderType setPropertyBytes(@NonNull String name, @NonNull byte[]... values) {
@@ -1276,8 +1270,7 @@ public class GenericDocument {
* @param name the name associated with the {@code values}. Must match the name for this
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@link GenericDocument} values of the property.
- * @throws IllegalArgumentException if no values are provided, if provided values exceed if
- * provided values exceed maximum repeated property length, or if a passed in {@link
+ * @throws IllegalArgumentException if no values are provided, or if a passed in {@link
* GenericDocument} is {@code null}.
*/
@NonNull
@@ -1308,7 +1301,6 @@ public class GenericDocument {
private void putInPropertyBundle(@NonNull String name, @NonNull String[] values)
throws IllegalArgumentException {
- validateRepeatedPropertyLength(name, values.length);
for (int i = 0; i < values.length; i++) {
if (values[i] == null) {
throw new IllegalArgumentException("The String at " + i + " is null.");
@@ -1327,17 +1319,14 @@ public class GenericDocument {
}
private void putInPropertyBundle(@NonNull String name, @NonNull boolean[] values) {
- validateRepeatedPropertyLength(name, values.length);
mProperties.putBooleanArray(name, values);
}
private void putInPropertyBundle(@NonNull String name, @NonNull double[] values) {
- validateRepeatedPropertyLength(name, values.length);
mProperties.putDoubleArray(name, values);
}
private void putInPropertyBundle(@NonNull String name, @NonNull long[] values) {
- validateRepeatedPropertyLength(name, values.length);
mProperties.putLongArray(name, values);
}
@@ -1348,7 +1337,6 @@ public class GenericDocument {
* into ArrayList<Bundle>, and each elements will contain a one dimension byte[].
*/
private void putInPropertyBundle(@NonNull String name, @NonNull byte[][] values) {
- validateRepeatedPropertyLength(name, values.length);
ArrayList<Bundle> bundles = new ArrayList<>(values.length);
for (int i = 0; i < values.length; i++) {
if (values[i] == null) {
@@ -1362,7 +1350,6 @@ public class GenericDocument {
}
private void putInPropertyBundle(@NonNull String name, @NonNull GenericDocument[] values) {
- validateRepeatedPropertyLength(name, values.length);
Parcelable[] documentBundles = new Parcelable[values.length];
for (int i = 0; i < values.length; i++) {
if (values[i] == null) {
@@ -1373,18 +1360,6 @@ public class GenericDocument {
mProperties.putParcelableArray(name, documentBundles);
}
- private static void validateRepeatedPropertyLength(@NonNull String name, int length) {
- if (length > MAX_REPEATED_PROPERTY_LENGTH) {
- throw new IllegalArgumentException(
- "Repeated property \""
- + name
- + "\" has length "
- + length
- + ", which exceeds the limit of "
- + MAX_REPEATED_PROPERTY_LENGTH);
- }
- }
-
/** Builds the {@link GenericDocument} object. */
@NonNull
public GenericDocument build() {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index 0af3e7a8987a..b72ca9af42cb 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -169,13 +169,14 @@ public final class SetSchemaRequest {
/** Builder for {@link SetSchemaRequest} objects. */
public static final class Builder {
+ private static final int DEFAULT_VERSION = 1;
private ArraySet<AppSearchSchema> mSchemas = new ArraySet<>();
private ArraySet<String> mSchemasNotDisplayedBySystem = new ArraySet<>();
private ArrayMap<String, Set<PackageIdentifier>> mSchemasVisibleToPackages =
new ArrayMap<>();
private ArrayMap<String, Migrator> mMigrators = new ArrayMap<>();
private boolean mForceOverride = false;
- private int mVersion = 1;
+ private int mVersion = DEFAULT_VERSION;
private boolean mBuilt = false;
/**
@@ -384,6 +385,9 @@ public final class SetSchemaRequest {
* <p>The version number can stay the same, increase, or decrease relative to the current
* version number that is already stored in the {@link AppSearchSession} database.
*
+ * <p>The version of an empty database will always be 0. You cannot set version to the
+ * {@link SetSchemaRequest}, if it doesn't contains any {@link AppSearchSchema}.
+ *
* @param version A positive integer representing the version of the entire set of schemas
* represents the version of the whole schema in the {@link AppSearchSession} database,
* default version is 1.
@@ -423,7 +427,10 @@ public final class SetSchemaRequest {
throw new IllegalArgumentException(
"Schema types " + referencedSchemas + " referenced, but were not added.");
}
-
+ if (mSchemas.isEmpty() && mVersion != DEFAULT_VERSION) {
+ throw new IllegalArgumentException(
+ "Cannot set version to the request if schema is empty.");
+ }
mBuilt = true;
return new SetSchemaRequest(
mSchemas,
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index ac584fe4b91b..c1a91badb668 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -60,8 +60,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemService;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
import com.android.server.appsearch.util.PackageUtil;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
import com.android.server.usage.StorageStatsManagerLocal;
import com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter;
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java
index 7e743edaf3ee..56e2af5fa2e3 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java
@@ -19,7 +19,7 @@ import android.annotation.NonNull;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
import com.android.server.appsearch.stats.PlatformLogger;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl;
import java.util.Objects;
@@ -30,12 +30,12 @@ import java.util.Objects;
public final class AppSearchUserInstance {
private final PlatformLogger mLogger;
private final AppSearchImpl mAppSearchImpl;
- private final VisibilityStore mVisibilityStore;
+ private final VisibilityStoreImpl mVisibilityStore;
AppSearchUserInstance(
@NonNull PlatformLogger logger,
@NonNull AppSearchImpl appSearchImpl,
- @NonNull VisibilityStore visibilityStore) {
+ @NonNull VisibilityStoreImpl visibilityStore) {
mLogger = Objects.requireNonNull(logger);
mAppSearchImpl = Objects.requireNonNull(appSearchImpl);
mVisibilityStore = Objects.requireNonNull(visibilityStore);
@@ -52,7 +52,7 @@ public final class AppSearchUserInstance {
}
@NonNull
- public VisibilityStore getVisibilityStore() {
+ public VisibilityStoreImpl getVisibilityStore() {
return mVisibilityStore;
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
index cedc364f6072..1d935cfc13d3 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
@@ -30,7 +30,7 @@ import com.android.server.appsearch.external.localstorage.AppSearchImpl;
import com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.stats.PlatformLogger;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl;
import java.io.File;
import java.util.Map;
@@ -178,7 +178,8 @@ public final class AppSearchUserInstanceManager {
AppSearchImpl.create(icingDir, initStatsBuilder, new FrameworkOptimizeStrategy());
long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime();
- VisibilityStore visibilityStore = VisibilityStore.create(appSearchImpl, userContext);
+ VisibilityStoreImpl visibilityStore =
+ VisibilityStoreImpl.create(appSearchImpl, userContext);
long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime();
initStatsBuilder
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 4ad1c8c56979..9dee179bd6f2 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -58,7 +58,7 @@ import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
import com.google.android.icing.IcingSearchEngine;
import com.google.android.icing.proto.DeleteByQueryResultProto;
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java
new file mode 100644
index 000000000000..fb89250cb096
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 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.appsearch.external.localstorage.visibilitystore;
+
+import android.annotation.NonNull;
+import android.app.appsearch.PackageIdentifier;
+import android.app.appsearch.exceptions.AppSearchException;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An interface for classes that store and validate document visibility data.
+ *
+ * @hide
+ */
+public interface VisibilityStore {
+ /**
+ * These cannot have any of the special characters used by AppSearchImpl (e.g. {@code
+ * AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}.
+ */
+ String PACKAGE_NAME = "VS#Pkg";
+
+ @VisibleForTesting String DATABASE_NAME = "VS#Db";
+
+ /**
+ * Sets visibility settings for the given database. Any previous visibility settings will be
+ * overwritten.
+ *
+ * @param packageName Package of app that owns the schemas.
+ * @param databaseName Database that owns the schemas.
+ * @param schemasNotDisplayedBySystem Set of prefixed schemas that should be hidden from
+ * platform surfaces.
+ * @param schemasVisibleToPackages Map of prefixed schemas to a list of package identifiers that
+ * have access to the schema.
+ * @throws AppSearchException on AppSearchImpl error.
+ */
+ void setVisibility(
+ @NonNull String packageName,
+ @NonNull String databaseName,
+ @NonNull Set<String> schemasNotDisplayedBySystem,
+ @NonNull Map<String, List<PackageIdentifier>> schemasVisibleToPackages)
+ throws AppSearchException;
+
+ /**
+ * Checks whether the given package has access to system-surfaceable schemas.
+ *
+ * @param callerUid UID of the app that wants to see the data.
+ */
+ boolean isSchemaSearchableByCaller(
+ @NonNull String packageName,
+ @NonNull String databaseName,
+ @NonNull String prefixedSchema,
+ int callerUid,
+ boolean callerHasSystemAccess);
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
index a096aef34c71..ce142d646d1c 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
@@ -30,9 +30,9 @@ import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
+import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
import com.android.server.appsearch.util.PackageUtil;
import com.google.android.icing.proto.PersistType;
@@ -60,23 +60,12 @@ import java.util.Set;
* <p>This class doesn't handle any locking itself. Its callers should handle the locking at a
* higher level.
*
- * <p>NOTE: This class holds an instance of AppSearchImpl and AppSearchImpl holds an instance of
- * this class. Take care to not cause any circular dependencies.
- *
* @hide
*/
-public class VisibilityStore {
+public class VisibilityStoreImpl implements VisibilityStore {
/** Version for the visibility schema */
private static final int SCHEMA_VERSION = 0;
- /**
- * These cannot have any of the special characters used by AppSearchImpl (e.g. {@code
- * AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}.
- */
- public static final String PACKAGE_NAME = "VS#Pkg";
-
- @VisibleForTesting public static final String DATABASE_NAME = "VS#Db";
-
/** Namespace of documents that contain visibility settings */
private static final String NAMESPACE = "";
@@ -101,13 +90,13 @@ public class VisibilityStore {
* @param userContext Context of the user that the call is being made as
*/
@NonNull
- public static VisibilityStore create(
+ public static VisibilityStoreImpl create(
@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext)
throws AppSearchException {
- return new VisibilityStore(appSearchImpl, userContext);
+ return new VisibilityStoreImpl(appSearchImpl, userContext);
}
- private VisibilityStore(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext)
+ private VisibilityStoreImpl(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext)
throws AppSearchException {
mAppSearchImpl = Objects.requireNonNull(appSearchImpl);
mUserContext = Objects.requireNonNull(userContext);
@@ -207,18 +196,7 @@ public class VisibilityStore {
}
}
- /**
- * Sets visibility settings for the given database. Any previous visibility settings will be
- * overwritten.
- *
- * @param packageName Package of app that owns the schemas.
- * @param databaseName Database that owns the schemas.
- * @param schemasNotDisplayedBySystem Set of prefixed schemas that should be hidden from the
- * platform.
- * @param schemasVisibleToPackages Map of prefixed schemas to a list of package identifiers that
- * have access to the schema.
- * @throws AppSearchException on AppSearchImpl error.
- */
+ @Override
public void setVisibility(
@NonNull String packageName,
@NonNull String databaseName,
@@ -282,17 +260,7 @@ public class VisibilityStore {
== PackageManager.PERMISSION_GRANTED;
}
- /**
- * Checks whether {@code prefixedSchema} can be searched over by the {@code callerUid}.
- *
- * @param packageName Package that owns the schema.
- * @param databaseName Database within the package that owns the schema.
- * @param prefixedSchema Prefixed schema type the caller is trying to access.
- * @param callerUid UID of the client making the globalQuery call.
- * @param callerHasSystemAccess Whether the caller has been identified as having
- * access to schemas marked system surfaceable by {@link
- * #doesCallerHaveSystemAccess}.
- */
+ @Override
public boolean isSchemaSearchableByCaller(
@NonNull String packageName,
@NonNull String databaseName,
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 9859f20643b5..970d3f1796a0 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-04351b43fbbf9d59ffeae41903322023931c84f2
+a11281766f66aa4919e9bbe70da95919ce054c35
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 0e9efbcc98b8..ac2018707c75 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -442,6 +442,7 @@ public class DeviceIdleController extends SystemService
private long mNextIdlePendingDelay;
private long mNextIdleDelay;
private long mNextLightIdleDelay;
+ private long mNextLightIdleDelayFlex;
private long mNextLightAlarmTime;
private long mNextSensingTimeoutAlarmTime;
@@ -886,16 +887,20 @@ public class DeviceIdleController extends SystemService
*/
public final class Constants implements DeviceConfig.OnPropertiesChangedListener {
// Key names stored in the settings value.
- private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
- = "light_after_inactive_to";
+ private static final String KEY_FLEX_TIME_SHORT = "flex_time_short";
+ private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
+ "light_after_inactive_to";
private static final String KEY_LIGHT_PRE_IDLE_TIMEOUT = "light_pre_idle_to";
private static final String KEY_LIGHT_IDLE_TIMEOUT = "light_idle_to";
+ private static final String KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX =
+ "light_idle_to_initial_flex";
+ private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX = "light_max_idle_to_flex";
private static final String KEY_LIGHT_IDLE_FACTOR = "light_idle_factor";
private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT = "light_max_idle_to";
- private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET
- = "light_idle_maintenance_min_budget";
- private static final String KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET
- = "light_idle_maintenance_max_budget";
+ private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET =
+ "light_idle_maintenance_min_budget";
+ private static final String KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET =
+ "light_idle_maintenance_max_budget";
private static final String KEY_MIN_LIGHT_MAINTENANCE_TIME = "min_light_maintenance_time";
private static final String KEY_MIN_DEEP_MAINTENANCE_TIME = "min_deep_maintenance_time";
private static final String KEY_INACTIVE_TIMEOUT = "inactive_to";
@@ -903,6 +908,7 @@ public class DeviceIdleController extends SystemService
private static final String KEY_LOCATING_TIMEOUT = "locating_to";
private static final String KEY_LOCATION_ACCURACY = "location_accuracy";
private static final String KEY_MOTION_INACTIVE_TIMEOUT = "motion_inactive_to";
+ private static final String KEY_MOTION_INACTIVE_TIMEOUT_FLEX = "motion_inactive_to_flex";
private static final String KEY_IDLE_AFTER_INACTIVE_TIMEOUT = "idle_after_inactive_to";
private static final String KEY_IDLE_PENDING_TIMEOUT = "idle_pending_to";
private static final String KEY_MAX_IDLE_PENDING_TIMEOUT = "max_idle_pending_to";
@@ -929,13 +935,20 @@ public class DeviceIdleController extends SystemService
"pre_idle_factor_long";
private static final String KEY_PRE_IDLE_FACTOR_SHORT =
"pre_idle_factor_short";
+ private static final String KEY_USE_WINDOW_ALARMS = "use_window_alarms";
+ private static final long DEFAULT_FLEX_TIME_SHORT =
+ !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
private static final long DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
- !COMPRESS_TIME ? 3 * 60 * 1000L : 15 * 1000L;
+ !COMPRESS_TIME ? 60 * 1000L : 15 * 1000L;
private static final long DEFAULT_LIGHT_PRE_IDLE_TIMEOUT =
!COMPRESS_TIME ? 3 * 60 * 1000L : 30 * 1000L;
private static final long DEFAULT_LIGHT_IDLE_TIMEOUT =
!COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L;
+ private static final long DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX =
+ !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
+ private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX =
+ !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
private static final float DEFAULT_LIGHT_IDLE_FACTOR = 2f;
private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT =
!COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
@@ -958,6 +971,8 @@ public class DeviceIdleController extends SystemService
private static final float DEFAULT_LOCATION_ACCURACY = 20f;
private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT =
!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L;
+ private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX =
+ !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT =
(30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY =
@@ -983,6 +998,14 @@ public class DeviceIdleController extends SystemService
private static final boolean DEFAULT_WAIT_FOR_UNLOCK = true;
private static final float DEFAULT_PRE_IDLE_FACTOR_LONG = 1.67f;
private static final float DEFAULT_PRE_IDLE_FACTOR_SHORT = .33f;
+ private static final boolean DEFAULT_USE_WINDOW_ALARMS = true;
+
+ /**
+ * A somewhat short alarm window size that we will tolerate for various alarm timings.
+ *
+ * @see #KEY_FLEX_TIME_SHORT
+ */
+ public long FLEX_TIME_SHORT = DEFAULT_FLEX_TIME_SHORT;
/**
* This is the time, after becoming inactive, that we go in to the first
@@ -1002,13 +1025,28 @@ public class DeviceIdleController extends SystemService
public long LIGHT_PRE_IDLE_TIMEOUT = DEFAULT_LIGHT_PRE_IDLE_TIMEOUT;
/**
- * This is the initial time that we will run in idle maintenance mode.
+ * This is the initial time that we will run in light idle maintenance mode.
*
* @see #KEY_LIGHT_IDLE_TIMEOUT
*/
public long LIGHT_IDLE_TIMEOUT = DEFAULT_LIGHT_IDLE_TIMEOUT;
/**
+ * This is the initial alarm window size that we will tolerate for light idle maintenance
+ * timing.
+ *
+ * @see #KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX
+ */
+ public long LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX;
+
+ /**
+ * This is the maximum value that {@link #LIGHT_IDLE_TIMEOUT_INITIAL_FLEX} should take.
+ *
+ * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX
+ */
+ public long LIGHT_MAX_IDLE_TIMEOUT_FLEX = DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX;
+
+ /**
* Scaling factor to apply to the light idle mode time each time we complete a cycle.
*
* @see #KEY_LIGHT_IDLE_FACTOR
@@ -1016,7 +1054,7 @@ public class DeviceIdleController extends SystemService
public float LIGHT_IDLE_FACTOR = DEFAULT_LIGHT_IDLE_FACTOR;
/**
- * This is the maximum time we will run in idle maintenance mode.
+ * This is the maximum time we will stay in light idle mode.
*
* @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
*/
@@ -1099,13 +1137,22 @@ public class DeviceIdleController extends SystemService
/**
* This is the time, after seeing motion, that we wait after becoming inactive from
* that until we start looking for motion again.
+ *
* @see #KEY_MOTION_INACTIVE_TIMEOUT
*/
public long MOTION_INACTIVE_TIMEOUT = DEFAULT_MOTION_INACTIVE_TIMEOUT;
/**
+ * This is the alarm window size we will tolerate for motion detection timings.
+ *
+ * @see #KEY_MOTION_INACTIVE_TIMEOUT_FLEX
+ */
+ public long MOTION_INACTIVE_TIMEOUT_FLEX = DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX;
+
+ /**
* This is the time, after the inactive timeout elapses, that we will wait looking
* for motion until we truly consider the device to be idle.
+ *
* @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
*/
public long IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT;
@@ -1204,6 +1251,12 @@ public class DeviceIdleController extends SystemService
public boolean WAIT_FOR_UNLOCK = DEFAULT_WAIT_FOR_UNLOCK;
+ /**
+ * Whether to use window alarms. True to use window alarms (call AlarmManager.setWindow()).
+ * False to use the legacy inexact alarms (call AlarmManager.set()).
+ */
+ public boolean USE_WINDOW_ALARMS = DEFAULT_USE_WINDOW_ALARMS;
+
private final boolean mSmallBatteryDevice;
public Constants() {
@@ -1227,6 +1280,10 @@ public class DeviceIdleController extends SystemService
continue;
}
switch (name) {
+ case KEY_FLEX_TIME_SHORT:
+ FLEX_TIME_SHORT = properties.getLong(
+ KEY_FLEX_TIME_SHORT, DEFAULT_FLEX_TIME_SHORT);
+ break;
case KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT:
LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
@@ -1240,9 +1297,19 @@ public class DeviceIdleController extends SystemService
LIGHT_IDLE_TIMEOUT = properties.getLong(
KEY_LIGHT_IDLE_TIMEOUT, DEFAULT_LIGHT_IDLE_TIMEOUT);
break;
+ case KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX:
+ LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = properties.getLong(
+ KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
+ DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX);
+ break;
+ case KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX:
+ LIGHT_MAX_IDLE_TIMEOUT_FLEX = properties.getLong(
+ KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX,
+ DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX);
+ break;
case KEY_LIGHT_IDLE_FACTOR:
- LIGHT_IDLE_FACTOR = properties.getFloat(
- KEY_LIGHT_IDLE_FACTOR, DEFAULT_LIGHT_IDLE_FACTOR);
+ LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
+ KEY_LIGHT_IDLE_FACTOR, DEFAULT_LIGHT_IDLE_FACTOR));
break;
case KEY_LIGHT_MAX_IDLE_TIMEOUT:
LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
@@ -1291,6 +1358,11 @@ public class DeviceIdleController extends SystemService
MOTION_INACTIVE_TIMEOUT = properties.getLong(
KEY_MOTION_INACTIVE_TIMEOUT, DEFAULT_MOTION_INACTIVE_TIMEOUT);
break;
+ case KEY_MOTION_INACTIVE_TIMEOUT_FLEX:
+ MOTION_INACTIVE_TIMEOUT_FLEX = properties.getLong(
+ KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
+ DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX);
+ break;
case KEY_IDLE_AFTER_INACTIVE_TIMEOUT:
final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
@@ -1362,6 +1434,10 @@ public class DeviceIdleController extends SystemService
PRE_IDLE_FACTOR_SHORT = properties.getFloat(
KEY_PRE_IDLE_FACTOR_SHORT, DEFAULT_PRE_IDLE_FACTOR_SHORT);
break;
+ case KEY_USE_WINDOW_ALARMS:
+ USE_WINDOW_ALARMS = properties.getBoolean(
+ KEY_USE_WINDOW_ALARMS, DEFAULT_USE_WINDOW_ALARMS);
+ break;
default:
Slog.e(TAG, "Unknown configuration key: " + name);
break;
@@ -1373,6 +1449,10 @@ public class DeviceIdleController extends SystemService
void dump(PrintWriter pw) {
pw.println(" Settings:");
+ pw.print(" "); pw.print(KEY_FLEX_TIME_SHORT); pw.print("=");
+ TimeUtils.formatDuration(FLEX_TIME_SHORT, pw);
+ pw.println();
+
pw.print(" ");
pw.print(KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
pw.print("=");
@@ -1387,6 +1467,14 @@ public class DeviceIdleController extends SystemService
TimeUtils.formatDuration(LIGHT_IDLE_TIMEOUT, pw);
pw.println();
+ pw.print(" "); pw.print(KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX); pw.print("=");
+ TimeUtils.formatDuration(LIGHT_IDLE_TIMEOUT_INITIAL_FLEX, pw);
+ pw.println();
+
+ pw.print(" "); pw.print(KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX); pw.print("=");
+ TimeUtils.formatDuration(LIGHT_MAX_IDLE_TIMEOUT_FLEX, pw);
+ pw.println();
+
pw.print(" "); pw.print(KEY_LIGHT_IDLE_FACTOR); pw.print("=");
pw.print(LIGHT_IDLE_FACTOR);
pw.println();
@@ -1431,6 +1519,10 @@ public class DeviceIdleController extends SystemService
TimeUtils.formatDuration(MOTION_INACTIVE_TIMEOUT, pw);
pw.println();
+ pw.print(" "); pw.print(KEY_MOTION_INACTIVE_TIMEOUT_FLEX); pw.print("=");
+ TimeUtils.formatDuration(MOTION_INACTIVE_TIMEOUT_FLEX, pw);
+ pw.println();
+
pw.print(" "); pw.print(KEY_IDLE_AFTER_INACTIVE_TIMEOUT); pw.print("=");
TimeUtils.formatDuration(IDLE_AFTER_INACTIVE_TIMEOUT, pw);
pw.println();
@@ -1489,6 +1581,9 @@ public class DeviceIdleController extends SystemService
pw.print(" "); pw.print(KEY_PRE_IDLE_FACTOR_SHORT); pw.print("=");
pw.println(PRE_IDLE_FACTOR_SHORT);
+
+ pw.print(" "); pw.print(KEY_USE_WINDOW_ALARMS); pw.print("=");
+ pw.println(USE_WINDOW_ALARMS);
}
}
@@ -3199,7 +3294,8 @@ public class DeviceIdleController extends SystemService
mLightState = LIGHT_STATE_INACTIVE;
if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");
resetLightIdleManagementLocked();
- scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
+ scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
+ mConstants.FLEX_TIME_SHORT);
EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
}
}
@@ -3219,6 +3315,7 @@ public class DeviceIdleController extends SystemService
private void resetLightIdleManagementLocked() {
mNextLightIdleDelay = 0;
+ mNextLightIdleDelayFlex = 0;
mCurLightIdleBudget = 0;
cancelLightAlarmLocked();
}
@@ -3265,13 +3362,15 @@ public class DeviceIdleController extends SystemService
mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
// Reset the upcoming idle delays.
mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
+ mNextLightIdleDelayFlex = mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX;
mMaintenanceStartTime = 0;
if (!isOpsInactiveLocked()) {
// We have some active ops going on... give them a chance to finish
// before going in to our first idle.
mLightState = LIGHT_STATE_PRE_IDLE;
EventLogTags.writeDeviceIdleLight(mLightState, reason);
- scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT);
+ scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT,
+ mConstants.FLEX_TIME_SHORT);
break;
}
// Nothing active, fall through to immediately idle.
@@ -3290,12 +3389,11 @@ public class DeviceIdleController extends SystemService
}
}
mMaintenanceStartTime = 0;
- scheduleLightAlarmLocked(mNextLightIdleDelay);
+ scheduleLightAlarmLocked(mNextLightIdleDelay, mNextLightIdleDelayFlex);
mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
- (long)(mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
- if (mNextLightIdleDelay < mConstants.LIGHT_IDLE_TIMEOUT) {
- mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
- }
+ (long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
+ mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT_FLEX,
+ (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR));
if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
mLightState = LIGHT_STATE_IDLE;
EventLogTags.writeDeviceIdleLight(mLightState, reason);
@@ -3315,7 +3413,7 @@ public class DeviceIdleController extends SystemService
} else if (mCurLightIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
}
- scheduleLightAlarmLocked(mCurLightIdleBudget);
+ scheduleLightAlarmLocked(mCurLightIdleBudget, mConstants.FLEX_TIME_SHORT);
if (DEBUG) Slog.d(TAG,
"Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
@@ -3326,7 +3424,7 @@ public class DeviceIdleController extends SystemService
// We'd like to do maintenance, but currently don't have network
// connectivity... let's try to wait until the network comes back.
// We'll only wait for another full idle period, however, and then give up.
- scheduleLightAlarmLocked(mNextLightIdleDelay);
+ scheduleLightAlarmLocked(mNextLightIdleDelay, mNextLightIdleDelayFlex / 2);
if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
EventLogTags.writeDeviceIdleLight(mLightState, reason);
@@ -3844,40 +3942,75 @@ public class DeviceIdleController extends SystemService
mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP,
mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
} else {
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
+ if (mConstants.USE_WINDOW_ALARMS) {
+ mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mConstants.FLEX_TIME_SHORT,
+ mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
+ } else {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
+ }
}
}
- void scheduleLightAlarmLocked(long delay) {
- if (DEBUG) Slog.d(TAG, "scheduleLightAlarmLocked(" + delay + ")");
+ void scheduleLightAlarmLocked(long delay, long flex) {
+ if (DEBUG) {
+ Slog.d(TAG, "scheduleLightAlarmLocked(" + delay
+ + (mConstants.USE_WINDOW_ALARMS ? "/" + flex : "") + ")");
+ }
mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
+ if (mConstants.USE_WINDOW_ALARMS) {
+ mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextLightAlarmTime, flex,
+ "DeviceIdleController.light", mLightAlarmListener, mHandler);
+ } else {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextLightAlarmTime,
+ "DeviceIdleController.light", mLightAlarmListener, mHandler);
+ }
}
private void scheduleMotionRegistrationAlarmLocked() {
if (DEBUG) Slog.d(TAG, "scheduleMotionRegistrationAlarmLocked");
long nextMotionRegistrationAlarmTime =
mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT / 2;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionRegistrationAlarmTime,
- "DeviceIdleController.motion_registration", mMotionRegistrationAlarmListener,
- mHandler);
+ if (mConstants.USE_WINDOW_ALARMS) {
+ mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ nextMotionRegistrationAlarmTime, mConstants.MOTION_INACTIVE_TIMEOUT_FLEX,
+ "DeviceIdleController.motion_registration", mMotionRegistrationAlarmListener,
+ mHandler);
+ } else {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionRegistrationAlarmTime,
+ "DeviceIdleController.motion_registration", mMotionRegistrationAlarmListener,
+ mHandler);
+ }
}
private void scheduleMotionTimeoutAlarmLocked() {
if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked");
long nextMotionTimeoutAlarmTime =
mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime,
- "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+ if (mConstants.USE_WINDOW_ALARMS) {
+ mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ nextMotionTimeoutAlarmTime,
+ mConstants.MOTION_INACTIVE_TIMEOUT_FLEX,
+ "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+ } else {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime,
+ "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+ }
}
void scheduleSensingTimeoutAlarmLocked(long delay) {
if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextSensingTimeoutAlarmTime,
- "DeviceIdleController.sensing", mSensingTimeoutAlarmListener, mHandler);
+ if (mConstants.USE_WINDOW_ALARMS) {
+ mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mNextSensingTimeoutAlarmTime,
+ mConstants.FLEX_TIME_SHORT,
+ "DeviceIdleController.sensing", mSensingTimeoutAlarmListener, mHandler);
+ } else {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextSensingTimeoutAlarmTime,
+ "DeviceIdleController.sensing", mSensingTimeoutAlarmListener, mHandler);
+ }
}
private static int[] buildAppIdArray(ArrayMap<String, Integer> systemApps,
@@ -4852,7 +4985,13 @@ public class DeviceIdleController extends SystemService
if (mNextLightIdleDelay != 0) {
pw.print(" mNextIdleDelay=");
TimeUtils.formatDuration(mNextLightIdleDelay, pw);
- pw.println();
+ if (mConstants.USE_WINDOW_ALARMS) {
+ pw.print(" (flex=");
+ TimeUtils.formatDuration(mNextLightIdleDelayFlex, pw);
+ pw.println(")");
+ } else {
+ pw.println();
+ }
}
if (mNextLightAlarmTime != 0) {
pw.print(" mNextLightAlarmTime=");
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 96d59b80b479..9bd6c750fb13 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -4804,6 +4804,16 @@ public class AppOpsManager {
public static final int HISTORY_FLAG_DISCRETE = 1 << 1;
/**
+ * Flag for querying app op history: assemble attribution chains, and attach the last visible
+ * node in the chain to the start as a proxy info. This only applies to discrete accesses.
+ *
+ * TODO 191512294: Add to @SystemApi
+ *
+ * @hide
+ */
+ public static final int HISTORY_FLAG_GET_ATTRIBUTION_CHAINS = 1 << 2;
+
+ /**
* Flag for querying app op history: get all types of historical access information.
*
* @see #getHistoricalOps(HistoricalOpsRequest, Executor, Consumer)
@@ -4819,7 +4829,8 @@ public class AppOpsManager {
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "HISTORY_FLAG_" }, value = {
HISTORY_FLAG_AGGREGATE,
- HISTORY_FLAG_DISCRETE
+ HISTORY_FLAG_DISCRETE,
+ HISTORY_FLAG_GET_ATTRIBUTION_CHAINS
})
public @interface OpHistoryFlags {}
@@ -5037,7 +5048,8 @@ public class AppOpsManager {
* @return This builder.
*/
public @NonNull Builder setHistoryFlags(@OpHistoryFlags int flags) {
- Preconditions.checkFlagsArgument(flags, HISTORY_FLAGS_ALL);
+ Preconditions.checkFlagsArgument(flags,
+ HISTORY_FLAGS_ALL | HISTORY_FLAG_GET_ATTRIBUTION_CHAINS);
mHistoryFlags = flags;
return this;
}
@@ -5290,8 +5302,17 @@ public class AppOpsManager {
@Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag,
long discreteAccessTime, long discreteAccessDuration) {
getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag,
- uidState, opFlag, discreteAccessTime, discreteAccessDuration);
- };
+ uidState, opFlag, discreteAccessTime, discreteAccessDuration, null);
+ }
+
+ /** @hide */
+ public void addDiscreteAccess(int opCode, int uid, @NonNull String packageName,
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag,
+ long discreteAccessTime, long discreteAccessDuration,
+ @Nullable OpEventProxyInfo proxy) {
+ getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag,
+ uidState, opFlag, discreteAccessTime, discreteAccessDuration, proxy);
+ }
/** @hide */
@@ -5623,9 +5644,10 @@ public class AppOpsManager {
private void addDiscreteAccess(int opCode, @NonNull String packageName,
@Nullable String attributionTag, @UidState int uidState,
- @OpFlags int flag, long discreteAccessTime, long discreteAccessDuration) {
+ @OpFlags int flag, long discreteAccessTime, long discreteAccessDuration,
+ @Nullable OpEventProxyInfo proxy) {
getOrCreateHistoricalPackageOps(packageName).addDiscreteAccess(opCode, attributionTag,
- uidState, flag, discreteAccessTime, discreteAccessDuration);
+ uidState, flag, discreteAccessTime, discreteAccessDuration, proxy);
};
/**
@@ -5889,9 +5911,9 @@ public class AppOpsManager {
private void addDiscreteAccess(int opCode, @Nullable String attributionTag,
@UidState int uidState, @OpFlags int flag, long discreteAccessTime,
- long discreteAccessDuration) {
+ long discreteAccessDuration, @Nullable OpEventProxyInfo proxy) {
getOrCreateAttributedHistoricalOps(attributionTag).addDiscreteAccess(opCode, uidState,
- flag, discreteAccessTime, discreteAccessDuration);
+ flag, discreteAccessTime, discreteAccessDuration, proxy);
}
/**
@@ -6212,9 +6234,10 @@ public class AppOpsManager {
}
private void addDiscreteAccess(int opCode, @UidState int uidState, @OpFlags int flag,
- long discreteAccessTime, long discreteAccessDuration) {
+ long discreteAccessTime, long discreteAccessDuration,
+ @Nullable OpEventProxyInfo proxy) {
getOrCreateHistoricalOp(opCode).addDiscreteAccess(uidState,flag, discreteAccessTime,
- discreteAccessDuration);
+ discreteAccessDuration, proxy);
}
/**
@@ -6583,11 +6606,12 @@ public class AppOpsManager {
}
private void addDiscreteAccess(@UidState int uidState, @OpFlags int flag,
- long discreteAccessTime, long discreteAccessDuration) {
+ long discreteAccessTime, long discreteAccessDuration,
+ @Nullable OpEventProxyInfo proxy) {
List<AttributedOpEntry> discreteAccesses = getOrCreateDiscreteAccesses();
LongSparseArray<NoteOpEvent> accessEvents = new LongSparseArray<>();
long key = makeKey(uidState, flag);
- NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, null);
+ NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, proxy);
accessEvents.append(key, note);
AttributedOpEntry access = new AttributedOpEntry(mOp, false, accessEvents, null);
int insertionPoint = discreteAccesses.size() - 1;
@@ -10022,6 +10046,8 @@ public class AppOpsManager {
NoteOpEvent existingAccess = accessEvents.get(key);
if (existingAccess == null || existingAccess.getDuration() == -1) {
accessEvents.append(key, access);
+ } else if (existingAccess.mProxy == null && access.mProxy != null ) {
+ existingAccess.mProxy = access.mProxy;
}
}
if (reject != null) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 9ed76c1c13d1..a2c9795204ad 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -546,6 +546,10 @@ public final class LoadedApk {
if (aInfo.sharedLibraryFiles != null) {
int index = 0;
for (String lib : aInfo.sharedLibraryFiles) {
+ // sharedLibraryFiles might contain native shared libraries that are not APK paths.
+ if (!lib.endsWith(".apk")) {
+ continue;
+ }
if (!outSeenPaths.contains(lib) && !outZipPaths.contains(lib)) {
outZipPaths.add(index, lib);
index++;
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index 2777a7aae517..7ef0a19ec44c 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -420,7 +420,7 @@ public final class WallpaperColors implements Parcelable {
for (Map.Entry<Integer, Integer> colorEntry : mAllColors.entrySet()) {
if (colorEntry.getKey() != null) {
dest.writeInt(colorEntry.getKey());
- Integer population = mAllColors.get(colorEntry.getValue());
+ Integer population = colorEntry.getValue();
int populationInt = (population != null) ? population : 0;
dest.writeInt(populationInt);
}
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index e24332a1885a..5b259f71feda 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -212,9 +212,9 @@ public final class CameraExtensionCharacteristics {
*/
private static final class CameraExtensionManagerGlobal {
private static final String TAG = "CameraExtensionManagerGlobal";
- private static final String PROXY_PACKAGE_NAME = "com.android.camera";
+ private static final String PROXY_PACKAGE_NAME = "com.android.cameraextensions";
private static final String PROXY_SERVICE_NAME =
- "com.android.camera.CameraExtensionsProxyService";
+ "com.android.cameraextensions.CameraExtensionsProxyService";
// Singleton instance
private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER =
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index 662ebb356f4c..5c2855307509 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -389,6 +389,10 @@ public class VpnManager {
/**
* Starts a legacy VPN.
+ *
+ * Legacy VPN is deprecated starting from Android S. So this API shouldn't be called if the
+ * initial SDK version of device is Android S+. Otherwise, UnsupportedOperationException will be
+ * thrown.
* @hide
*/
public void startLegacyVpn(VpnProfile profile) {
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index c5d0cd40bc6d..4ef0e6e785e8 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -908,9 +908,32 @@ public final class PermissionManager {
*/
public static boolean shouldShowPackageForIndicatorCached(@NonNull Context context,
@NonNull String packageName) {
- if (SYSTEM_PKG.equals(packageName)) {
- return false;
+ return !getIndicatorExemptedPackages(context).contains(packageName);
+ }
+
+ /**
+ * Get the list of packages that are not shown by the indicators. Only a select few roles, and
+ * the system app itself, are hidden. These values are updated at most every 15 seconds.
+ * @hide
+ */
+ public static Set<String> getIndicatorExemptedPackages(@NonNull Context context) {
+ updateIndicatorExemptedPackages(context);
+ ArraySet<String> pkgNames = new ArraySet<>();
+ pkgNames.add(SYSTEM_PKG);
+ for (int i = 0; i < INDICATOR_EXEMPTED_PACKAGES.length; i++) {
+ String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i];
+ if (exemptedPackage != null) {
+ pkgNames.add(exemptedPackage);
+ }
}
+ return pkgNames;
+ }
+
+ /**
+ * Update the cached indicator exempted packages
+ * @hide
+ */
+ public static void updateIndicatorExemptedPackages(@NonNull Context context) {
long now = SystemClock.elapsedRealtime();
if (sLastIndicatorUpdateTime == -1
|| (now - sLastIndicatorUpdateTime) > EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS) {
@@ -919,14 +942,6 @@ public final class PermissionManager {
INDICATOR_EXEMPTED_PACKAGES[i] = context.getString(EXEMPTED_ROLES[i]);
}
}
- for (int i = 0; i < EXEMPTED_ROLES.length; i++) {
- String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i];
- if (exemptedPackage != null && exemptedPackage.equals(packageName)) {
- return false;
- }
- }
-
- return true;
}
/**
* Gets the list of packages that have permissions that specified
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index d4e548e1df1e..791764b4342f 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -410,7 +410,9 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
int usageAttr = usage.getPackageIdHash();
// If this usage has a proxy, but is not a proxy, it is the end of a chain.
- if (!proxies.containsKey(usageAttr) && usage.proxy != null) {
+ // TODO remove once camera converted
+ if (!proxies.containsKey(usageAttr) && usage.proxy != null
+ && !usage.op.equals(OPSTR_RECORD_AUDIO)) {
proxyLabels.put(usage, new ArrayList<>());
proxyPackages.add(usage.getPackageIdHash());
}
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java
index 846f2f94d055..315392bf6a58 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -20,11 +20,18 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.res.Resources;
+import android.media.AudioRecord;
import android.media.MediaSyncEvent;
+import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
+import com.android.internal.R;
import com.android.internal.util.DataClass;
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
/**
* Represents a result supporting the hotword detection.
@@ -187,16 +194,20 @@ public final class HotwordDetectedResult implements Parcelable {
return new PersistableBundle();
}
+ private static int sMaxBundleSize = -1;
+
/**
* Returns the maximum byte size of the information contained in the bundle.
*
- * <p>The total size will be calculated as a sum of byte sizes over all bundle keys.
- *
- * <p>For example, for a bundle containing a single key: {@code "example_key" -> 42.0f}, the
- * bundle size will be {@code 11 + Float.BYTES = 15} bytes.
+ * <p>The total size will be calculated by how much bundle data should be written into the
+ * Parcel.
*/
public static int getMaxBundleSize() {
- return 50;
+ if (sMaxBundleSize < 0) {
+ sMaxBundleSize = Resources.getSystem().getInteger(
+ R.integer.config_hotwordDetectedResultMaxBundleSize);
+ }
+ return sMaxBundleSize;
}
/**
@@ -212,6 +223,34 @@ public final class HotwordDetectedResult implements Parcelable {
return mMediaSyncEvent;
}
+ /**
+ * Returns how many bytes should be written into the Parcel
+ *
+ * @hide
+ */
+ public static int getParcelableSize(@NonNull Parcelable parcelable) {
+ final Parcel p = Parcel.obtain();
+ parcelable.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ final int size = p.dataSize();
+ p.recycle();
+ return size;
+ }
+
+ private void onConstructed() {
+ Preconditions.checkArgumentInRange(mScore, 0, getMaxScore(), "score");
+ Preconditions.checkArgumentInRange(mPersonalizedScore, 0, getMaxScore(),
+ "personalizedScore");
+ Preconditions.checkArgumentInRange(mHotwordPhraseId, 0, getMaxHotwordPhraseId(),
+ "hotwordPhraseId");
+ Preconditions.checkArgumentInRange((long) mHotwordDurationMillis, 0,
+ AudioRecord.getMaxSharedAudioHistoryMillis(), "hotwordDurationMillis");
+ if (!mExtras.isEmpty()) {
+ Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0, getMaxBundleSize(),
+ "extras");
+ }
+ }
+
// Code below generated by codegen v1.0.23.
@@ -290,7 +329,7 @@ public final class HotwordDetectedResult implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mExtras);
- // onConstructed(); // You can define this method to get a callback
+ onConstructed();
}
/**
@@ -422,7 +461,7 @@ public final class HotwordDetectedResult implements Parcelable {
//noinspection PointlessBooleanExpression
return true
&& mConfidenceLevel == that.mConfidenceLevel
- && java.util.Objects.equals(mMediaSyncEvent, that.mMediaSyncEvent)
+ && Objects.equals(mMediaSyncEvent, that.mMediaSyncEvent)
&& mHotwordOffsetMillis == that.mHotwordOffsetMillis
&& mHotwordDurationMillis == that.mHotwordDurationMillis
&& mAudioChannel == that.mAudioChannel
@@ -430,7 +469,7 @@ public final class HotwordDetectedResult implements Parcelable {
&& mScore == that.mScore
&& mPersonalizedScore == that.mPersonalizedScore
&& mHotwordPhraseId == that.mHotwordPhraseId
- && java.util.Objects.equals(mExtras, that.mExtras);
+ && Objects.equals(mExtras, that.mExtras);
}
@Override
@@ -441,7 +480,7 @@ public final class HotwordDetectedResult implements Parcelable {
int _hash = 1;
_hash = 31 * _hash + mConfidenceLevel;
- _hash = 31 * _hash + java.util.Objects.hashCode(mMediaSyncEvent);
+ _hash = 31 * _hash + Objects.hashCode(mMediaSyncEvent);
_hash = 31 * _hash + mHotwordOffsetMillis;
_hash = 31 * _hash + mHotwordDurationMillis;
_hash = 31 * _hash + mAudioChannel;
@@ -449,13 +488,13 @@ public final class HotwordDetectedResult implements Parcelable {
_hash = 31 * _hash + mScore;
_hash = 31 * _hash + mPersonalizedScore;
_hash = 31 * _hash + mHotwordPhraseId;
- _hash = 31 * _hash + java.util.Objects.hashCode(mExtras);
+ _hash = 31 * _hash + Objects.hashCode(mExtras);
return _hash;
}
@Override
@DataClass.Generated.Member
- public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
@@ -481,7 +520,7 @@ public final class HotwordDetectedResult implements Parcelable {
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ HotwordDetectedResult(@NonNull android.os.Parcel in) {
+ /* package-private */ HotwordDetectedResult(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -512,7 +551,7 @@ public final class HotwordDetectedResult implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mExtras);
- // onConstructed(); // You can define this method to get a callback
+ onConstructed();
}
@DataClass.Generated.Member
@@ -524,7 +563,7 @@ public final class HotwordDetectedResult implements Parcelable {
}
@Override
- public HotwordDetectedResult createFromParcel(@NonNull android.os.Parcel in) {
+ public HotwordDetectedResult createFromParcel(@NonNull Parcel in) {
return new HotwordDetectedResult(in);
}
};
@@ -745,10 +784,10 @@ public final class HotwordDetectedResult implements Parcelable {
}
@DataClass.Generated(
- time = 1621943150502L,
+ time = 1624361647985L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java",
- inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\nprivate void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index 93a7ec793536..567ee2f65565 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -76,6 +76,7 @@ public abstract class HotwordDetectionService extends Service {
private static final boolean DBG = true;
private static final long UPDATE_TIMEOUT_MILLIS = 5000;
+
/** @hide */
public static final String KEY_INITIALIZATION_STATUS = "initialization_status";
@@ -388,6 +389,14 @@ public abstract class HotwordDetectionService extends Service {
*/
public void onDetected(@NonNull HotwordDetectedResult result) {
requireNonNull(result);
+ final PersistableBundle persistableBundle = result.getExtras();
+ if (!persistableBundle.isEmpty() && HotwordDetectedResult.getParcelableSize(
+ persistableBundle) > HotwordDetectedResult.getMaxBundleSize()) {
+ throw new IllegalArgumentException(
+ "The bundle size of result is larger than max bundle size ("
+ + HotwordDetectedResult.getMaxBundleSize()
+ + ") of HotwordDetectedResult");
+ }
try {
mRemoteCallback.onDetected(result);
} catch (RemoteException e) {
diff --git a/core/java/android/view/CrossWindowBlurListeners.java b/core/java/android/view/CrossWindowBlurListeners.java
index e307b969ef91..761a2b88dec2 100644
--- a/core/java/android/view/CrossWindowBlurListeners.java
+++ b/core/java/android/view/CrossWindowBlurListeners.java
@@ -73,14 +73,14 @@ public final class CrossWindowBlurListeners {
return instance;
}
- boolean isCrossWindowBlurEnabled() {
+ public boolean isCrossWindowBlurEnabled() {
synchronized (sLock) {
attachInternalListenerIfNeededLocked();
return mCrossWindowBlurEnabled;
}
}
- void addListener(@NonNull @CallbackExecutor Executor executor,
+ public void addListener(@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<Boolean> listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
Preconditions.checkNotNull(executor, "executor cannot be null");
@@ -94,7 +94,7 @@ public final class CrossWindowBlurListeners {
}
- void removeListener(Consumer<Boolean> listener) {
+ public void removeListener(Consumer<Boolean> listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
synchronized (sLock) {
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 10ae69118f54..ce6d034c585e 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -286,6 +286,15 @@ public final class ContentCaptureEvent implements Parcelable {
return this;
}
+ boolean hasSameComposingSpan(@NonNull ContentCaptureEvent other) {
+ return mComposingStart == other.mComposingStart && mComposingEnd == other.mComposingEnd;
+ }
+
+ boolean hasSameSelectionSpan(@NonNull ContentCaptureEvent other) {
+ return mSelectionStartIndex == other.mSelectionStartIndex
+ && mSelectionEndIndex == other.mSelectionEndIndex;
+ }
+
private int getComposingStart() {
return mComposingStart;
}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index bcb914208958..7ec9d34bd364 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -368,7 +368,10 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
final CharSequence lastText = lastEvent.getText();
final boolean bothNonEmpty = !TextUtils.isEmpty(lastText)
&& !TextUtils.isEmpty(text);
- boolean equalContent = TextUtils.equals(lastText, text);
+ boolean equalContent =
+ TextUtils.equals(lastText, text)
+ && lastEvent.hasSameComposingSpan(event)
+ && lastEvent.hasSameSelectionSpan(event);
if (equalContent) {
addEvent = false;
} else if (bothNonEmpty) {
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index bdd12063e6cf..c4540b0d8726 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -163,6 +163,17 @@ public class BaseInputConnection implements InputConnection {
}
/**
+ * Called after only the composing region is modified (so it isn't called if the text also
+ * changes).
+ * <p>
+ * Default implementation does nothing.
+ *
+ * @hide
+ */
+ public void endComposingRegionEditInternal() {
+ }
+
+ /**
* Default implementation calls {@link #finishComposingText()} and
* {@code setImeConsumesInput(false)}.
*/
@@ -468,6 +479,7 @@ public class BaseInputConnection implements InputConnection {
// Note: sendCurrentText does nothing unless mFallbackMode is set
sendCurrentText();
endBatchEdit();
+ endComposingRegionEditInternal();
}
return true;
}
@@ -734,6 +746,7 @@ public class BaseInputConnection implements InputConnection {
// Note: sendCurrentText does nothing unless mFallbackMode is set
sendCurrentText();
endBatchEdit();
+ endComposingRegionEditInternal();
}
return true;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3c4fd5e93580..cd560d75d913 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10832,11 +10832,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
+ notifyContentCaptureTextChanged();
+ }
+
+ /**
+ * Notifies the ContentCapture service that the text of the view has changed (only if
+ * ContentCapture has been notified of this view's existence already).
+ *
+ * @hide
+ */
+ public void notifyContentCaptureTextChanged() {
// TODO(b/121045053): should use a flag / boolean to keep status of SHOWN / HIDDEN instead
// of using isLaidout(), so it's not called in cases where it's laid out but a
// notifyAppeared was not sent.
-
- // ContentCapture
if (isLaidOut() && isImportantForContentCapture() && getNotifiedContentCaptureAppeared()) {
final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
if (cm != null && cm.isContentCaptureEnabled()) {
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 4f2f973b51b1..dfd561a8cc30 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -23,7 +23,10 @@ import java.util.List;
/**
* Contains power usage of an application, system service, or hardware type.
+ *
+ * @deprecated Please use BatteryStatsManager.getBatteryUsageStats instead.
*/
+@Deprecated
public class BatterySipper implements Comparable<BatterySipper> {
@UnsupportedAppUsage
public int userId;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index b20f50d62de4..608782a39f6b 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -59,7 +59,10 @@ import java.util.List;
*
* The caller must initialize this class as soon as activity object is ready to use (for example, in
* onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
+ *
+ * @deprecated Please use BatteryStatsManager.getBatteryUsageStats instead.
*/
+@Deprecated
public class BatteryStatsHelper {
static final boolean DEBUG = false;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 1a23cc11fca8..134b1587ee58 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -70,12 +70,12 @@ import android.transition.TransitionInflater;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.AndroidRuntimeException;
-import android.view.AttachedSurfaceControl;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.util.TypedValue;
+import android.view.AttachedSurfaceControl;
import android.view.ContextThemeWrapper;
import android.view.CrossWindowBlurListeners;
import android.view.Gravity;
@@ -3148,7 +3148,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (cb == null || isDestroyed()) {
result = false;
} else {
- sendCloseSystemWindows("search");
int deviceId = event.getDeviceId();
SearchEvent searchEvent = null;
if (deviceId != 0) {
diff --git a/core/java/com/android/internal/util/function/DodecConsumer.java b/core/java/com/android/internal/util/function/DodecConsumer.java
new file mode 100644
index 000000000000..b4d2fb94d245
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DodecConsumer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
+
+
+/**
+ * A 12-argument {@link Consumer}
+ *
+ * @hide
+ */
+public interface DodecConsumer<A, B, C, D, E, F, G, H, I, J, K, L> {
+ void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
+}
diff --git a/core/java/com/android/internal/util/function/DodecFunction.java b/core/java/com/android/internal/util/function/DodecFunction.java
new file mode 100644
index 000000000000..178b2c111fd7
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DodecFunction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Function;
+
+/**
+ * A 12-argument {@link Function}
+ *
+ * @hide
+ */
+public interface DodecFunction<A, B, C, D, E, F, G, H, I, J, K, L, R> {
+ R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
+}
diff --git a/core/java/com/android/internal/util/function/DodecPredicate.java b/core/java/com/android/internal/util/function/DodecPredicate.java
new file mode 100644
index 000000000000..d3a2b856100f
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DodecPredicate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Predicate;
+
+/**
+ * A 12-argument {@link Predicate}
+ *
+ * @hide
+ */
+public interface DodecPredicate<A, B, C, D, E, F, G, H, I, J, K, L> {
+ boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index a60cc0fb101f..f073c1c046c5 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -23,6 +23,8 @@ import android.os.Message;
import com.android.internal.util.function.DecConsumer;
import com.android.internal.util.function.DecFunction;
+import com.android.internal.util.function.DodecConsumer;
+import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HeptConsumer;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexConsumer;
@@ -188,7 +190,7 @@ public interface PooledLambda {
A arg1) {
return acquire(PooledLambdaImpl.sPool,
function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -205,7 +207,7 @@ public interface PooledLambda {
A arg1) {
return acquire(PooledLambdaImpl.sPool,
function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -222,7 +224,7 @@ public interface PooledLambda {
A arg1) {
return acquire(PooledLambdaImpl.sPool,
function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -253,7 +255,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -273,7 +275,7 @@ public interface PooledLambda {
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -291,7 +293,7 @@ public interface PooledLambda {
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -309,7 +311,7 @@ public interface PooledLambda {
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -327,7 +329,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -345,7 +347,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -364,7 +366,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -384,7 +386,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -405,7 +407,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
function, 5, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -423,7 +425,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -441,7 +443,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -459,7 +461,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -477,7 +479,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -509,7 +511,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -530,7 +532,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -549,7 +551,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -568,7 +570,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -587,7 +589,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -606,7 +608,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -625,7 +627,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -644,7 +646,7 @@ public interface PooledLambda {
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -663,7 +665,7 @@ public interface PooledLambda {
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -696,7 +698,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -718,7 +720,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -738,7 +740,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -758,7 +760,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -778,7 +780,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -798,7 +800,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -818,7 +820,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -838,7 +840,7 @@ public interface PooledLambda {
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -858,7 +860,7 @@ public interface PooledLambda {
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -878,7 +880,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -898,7 +900,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -932,7 +934,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -955,7 +957,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -976,7 +978,7 @@ public interface PooledLambda {
function, A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1012,7 +1014,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1036,7 +1038,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1058,7 +1060,7 @@ public interface PooledLambda {
? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1095,7 +1097,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1120,7 +1122,7 @@ public interface PooledLambda {
? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1144,7 +1146,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1182,7 +1184,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1209,7 +1211,7 @@ public interface PooledLambda {
H arg8) {
return acquire(PooledLambdaImpl.sPool,
function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1234,7 +1236,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) {
return acquire(PooledLambdaImpl.sPool,
function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1274,7 +1276,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1302,7 +1304,7 @@ public interface PooledLambda {
E arg5, F arg6, G arg7, H arg8, I arg9) {
return acquire(PooledLambdaImpl.sPool,
function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, null, null);
+ arg9, null, null, null);
}
/**
@@ -1328,7 +1330,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) {
return acquire(PooledLambdaImpl.sPool,
function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, null, null);
+ arg9, null, null, null);
}
/**
@@ -1369,7 +1371,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, null, null);
+ arg9, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1398,7 +1400,7 @@ public interface PooledLambda {
D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) {
return acquire(PooledLambdaImpl.sPool,
function, 10, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, null);
+ arg9, arg10, null, null);
}
/**
@@ -1425,7 +1427,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) {
return acquire(PooledLambdaImpl.sPool,
function, 10, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, null);
+ arg9, arg10, null, null);
}
/**
@@ -1467,7 +1469,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 10, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
- arg8, arg9, arg10, null);
+ arg8, arg9, arg10, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1498,7 +1500,7 @@ public interface PooledLambda {
C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10, K arg11) {
return acquire(PooledLambdaImpl.sPool,
function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, arg11);
+ arg9, arg10, arg11, null);
}
/**
@@ -1528,7 +1530,7 @@ public interface PooledLambda {
K arg11) {
return acquire(PooledLambdaImpl.sPool,
function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, arg11);
+ arg9, arg10, arg11, null);
}
/**
@@ -1571,7 +1573,118 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
- arg8, arg9, arg10, arg11);
+ arg8, arg9, arg10, arg11, null);
+ return Message.obtain().setCallback(callback.recycleOnUse());
+ }
+ }
+
+ /**
+ * {@link PooledRunnable} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @param arg11 parameter supplied to {@code function} on call
+ * @param arg12 parameter supplied to {@code function} on call
+ * @return a {@link PooledRunnable}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ * arg11, arg12) }
+ */
+ static <A, B, C, D, E, F, G, H, I, J, K, L> PooledRunnable obtainRunnable(
+ DodecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? super K,
+ ? super L> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+ K arg11, L arg12) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 12, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9, arg10, arg11, arg12);
+ }
+
+ /**
+ * {@link PooledSupplier} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @param arg11 parameter supplied to {@code function} on call
+ * @param arg12 parameter supplied to {@code function} on call
+ * @return a {@link PooledSupplier}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ * arg11) }
+ */
+ static <A, B, C, D, E, F, G, H, I, J, K, L, R> PooledSupplier<R> obtainSupplier(
+ DodecFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? super K, ? extends L,
+ ? extends R> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+ K arg11, L arg12) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9, arg10, arg11, arg12);
+ }
+
+ /**
+ * Factory of {@link Message}s that contain an
+ * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+ * {@link Message#getCallback internal callback}.
+ *
+ * The callback is equivalent to one obtainable via
+ * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+ *
+ * Note that using this method with {@link android.os.Handler#handleMessage}
+ * is more efficient than the alternative of {@link android.os.Handler#post}
+ * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+ * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+ *
+ * You may optionally set a {@link Message#what} for the message if you want to be
+ * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+ * there's no need to do so
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @param arg11 parameter supplied to {@code function} on call
+ * @param arg12 parameter supplied to {@code function} on call
+ * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+ * arg7, arg8, arg9, arg10, arg11) } when handled
+ */
+ static <A, B, C, D, E, F, G, H, I, J, K, L> Message obtainMessage(
+ DodecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? super K, ? super L> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+ K arg11, L arg12) {
+ synchronized (Message.sPoolSync) {
+ PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+ function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ arg8, arg9, arg10, arg11, arg12);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index 1646a07b8001..19f0816e3e48 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -28,6 +28,9 @@ import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.function.DecConsumer;
import com.android.internal.util.function.DecFunction;
import com.android.internal.util.function.DecPredicate;
+import com.android.internal.util.function.DodecConsumer;
+import com.android.internal.util.function.DodecFunction;
+import com.android.internal.util.function.DodecPredicate;
import com.android.internal.util.function.HeptConsumer;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HeptPredicate;
@@ -458,6 +461,28 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
}
}
} break;
+
+ case 12: {
+ switch (returnType) {
+ case LambdaType.ReturnType.VOID: {
+ ((DodecConsumer) mFunc).accept(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8), popArg(9), popArg(10), popArg(11));
+ return null;
+ }
+ case LambdaType.ReturnType.BOOLEAN: {
+ return (R) (Object) ((DodecPredicate) mFunc).test(popArg(0),
+ popArg(1), popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7), popArg(8), popArg(9), popArg(10),
+ popArg(11));
+ }
+ case LambdaType.ReturnType.OBJECT: {
+ return (R) ((DodecFunction) mFunc).apply(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8), popArg(9), popArg(10), popArg(11));
+ }
+ }
+ } break;
}
throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType));
}
@@ -523,7 +548,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
*/
static <E extends PooledLambda> E acquire(Pool pool, Object func,
int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c,
- Object d, Object e, Object f, Object g, Object h, Object i, Object j, Object k) {
+ Object d, Object e, Object f, Object g, Object h, Object i, Object j, Object k,
+ Object l) {
PooledLambdaImpl r = acquire(pool);
if (DEBUG) {
Log.i(LOG_TAG,
@@ -543,6 +569,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
+ ", i = " + i
+ ", j = " + j
+ ", k = " + k
+ + ", l = " + l
+ ")");
}
r.mFunc = Objects.requireNonNull(func);
@@ -560,6 +587,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
setIfInBounds(r.mArgs, 8, i);
setIfInBounds(r.mArgs, 9, j);
setIfInBounds(r.mArgs, 10, k);
+ setIfInBounds(r.mArgs, 11, l);
return (E) r;
}
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index 4a70f7416075..299cbe12b4d1 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -257,7 +257,7 @@ public class CachingIconView extends ImageView {
boolean hasColor = color != ColoredIconHelper.COLOR_INVALID;
if (background == null) {
// This is the pre-S style -- colored icon with no background.
- if (hasColor) {
+ if (hasColor && icon != null) {
icon.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
} else {
@@ -265,7 +265,9 @@ public class CachingIconView extends ImageView {
// colorize the icon itself with the background color, creating an inverted effect.
if (hasColor) {
background.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
- icon.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
+ if (icon != null) {
+ icon.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
+ }
} else {
background.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
}
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 3d054a5e773c..02ffe8c5268e 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -99,6 +99,12 @@ public class EditableInputConnection extends BaseInputConnection
}
@Override
+ public void endComposingRegionEditInternal() {
+ // The ContentCapture service is interested in Composing-state changes.
+ mTextView.notifyContentCaptureTextChanged();
+ }
+
+ @Override
public void closeConnection() {
super.closeConnection();
synchronized(this) {
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 73c5460755b1..3f756d757706 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1524,7 +1524,9 @@ public class LockPatternView extends View {
if (virtualViewId != ExploreByTouchHelper.INVALID_ID) {
int row = (virtualViewId - VIRTUAL_BASE_VIEW_ID) / 3;
int col = (virtualViewId - VIRTUAL_BASE_VIEW_ID) % 3;
- return !mPatternDrawLookup[row][col];
+ if (row < 3) {
+ return !mPatternDrawLookup[row][col];
+ }
}
return false;
}
@@ -1570,7 +1572,6 @@ public class LockPatternView extends View {
final Rect bounds = mTempRect;
final int row = ordinal / 3;
final int col = ordinal % 3;
- final CellState cell = mCellStates[row][col];
float centerX = getCenterXForColumn(col);
float centerY = getCenterYForRow(row);
float cellheight = mSquareHeight * mHitFactor * 0.5f;
diff --git a/core/java/com/android/server/AppWidgetBackupBridge.java b/core/java/com/android/server/AppWidgetBackupBridge.java
index 7d82d355e3eb..8e834a87784d 100644
--- a/core/java/com/android/server/AppWidgetBackupBridge.java
+++ b/core/java/com/android/server/AppWidgetBackupBridge.java
@@ -47,9 +47,9 @@ public class AppWidgetBackupBridge {
: null;
}
- public static void restoreStarting(int userId) {
+ public static void systemRestoreStarting(int userId) {
if (sAppWidgetService != null) {
- sAppWidgetService.restoreStarting(userId);
+ sAppWidgetService.systemRestoreStarting(userId);
}
}
@@ -59,9 +59,9 @@ public class AppWidgetBackupBridge {
}
}
- public static void restoreFinished(int userId) {
+ public static void systemRestoreFinished(int userId) {
if (sAppWidgetService != null) {
- sAppWidgetService.restoreFinished(userId);
+ sAppWidgetService.systemRestoreFinished(userId);
}
}
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 6976ace36c11..a8dcbaffeeb5 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -86,6 +86,7 @@ public class SystemConfig {
// and "allow-ignore-location-settings".
private static final int ALLOW_OVERRIDE_APP_RESTRICTIONS = 0x100;
private static final int ALLOW_IMPLICIT_BROADCASTS = 0x200;
+ private static final int ALLOW_VENDOR_APEX = 0x400;
private static final int ALLOW_ALL = ~0;
// property for runtime configuration differentiation
@@ -240,7 +241,7 @@ public class SystemConfig {
private final ArraySet<String> mRollbackWhitelistedPackages = new ArraySet<>();
private final ArraySet<String> mWhitelistedStagedInstallers = new ArraySet<>();
- private final ArraySet<String> mAllowedPartnerApexes = new ArraySet<>();
+ private final ArraySet<String> mAllowedVendorApexes = new ArraySet<>();
/**
* Map of system pre-defined, uniquely named actors; keys are namespace,
@@ -411,8 +412,8 @@ public class SystemConfig {
return mWhitelistedStagedInstallers;
}
- public Set<String> getAllowedPartnerApexes() {
- return mAllowedPartnerApexes;
+ public Set<String> getAllowedVendorApexes() {
+ return mAllowedVendorApexes;
}
public ArraySet<String> getAppDataIsolationWhitelistedApps() {
@@ -489,7 +490,7 @@ public class SystemConfig {
// Vendors are only allowed to customize these
int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS
- | ALLOW_ASSOCIATIONS;
+ | ALLOW_ASSOCIATIONS | ALLOW_VENDOR_APEX;
if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.O_MR1) {
// For backward compatibility
vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
@@ -530,7 +531,8 @@ public class SystemConfig {
}
// Allow OEM to customize these
- int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS;
+ int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS
+ | ALLOW_VENDOR_APEX;
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
readPermissions(Environment.buildPath(
@@ -541,7 +543,8 @@ public class SystemConfig {
// the use of hidden APIs from the product partition.
int productPermissionFlag = ALLOW_FEATURES | ALLOW_LIBS | ALLOW_PERMISSIONS
| ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS | ALLOW_HIDDENAPI_WHITELISTING
- | ALLOW_ASSOCIATIONS | ALLOW_OVERRIDE_APP_RESTRICTIONS | ALLOW_IMPLICIT_BROADCASTS;
+ | ALLOW_ASSOCIATIONS | ALLOW_OVERRIDE_APP_RESTRICTIONS | ALLOW_IMPLICIT_BROADCASTS
+ | ALLOW_VENDOR_APEX;
if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.R) {
// TODO(b/157393157): This must check product interface enforcement instead of
// DEVICE_INITIAL_SDK_INT for the devices without product interface enforcement.
@@ -668,6 +671,7 @@ public class SystemConfig {
(permissionFlag & ALLOW_OVERRIDE_APP_RESTRICTIONS) != 0;
final boolean allowImplicitBroadcasts = (permissionFlag & ALLOW_IMPLICIT_BROADCASTS)
!= 0;
+ final boolean allowVendorApex = (permissionFlag & ALLOW_VENDOR_APEX) != 0;
while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -1217,15 +1221,14 @@ public class SystemConfig {
}
XmlUtils.skipCurrentTag(parser);
} break;
- case "allowed-partner-apex": {
- // TODO(b/189274479): should this be allowOemPermissions instead?
- if (allowAppConfigs) {
+ case "allowed-vendor-apex": {
+ if (allowVendorApex) {
String pkgName = parser.getAttributeValue(null, "package");
if (pkgName == null) {
Slog.w(TAG, "<" + name + "> without package in " + permFile
+ " at " + parser.getPositionDescription());
} else {
- mAllowedPartnerApexes.add(pkgName);
+ mAllowedVendorApexes.add(pkgName);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
diff --git a/core/java/com/android/server/WidgetBackupProvider.java b/core/java/com/android/server/WidgetBackupProvider.java
index a2efbdd6f2f8..5453c4de6986 100644
--- a/core/java/com/android/server/WidgetBackupProvider.java
+++ b/core/java/com/android/server/WidgetBackupProvider.java
@@ -28,7 +28,7 @@ import java.util.List;
public interface WidgetBackupProvider {
public List<String> getWidgetParticipants(int userId);
public byte[] getWidgetState(String packageName, int userId);
- public void restoreStarting(int userId);
+ public void systemRestoreStarting(int userId);
public void restoreWidgetState(String packageName, byte[] restoredState, int userId);
- public void restoreFinished(int userId);
+ public void systemRestoreFinished(int userId);
}
diff --git a/core/proto/android/server/apphibernationservice.proto b/core/proto/android/server/apphibernationservice.proto
index d341c4b2f0a8..64c2a322548f 100644
--- a/core/proto/android/server/apphibernationservice.proto
+++ b/core/proto/android/server/apphibernationservice.proto
@@ -39,4 +39,5 @@ message GlobalLevelHibernationStatesProto {
message GlobalLevelHibernationStateProto {
optional string package_name = 1;
optional bool hibernated = 2;
+ optional int64 saved_byte = 3;
} \ No newline at end of file
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 3ecb1dddd916..55e5685b95e5 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -42,4 +42,8 @@
<!-- Allow SystemUI to show the shutdown dialog -->
<bool name="config_showSysuiShutdown">true</bool>
+
+ <!-- Component name of the activity used to inform a user about a sensory being blocked because
+ of privacy settings. -->
+ <string name="config_sensorUseStartedActivity">com.android.systemui/com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity</string>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f4aff9467cee..7c7a89385c5e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1706,6 +1706,10 @@
config_enableFusedLocationOverlay is false. -->
<string name="config_fusedLocationProviderPackageName" translatable="false">com.android.location.fused</string>
+ <!-- Default value for the ADAS GNSS Location Enabled setting if this setting has never been
+ set before. -->
+ <bool name="config_defaultAdasGnssLocationEnabled" translatable="false">false</bool>
+
<string-array name="config_locationExtraPackageNames" translatable="false"></string-array>
<!-- The package name of the default network recommendation app.
@@ -5007,6 +5011,10 @@
<!-- Default value for Settings.ASSIST_TOUCH_GESTURE_ENABLED -->
<bool name="config_assistTouchGestureEnabledDefault">true</bool>
+ <!-- The maximum byte size of the information contained in the bundle of
+ HotwordDetectedResult. -->
+ <integer translatable="false" name="config_hotwordDetectedResultMaxBundleSize">0</integer>
+
<!-- The amount of dimming to apply to wallpapers with mid range luminance. 0 displays
the wallpaper at full brightness. 1 displays the wallpaper as fully black. -->
<item name="config_wallpaperDimAmount" format="float" type="dimen">0.05</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5f849b4e1eb0..94c65a04e244 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1918,6 +1918,7 @@
<java-symbol type="bool" name="config_tintNotificationActionButtons" />
<java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
<java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
+ <java-symbol type="bool" name="config_defaultAdasGnssLocationEnabled" />
<java-symbol type="bool" name="config_enableFusedLocationOverlay" />
<java-symbol type="bool" name="config_enableGeocoderOverlay" />
<java-symbol type="bool" name="config_enableGeofenceOverlay" />
@@ -4404,5 +4405,7 @@
<java-symbol type="bool" name="config_assistLongPressHomeEnabledDefault" />
<java-symbol type="bool" name="config_assistTouchGestureEnabledDefault" />
+ <java-symbol type="integer" name="config_hotwordDetectedResultMaxBundleSize" />
+
<java-symbol type="dimen" name="config_wallpaperDimAmount" />
</resources>
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
deleted file mode 100644
index 6fad4b8dcdac..000000000000
--- a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 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 android.app.appsearch;
-
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.server.appsearch.testing.AppSearchEmail;
-
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.Test;
-
-import java.util.Set;
-
-public class PutDocumentsRequestTest {
-
- @Test
- public void addGenericDocument_byCollection() {
- Set<AppSearchEmail> emails =
- ImmutableSet.of(
- new AppSearchEmail.Builder("namespace", "test1").build(),
- new AppSearchEmail.Builder("namespace", "test2").build());
- PutDocumentsRequest request =
- new PutDocumentsRequest.Builder().addGenericDocuments(emails).build();
-
- assertThat(request.getGenericDocuments().get(0).getId()).isEqualTo("test1");
- assertThat(request.getGenericDocuments().get(1).getId()).isEqualTo("test2");
- }
-}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 89d2b743a619..72a145fa6a05 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -151,7 +151,7 @@ public class AndroidKeyStoreProvider extends Provider {
* Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
* primitive.
*
- * <p>The following primitives are supported: {@link Cipher} and {@link Mac}.
+ * <p>The following primitives are supported: {@link Cipher}, {@link Signature} and {@link Mac}.
*
* @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
* is not in progress.
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 70f03d234a56..f28ee820eb35 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -117,7 +117,9 @@
<!-- This should be at least the size of bubble_expanded_view_padding; it is used to include
a slight touch slop around the expanded view. -->
<dimen name="bubble_expanded_view_slop">8dp</dimen>
- <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded -->
+ <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded.
+ If this value changes then R.dimen.bubble_expanded_view_min_height in CtsVerifier
+ should also be updated. -->
<dimen name="bubble_expanded_default_height">180dp</dimen>
<!-- On large screens the width of the expanded view is restricted to this size. -->
<dimen name="bubble_expanded_view_tablet_width">412dp</dimen>
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index c770150650e2..45a4f6c9c70d 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -331,6 +331,8 @@ public:
mSkiaLayer.reset();
}
+ mProperties.mutateLayerProperties().mutableStretchEffect().clear();
+ mStretchMask.clear();
// Clear out the previous snapshot and the image filter the previous
// snapshot was created with whenever the layer changes.
mSnapshotResult.snapshot = nullptr;
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index 43f805d906a5..17cd3ceb577c 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -186,6 +186,7 @@ static const SkString stretchShader = SkString(R"(
static const float ZERO = 0.f;
static const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
+static const char CONTENT_TEXTURE[] = "uContentTexture";
sk_sp<SkShader> StretchEffect::getShader(float width, float height,
const sk_sp<SkImage>& snapshotImage,
@@ -207,7 +208,7 @@ sk_sp<SkShader> StretchEffect::getShader(float width, float height,
mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
}
- mBuilder->child("uContentTexture") =
+ mBuilder->child(CONTENT_TEXTURE) =
snapshotImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
SkSamplingOptions(SkFilterMode::kLinear), matrix);
mBuilder->uniform("uInterpolationStrength").set(&INTERPOLATION_STRENGTH_VALUE, 1);
@@ -226,7 +227,9 @@ sk_sp<SkShader> StretchEffect::getShader(float width, float height,
mBuilder->uniform("viewportWidth").set(&width, 1);
mBuilder->uniform("viewportHeight").set(&height, 1);
- return mBuilder->makeShader(nullptr, false);
+ auto result = mBuilder->makeShader(nullptr, false);
+ mBuilder->child(CONTENT_TEXTURE) = nullptr;
+ return result;
}
sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index 25777c278a11..3eab9f05ebe5 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -113,6 +113,10 @@ public:
return !isEmpty();
}
+ void clear() {
+ mBuilder = nullptr;
+ }
+
private:
static sk_sp<SkRuntimeEffect> getStretchEffect();
mutable SkVector mStretchDirection{0, 0};
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index c9e4e0a9cb92..5d5c0fc6265d 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -122,6 +122,9 @@ interface ILocationManager
boolean isLocationEnabledForUser(int userId);
void setLocationEnabledForUser(boolean enabled, int userId);
+ boolean isAdasGnssLocationEnabledForUser(int userId);
+ void setAdasGnssLocationEnabledForUser(boolean enabled, int userId);
+
void addTestProvider(String name, in ProviderProperties properties,
in List<String> locationTags, String packageName, @nullable String attributionTag);
void removeTestProvider(String provider, String packageName, @nullable String attributionTag);
diff --git a/location/java/android/location/LastLocationRequest.java b/location/java/android/location/LastLocationRequest.java
index 9ea8048ad476..0970c1c76a36 100644
--- a/location/java/android/location/LastLocationRequest.java
+++ b/location/java/android/location/LastLocationRequest.java
@@ -34,12 +34,15 @@ import java.util.Objects;
public final class LastLocationRequest implements Parcelable {
private final boolean mHiddenFromAppOps;
+ private final boolean mAdasGnssBypass;
private final boolean mLocationSettingsIgnored;
private LastLocationRequest(
boolean hiddenFromAppOps,
+ boolean adasGnssBypass,
boolean locationSettingsIgnored) {
mHiddenFromAppOps = hiddenFromAppOps;
+ mAdasGnssBypass = adasGnssBypass;
mLocationSettingsIgnored = locationSettingsIgnored;
}
@@ -56,6 +59,21 @@ public final class LastLocationRequest implements Parcelable {
}
/**
+ * Returns true if this request may access GNSS even if location settings would normally deny
+ * this, in order to enable automotive safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced
+ * Driving Assistance Systems) application.
+ *
+ * @return true if all limiting factors will be ignored to satisfy GNSS request
+ * @hide
+ */
+ // TODO: make this system api
+ public boolean isAdasGnssBypass() {
+ return mAdasGnssBypass;
+ }
+
+
+ /**
* Returns true if location settings, throttling, background location limits, and any other
* possible limiting factors will be ignored in order to satisfy this last location request.
*
@@ -65,12 +83,22 @@ public final class LastLocationRequest implements Parcelable {
return mLocationSettingsIgnored;
}
+ /**
+ * Returns true if any bypass flag is set on this request. For internal use only.
+ *
+ * @hide
+ */
+ public boolean isBypass() {
+ return mAdasGnssBypass || mLocationSettingsIgnored;
+ }
+
public static final @NonNull Parcelable.Creator<LastLocationRequest> CREATOR =
new Parcelable.Creator<LastLocationRequest>() {
@Override
public LastLocationRequest createFromParcel(Parcel in) {
return new LastLocationRequest(
/* hiddenFromAppOps= */ in.readBoolean(),
+ /* adasGnssBypass= */ in.readBoolean(),
/* locationSettingsIgnored= */ in.readBoolean());
}
@Override
@@ -86,6 +114,7 @@ public final class LastLocationRequest implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeBoolean(mHiddenFromAppOps);
+ parcel.writeBoolean(mAdasGnssBypass);
parcel.writeBoolean(mLocationSettingsIgnored);
}
@@ -99,12 +128,13 @@ public final class LastLocationRequest implements Parcelable {
}
LastLocationRequest that = (LastLocationRequest) o;
return mHiddenFromAppOps == that.mHiddenFromAppOps
+ && mAdasGnssBypass == that.mAdasGnssBypass
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored;
}
@Override
public int hashCode() {
- return Objects.hash(mHiddenFromAppOps, mLocationSettingsIgnored);
+ return Objects.hash(mHiddenFromAppOps, mAdasGnssBypass, mLocationSettingsIgnored);
}
@NonNull
@@ -115,8 +145,11 @@ public final class LastLocationRequest implements Parcelable {
if (mHiddenFromAppOps) {
s.append("hiddenFromAppOps, ");
}
+ if (mAdasGnssBypass) {
+ s.append("adasGnssBypass, ");
+ }
if (mLocationSettingsIgnored) {
- s.append("locationSettingsIgnored, ");
+ s.append("settingsBypass, ");
}
if (s.length() > "LastLocationRequest[".length()) {
s.setLength(s.length() - 2);
@@ -131,6 +164,7 @@ public final class LastLocationRequest implements Parcelable {
public static final class Builder {
private boolean mHiddenFromAppOps;
+ private boolean mAdasGnssBypass;
private boolean mLocationSettingsIgnored;
/**
@@ -138,6 +172,7 @@ public final class LastLocationRequest implements Parcelable {
*/
public Builder() {
mHiddenFromAppOps = false;
+ mAdasGnssBypass = false;
mLocationSettingsIgnored = false;
}
@@ -146,6 +181,7 @@ public final class LastLocationRequest implements Parcelable {
*/
public Builder(@NonNull LastLocationRequest lastLocationRequest) {
mHiddenFromAppOps = lastLocationRequest.mHiddenFromAppOps;
+ mAdasGnssBypass = lastLocationRequest.mAdasGnssBypass;
mLocationSettingsIgnored = lastLocationRequest.mLocationSettingsIgnored;
}
@@ -164,6 +200,25 @@ public final class LastLocationRequest implements Parcelable {
}
/**
+ * If set to true, indicates that the client is an ADAS (Advanced Driving Assistance
+ * Systems) client, which requires access to GNSS even if location settings would normally
+ * deny this, in order to enable auto safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS
+ * application. Defaults to false.
+ *
+ * <p>Permissions enforcement occurs when resulting location request is actually used, not
+ * when this method is invoked.
+ *
+ * @hide
+ */
+ // TODO: make this system api
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public @NonNull LastLocationRequest.Builder setAdasGnssBypass(boolean adasGnssBypass) {
+ mAdasGnssBypass = adasGnssBypass;
+ return this;
+ }
+
+ /**
* If set to true, indicates that location settings, throttling, background location limits,
* and any other possible limiting factors should be ignored in order to satisfy this
* last location request. This is only intended for use in user initiated emergency
@@ -186,6 +241,7 @@ public final class LastLocationRequest implements Parcelable {
public @NonNull LastLocationRequest build() {
return new LastLocationRequest(
mHiddenFromAppOps,
+ mAdasGnssBypass,
mLocationSettingsIgnored);
}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index ae44c5e34521..526b84e85e38 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -315,6 +315,33 @@ public class LocationManager {
public static final String EXTRA_LOCATION_ENABLED = "android.location.extra.LOCATION_ENABLED";
/**
+ * Broadcast intent action when the ADAS (Advanced Driving Assistance Systems) GNSS location
+ * enabled state changes. Includes a boolean intent extra, {@link #EXTRA_ADAS_GNSS_ENABLED},
+ * with the enabled state of ADAS GNSS location. This broadcast only has meaning on automotive
+ * devices.
+ *
+ * @see #EXTRA_ADAS_GNSS_ENABLED
+ * @see #isAdasGnssLocationEnabled()
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ADAS_GNSS_ENABLED_CHANGED =
+ "android.location.action.ADAS_GNSS_ENABLED_CHANGED";
+
+ /**
+ * Intent extra included with {@link #ACTION_ADAS_GNSS_ENABLED_CHANGED} broadcasts, containing
+ * the boolean enabled state of ADAS GNSS location.
+ *
+ * @see #ACTION_ADAS_GNSS_ENABLED_CHANGED
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ public static final String EXTRA_ADAS_GNSS_ENABLED = "android.location.extra.ADAS_GNSS_ENABLED";
+
+ /**
* Broadcast intent action indicating that a high power location requests
* has either started or stopped being active. The current state of
* active location requests should be read from AppOpsManager using
@@ -621,6 +648,42 @@ public class LocationManager {
}
/**
+ * Returns the current enabled/disabled state of ADAS (Advanced Driving Assistance Systems)
+ * GNSS location access for the given user. This controls safety critical automotive access to
+ * GNSS location. This only has meaning on automotive devices.
+ *
+ * @return true if ADAS location is enabled and false if ADAS location is disabled.
+ *
+ * @hide
+ */
+ //TODO: @SystemApi
+ public boolean isAdasGnssLocationEnabled() {
+ try {
+ return mService.isAdasGnssLocationEnabledForUser(mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Enables or disables ADAS (Advanced Driving Assistance Systems) GNSS location access for the
+ * given user. This only has meaning on automotive devices.
+ *
+ * @param enabled true to enable ADAS location and false to disable ADAS location.
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ @RequiresPermission(WRITE_SECURE_SETTINGS)
+ public void setAdasGnssLocationEnabled(boolean enabled) {
+ try {
+ mService.setAdasGnssLocationEnabledForUser(enabled, mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the current enabled/disabled status of the given provider. To listen for changes, see
* {@link #PROVIDERS_CHANGED_ACTION}.
*
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index a3842a1ffd0a..b48e59676ac1 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -194,6 +194,7 @@ public final class LocationRequest implements Parcelable {
private float mMinUpdateDistanceMeters;
private final long mMaxUpdateDelayMillis;
private boolean mHideFromAppOps;
+ private final boolean mAdasGnssBypass;
private boolean mLocationSettingsIgnored;
private boolean mLowPower;
private @Nullable WorkSource mWorkSource;
@@ -236,7 +237,7 @@ public final class LocationRequest implements Parcelable {
if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
quality = POWER_NONE;
} else if (LocationManager.GPS_PROVIDER.equals(provider)) {
- quality = ACCURACY_FINE;
+ quality = QUALITY_HIGH_ACCURACY;
} else {
quality = POWER_LOW;
}
@@ -289,6 +290,7 @@ public final class LocationRequest implements Parcelable {
float minUpdateDistanceMeters,
long maxUpdateDelayMillis,
boolean hiddenFromAppOps,
+ boolean adasGnssBypass,
boolean locationSettingsIgnored,
boolean lowPower,
WorkSource workSource) {
@@ -302,8 +304,9 @@ public final class LocationRequest implements Parcelable {
mMinUpdateDistanceMeters = minUpdateDistanceMeters;
mMaxUpdateDelayMillis = maxUpdateDelayMillis;
mHideFromAppOps = hiddenFromAppOps;
- mLowPower = lowPower;
+ mAdasGnssBypass = adasGnssBypass;
mLocationSettingsIgnored = locationSettingsIgnored;
+ mLowPower = lowPower;
mWorkSource = Objects.requireNonNull(workSource);
}
@@ -339,15 +342,15 @@ public final class LocationRequest implements Parcelable {
switch (quality) {
case POWER_HIGH:
// fall through
- case ACCURACY_FINE:
+ case QUALITY_HIGH_ACCURACY:
mQuality = QUALITY_HIGH_ACCURACY;
break;
- case ACCURACY_BLOCK:
+ case QUALITY_BALANCED_POWER_ACCURACY:
mQuality = QUALITY_BALANCED_POWER_ACCURACY;
break;
case POWER_LOW:
// fall through
- case ACCURACY_CITY:
+ case QUALITY_LOW_POWER:
mQuality = QUALITY_LOW_POWER;
break;
case POWER_NONE:
@@ -648,6 +651,21 @@ public final class LocationRequest implements Parcelable {
}
/**
+ * Returns true if this request may access GNSS even if location settings would normally deny
+ * this, in order to enable automotive safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced
+ * Driving Assistance Systems) application.
+ *
+ * @return true if all limiting factors will be ignored to satisfy GNSS request
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ public boolean isAdasGnssBypass() {
+ return mAdasGnssBypass;
+ }
+
+ /**
* @hide
* @deprecated LocationRequests should be treated as immutable.
*/
@@ -673,6 +691,15 @@ public final class LocationRequest implements Parcelable {
}
/**
+ * Returns true if any bypass flag is set on this request. For internal use only.
+ *
+ * @hide
+ */
+ public boolean isBypass() {
+ return mAdasGnssBypass || mLocationSettingsIgnored;
+ }
+
+ /**
* @hide
* @deprecated LocationRequests should be treated as immutable.
*/
@@ -749,6 +776,7 @@ public final class LocationRequest implements Parcelable {
/* minUpdateDistanceMeters= */ in.readFloat(),
/* maxUpdateDelayMillis= */ in.readLong(),
/* hiddenFromAppOps= */ in.readBoolean(),
+ /* adasGnssBypass= */ in.readBoolean(),
/* locationSettingsIgnored= */ in.readBoolean(),
/* lowPower= */ in.readBoolean(),
/* workSource= */ in.readTypedObject(WorkSource.CREATOR));
@@ -777,6 +805,7 @@ public final class LocationRequest implements Parcelable {
parcel.writeFloat(mMinUpdateDistanceMeters);
parcel.writeLong(mMaxUpdateDelayMillis);
parcel.writeBoolean(mHideFromAppOps);
+ parcel.writeBoolean(mAdasGnssBypass);
parcel.writeBoolean(mLocationSettingsIgnored);
parcel.writeBoolean(mLowPower);
parcel.writeTypedObject(mWorkSource, 0);
@@ -801,6 +830,7 @@ public final class LocationRequest implements Parcelable {
&& Float.compare(that.mMinUpdateDistanceMeters, mMinUpdateDistanceMeters) == 0
&& mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis
&& mHideFromAppOps == that.mHideFromAppOps
+ && mAdasGnssBypass == that.mAdasGnssBypass
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored
&& mLowPower == that.mLowPower
&& Objects.equals(mProvider, that.mProvider)
@@ -866,8 +896,11 @@ public final class LocationRequest implements Parcelable {
if (mHideFromAppOps) {
s.append(", hiddenFromAppOps");
}
+ if (mAdasGnssBypass) {
+ s.append(", adasGnssBypass");
+ }
if (mLocationSettingsIgnored) {
- s.append(", locationSettingsIgnored");
+ s.append(", settingsBypass");
}
if (mWorkSource != null && !mWorkSource.isEmpty()) {
s.append(", ").append(mWorkSource);
@@ -889,6 +922,7 @@ public final class LocationRequest implements Parcelable {
private float mMinUpdateDistanceMeters;
private long mMaxUpdateDelayMillis;
private boolean mHiddenFromAppOps;
+ private boolean mAdasGnssBypass;
private boolean mLocationSettingsIgnored;
private boolean mLowPower;
@Nullable private WorkSource mWorkSource;
@@ -908,6 +942,7 @@ public final class LocationRequest implements Parcelable {
mMinUpdateDistanceMeters = 0;
mMaxUpdateDelayMillis = 0;
mHiddenFromAppOps = false;
+ mAdasGnssBypass = false;
mLocationSettingsIgnored = false;
mLowPower = false;
mWorkSource = null;
@@ -925,6 +960,7 @@ public final class LocationRequest implements Parcelable {
mMinUpdateDistanceMeters = locationRequest.mMinUpdateDistanceMeters;
mMaxUpdateDelayMillis = locationRequest.mMaxUpdateDelayMillis;
mHiddenFromAppOps = locationRequest.mHideFromAppOps;
+ mAdasGnssBypass = locationRequest.mAdasGnssBypass;
mLocationSettingsIgnored = locationRequest.mLocationSettingsIgnored;
mLowPower = locationRequest.mLowPower;
mWorkSource = locationRequest.mWorkSource;
@@ -977,10 +1013,10 @@ public final class LocationRequest implements Parcelable {
public @NonNull Builder setQuality(@NonNull Criteria criteria) {
switch (criteria.getAccuracy()) {
case Criteria.ACCURACY_COARSE:
- mQuality = ACCURACY_BLOCK;
+ mQuality = QUALITY_BALANCED_POWER_ACCURACY;
break;
case Criteria.ACCURACY_FINE:
- mQuality = ACCURACY_FINE;
+ mQuality = QUALITY_HIGH_ACCURACY;
break;
default: {
if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) {
@@ -1092,6 +1128,25 @@ public final class LocationRequest implements Parcelable {
}
/**
+ * If set to true, indicates that the client is an ADAS (Advanced Driving Assistance
+ * Systems) client, which requires access to GNSS even if location settings would normally
+ * deny this, in order to enable auto safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS
+ * application. Defaults to false.
+ *
+ * <p>Permissions enforcement occurs when resulting location request is actually used, not
+ * when this method is invoked.
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) {
+ mAdasGnssBypass = adasGnssBypass;
+ return this;
+ }
+
+ /**
* If set to true, indicates that location settings, throttling, background location limits,
* and any other possible limiting factors should be ignored in order to satisfy this
* request. This is only intended for use in user initiated emergency situations, and
@@ -1171,6 +1226,7 @@ public final class LocationRequest implements Parcelable {
mMinUpdateDistanceMeters,
mMaxUpdateDelayMillis,
mHiddenFromAppOps,
+ mAdasGnssBypass,
mLocationSettingsIgnored,
mLowPower,
new WorkSource(mWorkSource));
diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java
index b72d36519e72..4f33a529a812 100644
--- a/location/java/android/location/provider/ProviderRequest.java
+++ b/location/java/android/location/provider/ProviderRequest.java
@@ -44,12 +44,19 @@ public final class ProviderRequest implements Parcelable {
public static final long INTERVAL_DISABLED = Long.MAX_VALUE;
public static final @NonNull ProviderRequest EMPTY_REQUEST = new ProviderRequest(
- INTERVAL_DISABLED, QUALITY_BALANCED_POWER_ACCURACY, 0, false, false, new WorkSource());
+ INTERVAL_DISABLED,
+ QUALITY_BALANCED_POWER_ACCURACY,
+ 0,
+ false,
+ false,
+ false,
+ new WorkSource());
private final long mIntervalMillis;
private final @Quality int mQuality;
private final long mMaxUpdateDelayMillis;
private final boolean mLowPower;
+ private final boolean mAdasGnssBypass;
private final boolean mLocationSettingsIgnored;
private final WorkSource mWorkSource;
@@ -72,12 +79,14 @@ public final class ProviderRequest implements Parcelable {
@Quality int quality,
long maxUpdateDelayMillis,
boolean lowPower,
+ boolean adasGnssBypass,
boolean locationSettingsIgnored,
@NonNull WorkSource workSource) {
mIntervalMillis = intervalMillis;
mQuality = quality;
mMaxUpdateDelayMillis = maxUpdateDelayMillis;
mLowPower = lowPower;
+ mAdasGnssBypass = adasGnssBypass;
mLocationSettingsIgnored = locationSettingsIgnored;
mWorkSource = Objects.requireNonNull(workSource);
}
@@ -126,6 +135,18 @@ public final class ProviderRequest implements Parcelable {
}
/**
+ * Returns true if this request may access GNSS even if location settings would normally deny
+ * this, in order to enable automotive safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced
+ * Driving Assistance Systems) application.
+ *
+ * @hide
+ */
+ public boolean isAdasGnssBypass() {
+ return mAdasGnssBypass;
+ }
+
+ /**
* Whether the provider should ignore all location settings, user consents, power restrictions
* or any other restricting factors and always satisfy this request to the best of their
* ability. This should only be used in case of a user initiated emergency.
@@ -135,6 +156,15 @@ public final class ProviderRequest implements Parcelable {
}
/**
+ * Returns true if any bypass flag is set on this request.
+ *
+ * @hide
+ */
+ public boolean isBypass() {
+ return mAdasGnssBypass || mLocationSettingsIgnored;
+ }
+
+ /**
* The power blame for this provider request.
*/
public @NonNull WorkSource getWorkSource() {
@@ -153,6 +183,7 @@ public final class ProviderRequest implements Parcelable {
/* quality= */ in.readInt(),
/* maxUpdateDelayMillis= */ in.readLong(),
/* lowPower= */ in.readBoolean(),
+ /* adasGnssBypass= */ in.readBoolean(),
/* locationSettingsIgnored= */ in.readBoolean(),
/* workSource= */ in.readTypedObject(WorkSource.CREATOR));
}
@@ -176,6 +207,7 @@ public final class ProviderRequest implements Parcelable {
parcel.writeInt(mQuality);
parcel.writeLong(mMaxUpdateDelayMillis);
parcel.writeBoolean(mLowPower);
+ parcel.writeBoolean(mAdasGnssBypass);
parcel.writeBoolean(mLocationSettingsIgnored);
parcel.writeTypedObject(mWorkSource, flags);
}
@@ -198,6 +230,7 @@ public final class ProviderRequest implements Parcelable {
&& mQuality == that.mQuality
&& mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis
&& mLowPower == that.mLowPower
+ && mAdasGnssBypass == that.mAdasGnssBypass
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored
&& mWorkSource.equals(that.mWorkSource);
}
@@ -229,8 +262,11 @@ public final class ProviderRequest implements Parcelable {
if (mLowPower) {
s.append(", lowPower");
}
+ if (mAdasGnssBypass) {
+ s.append(", adasGnssBypass");
+ }
if (mLocationSettingsIgnored) {
- s.append(", locationSettingsIgnored");
+ s.append(", settingsBypass");
}
if (!mWorkSource.isEmpty()) {
s.append(", ").append(mWorkSource);
@@ -246,10 +282,12 @@ public final class ProviderRequest implements Parcelable {
* A Builder for {@link ProviderRequest}s.
*/
public static final class Builder {
+
private long mIntervalMillis = INTERVAL_DISABLED;
private int mQuality = QUALITY_BALANCED_POWER_ACCURACY;
private long mMaxUpdateDelayMillis = 0;
private boolean mLowPower;
+ private boolean mAdasGnssBypass;
private boolean mLocationSettingsIgnored;
private WorkSource mWorkSource = new WorkSource();
@@ -299,6 +337,16 @@ public final class ProviderRequest implements Parcelable {
}
/**
+ * Sets whether this ADAS request should bypass GNSS settings. False by default.
+ *
+ * @hide
+ */
+ public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) {
+ this.mAdasGnssBypass = adasGnssBypass;
+ return this;
+ }
+
+ /**
* Sets whether location settings should be ignored. False by default.
*/
public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
@@ -326,6 +374,7 @@ public final class ProviderRequest implements Parcelable {
mQuality,
mMaxUpdateDelayMillis,
mLowPower,
+ mAdasGnssBypass,
mLocationSettingsIgnored,
mWorkSource);
}
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 860d88afe4a2..d8f48c2cf0c6 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -101,6 +101,8 @@ public class MtpDatabase implements AutoCloseable {
private int mBatteryLevel;
private int mBatteryScale;
private int mDeviceType;
+ private String mHostType;
+ private boolean mSkipThumbForHost = false;
private MtpServer mServer;
private MtpStorageManager mManager;
@@ -192,6 +194,7 @@ public class MtpDatabase implements AutoCloseable {
MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE,
MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL,
MtpConstants.DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE,
+ MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO,
};
@VisibleForNative
@@ -408,6 +411,8 @@ public class MtpDatabase implements AutoCloseable {
}
context.deleteDatabase(devicePropertiesName);
}
+ mHostType = "";
+ mSkipThumbForHost = false;
}
@VisibleForNative
@@ -672,12 +677,24 @@ public class MtpDatabase implements AutoCloseable {
@VisibleForNative
private int getDeviceProperty(int property, long[] outIntValue, char[] outStringValue) {
+ int length;
+ String value;
+
switch (property) {
case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
// writable string properties kept in shared preferences
- String value = mDeviceProperties.getString(Integer.toString(property), "");
- int length = value.length();
+ value = mDeviceProperties.getString(Integer.toString(property), "");
+ length = value.length();
+ if (length > 255) {
+ length = 255;
+ }
+ value.getChars(0, length, outStringValue, 0);
+ outStringValue[length] = 0;
+ return MtpConstants.RESPONSE_OK;
+ case MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO:
+ value = mHostType;
+ length = value.length();
if (length > 255) {
length = 255;
}
@@ -717,6 +734,14 @@ public class MtpDatabase implements AutoCloseable {
e.putString(Integer.toString(property), stringValue);
return (e.commit() ? MtpConstants.RESPONSE_OK
: MtpConstants.RESPONSE_GENERAL_ERROR);
+ case MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO:
+ mHostType = stringValue;
+ if (stringValue.startsWith("Android/")) {
+ Log.d(TAG, "setDeviceProperty." + Integer.toHexString(property)
+ + "=" + stringValue);
+ mSkipThumbForHost = true;
+ }
+ return MtpConstants.RESPONSE_OK;
}
return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
@@ -838,6 +863,10 @@ public class MtpDatabase implements AutoCloseable {
outLongs[0] = thumbOffsetAndSize != null ? thumbOffsetAndSize[1] : 0;
outLongs[1] = exif.getAttributeInt(ExifInterface.TAG_PIXEL_X_DIMENSION, 0);
outLongs[2] = exif.getAttributeInt(ExifInterface.TAG_PIXEL_Y_DIMENSION, 0);
+ if (mSkipThumbForHost) {
+ Log.d(TAG, "getThumbnailInfo: Skip runtime thumbnail.");
+ return true;
+ }
if (exif.getThumbnailRange() != null) {
if ((outLongs[0] == 0) || (outLongs[1] == 0) || (outLongs[2] == 0)) {
Log.d(TAG, "getThumbnailInfo: check thumb info:"
@@ -880,6 +909,10 @@ public class MtpDatabase implements AutoCloseable {
try {
ExifInterface exif = new ExifInterface(path);
+ if (mSkipThumbForHost) {
+ Log.d(TAG, "getThumbnailData: Skip runtime thumbnail.");
+ return exif.getThumbnail();
+ }
if (exif.getThumbnailRange() != null)
return exif.getThumbnail();
} catch (IOException e) {
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index e8b04edb2e1b..ec925918d4e6 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -170,6 +170,18 @@ public final class MtpDevice {
}
/**
+ * Set device property SESSION_INITIATOR_VERSION_INFO
+ *
+ * @param propertyStr string value for device property SESSION_INITIATOR_VERSION_INFO
+ * @return -1 for error, 0 for success
+ *
+ * {@hide}
+ */
+ public int setDevicePropertyInitVersion(@NonNull String propertyStr) {
+ return native_set_device_property_init_version(propertyStr);
+ }
+
+ /**
* Returns the list of IDs for all storage units on this device
* Information about each storage unit can be accessed via {@link #getStorageInfo}.
*
@@ -421,6 +433,7 @@ public final class MtpDevice {
private native boolean native_open(String deviceName, int fd);
private native void native_close();
private native MtpDeviceInfo native_get_device_info();
+ private native int native_set_device_property_init_version(String propertyStr);
private native int[] native_get_storage_ids();
private native MtpStorageInfo native_get_storage_info(int storageId);
private native int[] native_get_object_handles(int storageId, int format, int objectHandle);
diff --git a/media/java/android/mtp/MtpDeviceInfo.java b/media/java/android/mtp/MtpDeviceInfo.java
index 0304ee386ace..88514515eabf 100644
--- a/media/java/android/mtp/MtpDeviceInfo.java
+++ b/media/java/android/mtp/MtpDeviceInfo.java
@@ -31,6 +31,7 @@ public class MtpDeviceInfo {
private String mSerialNumber;
private int[] mOperationsSupported;
private int[] mEventsSupported;
+ private int[] mDevicePropertySupported;
// only instantiated via JNI
private MtpDeviceInfo() {
@@ -144,6 +145,21 @@ public class MtpDeviceInfo {
}
/**
+ * Returns Device property code supported by the device.
+ *
+ * @return supported Device property code. Can be null if device does not provide the property.
+ *
+ * @see MtpConstants#DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER
+ * @see MtpConstants#DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME
+ * @see MtpConstants#DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO
+ *
+ * {@hide}
+ */
+ public final @NonNull int[] getDevicePropertySupported() {
+ return mDevicePropertySupported;
+ }
+
+ /**
* Returns if the given operation is supported by the device or not.
* @param code Operation code.
* @return If the given operation is supported by the device or not.
@@ -162,6 +178,17 @@ public class MtpDeviceInfo {
}
/**
+ * Returns if the given Device property is supported by the device or not.
+ * @param code Device property code.
+ * @return If the given Device property is supported by the device or not.
+ *
+ * {@hide}
+ */
+ public boolean isDevicePropertySupported(int code) {
+ return isSupported(mDevicePropertySupported, code);
+ }
+
+ /**
* Returns if the code set contains code.
* @hide
*/
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index ffed4747d3ea..a77bc9fe0570 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -1131,6 +1131,7 @@ static const PropertyTableEntry kDevicePropertyTable[] = {
{ MTP_DEVICE_PROPERTY_IMAGE_SIZE, MTP_TYPE_STR },
{ MTP_DEVICE_PROPERTY_BATTERY_LEVEL, MTP_TYPE_UINT8 },
{ MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE, MTP_TYPE_UINT32 },
+ { MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO, MTP_TYPE_STR },
};
bool MtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
@@ -1289,6 +1290,7 @@ MtpProperty* MtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
switch (property) {
case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+ case MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO:
writable = true;
// fall through
FALLTHROUGH_INTENDED;
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 3d2b00fec26c..ac89fecd9150 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -72,6 +72,7 @@ static jfieldID field_deviceInfo_version;
static jfieldID field_deviceInfo_serialNumber;
static jfieldID field_deviceInfo_operationsSupported;
static jfieldID field_deviceInfo_eventsSupported;
+static jfieldID field_deviceInfo_devicePropertySupported;
// MtpStorageInfo fields
static jfieldID field_storageInfo_storageId;
@@ -129,6 +130,8 @@ static void initializeJavaIDs(JNIEnv* env) {
GetFieldIDOrDie(env, clazz_deviceInfo, "mOperationsSupported", "[I");
field_deviceInfo_eventsSupported =
GetFieldIDOrDie(env, clazz_deviceInfo, "mEventsSupported", "[I");
+ field_deviceInfo_devicePropertySupported =
+ GetFieldIDOrDie(env, clazz_deviceInfo, "mDevicePropertySupported", "[I");
clazz_storageInfo =
(jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpStorageInfo"));
@@ -377,9 +380,65 @@ android_mtp_MtpDevice_get_device_info(JNIEnv *env, jobject thiz)
}
}
+ assert(deviceInfo->mDeviceProperties);
+ {
+ const size_t size = deviceInfo->mDeviceProperties->size();
+ ScopedLocalRef<jintArray> events(env, static_cast<jintArray>(env->NewIntArray(size)));
+ {
+ ScopedIntArrayRW elements(env, events.get());
+ if (elements.get() == NULL) {
+ ALOGE("Could not create devicePropertySupported element.");
+ return NULL;
+ }
+ for (size_t i = 0; i < size; ++i) {
+ elements[i] = static_cast<int>(deviceInfo->mDeviceProperties->at(i));
+ }
+ env->SetObjectField(info, field_deviceInfo_devicePropertySupported, events.get());
+ }
+ }
+
return info;
}
+static jint
+android_mtp_MtpDevice_set_device_property_init_version(JNIEnv *env, jobject thiz,
+ jstring property_str) {
+ MtpDevice* const device = get_device_from_object(env, thiz);
+
+ if (!device) {
+ ALOGD("%s device is null\n", __func__);
+ env->ThrowNew(clazz_io_exception, "Failed to obtain MtpDevice.");
+ return -1;
+ }
+
+ const char *propertyStr = env->GetStringUTFChars(property_str, NULL);
+ if (propertyStr == NULL) {
+ return -1;
+ }
+
+ MtpProperty* property = new MtpProperty(MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO,
+ MTP_TYPE_STR, true);
+ if (!property) {
+ env->ThrowNew(clazz_io_exception, "Failed to obtain property.");
+ return -1;
+ }
+
+ if (property->getDataType() != MTP_TYPE_STR) {
+ env->ThrowNew(clazz_io_exception, "Unexpected property data type.");
+ return -1;
+ }
+
+ property->setCurrentValue(propertyStr);
+ if (!device->setDevicePropValueStr(property)) {
+ env->ThrowNew(clazz_io_exception, "Failed to obtain property value.");
+ return -1;
+ }
+
+ env->ReleaseStringUTFChars(property_str, propertyStr);
+
+ return 0;
+}
+
static jintArray
android_mtp_MtpDevice_get_storage_ids(JNIEnv *env, jobject thiz)
{
@@ -847,6 +906,8 @@ static const JNINativeMethod gMethods[] = {
{"native_close", "()V", (void *)android_mtp_MtpDevice_close},
{"native_get_device_info", "()Landroid/mtp/MtpDeviceInfo;",
(void *)android_mtp_MtpDevice_get_device_info},
+ {"native_set_device_property_init_version", "(Ljava/lang/String;)I",
+ (void *)android_mtp_MtpDevice_set_device_property_init_version},
{"native_get_storage_ids", "()[I", (void *)android_mtp_MtpDevice_get_storage_ids},
{"native_get_storage_info", "(I)Landroid/mtp/MtpStorageInfo;",
(void *)android_mtp_MtpDevice_get_storage_info},
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index 9610c9443184..46f1e030af23 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -25,6 +25,7 @@
<style name="Switch.SettingsLib" parent="@android:style/Widget.Material.CompoundButton.Switch">
<item name="android:switchMinWidth">52dp</item>
+ <item name="android:minHeight">@dimen/settingslib_preferred_minimum_touch_target</item>
<item name="android:track">@drawable/settingslib_switch_track</item>
<item name="android:thumb">@drawable/settingslib_switch_thumb</item>
</style>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 8c092ae37222..604310a9e905 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -454,6 +454,14 @@
android:finishOnCloseSystemDialogs="true">
</activity>
+ <!-- started from SensoryPrivacyService -->
+ <activity android:name=".sensorprivacy.television.TvUnblockSensorActivity"
+ android:exported="true"
+ android:permission="android.permission.MANAGE_SENSOR_PRIVACY"
+ android:theme="@style/BottomSheet"
+ android:finishOnCloseSystemDialogs="true">
+ </activity>
+
<!-- started from UsbDeviceSettingsManager -->
<activity android:name=".usb.UsbAccessoryUriActivity"
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml
new file mode 100644
index 000000000000..fc3b4ed6eecb
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml
@@ -0,0 +1,52 @@
+<?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.
+ -->
+
+<selector
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true">
+ <set>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleX"
+ android:valueFrom="1.0"
+ android:valueTo="@dimen/bottom_sheet_button_selection_scaled"
+ android:valueType="floatType"/>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleY"
+ android:valueFrom="1.0"
+ android:valueTo="@dimen/bottom_sheet_button_selection_scaled"
+ android:valueType="floatType"/>
+ </set>
+ </item>
+ <item android:state_focused="false">
+ <set>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleX"
+ android:valueFrom="@dimen/bottom_sheet_button_selection_scaled"
+ android:valueTo="1.0"
+ android:valueType="floatType"/>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleY"
+ android:valueFrom="@dimen/bottom_sheet_button_selection_scaled"
+ android:valueTo="1.0"
+ android:valueType="floatType"/>
+ </set>
+ </item>
+</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml
new file mode 100644
index 000000000000..cace36d68b43
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_bottom_sheet_enter.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.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/decelerate_quint">
+ <translate android:fromYDelta="100%"
+ android:toYDelta="0"
+ android:duration="900"/>
+</set> \ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml
new file mode 100644
index 000000000000..f7efe7cd2584
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_bottom_sheet_exit.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.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/decelerate_quint">
+ <translate android:fromYDelta="0"
+ android:toYDelta="100%"
+ android:duration="500"/>
+</set> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml b/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
new file mode 100644
index 000000000000..9b0bae09b326
--- /dev/null
+++ b/packages/SystemUI/res/color/bottom_sheet_button_background_color.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.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"
+ android:color="@color/bottom_sheet_button_background_color_focused"/>
+ <item android:color="@color/bottom_sheet_button_background_color_unfocused"/>
+</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml b/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml
new file mode 100644
index 000000000000..05248f17097b
--- /dev/null
+++ b/packages/SystemUI/res/color/bottom_sheet_button_text_color.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.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"
+ android:color="@color/bottom_sheet_button_text_color_focused"/>
+ <item android:color="@color/bottom_sheet_button_text_color_unfocused"/>
+</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bottom_sheet_background.xml b/packages/SystemUI/res/drawable/bottom_sheet_background.xml
new file mode 100644
index 000000000000..87850a0d00ec
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bottom_sheet_background.xml
@@ -0,0 +1,20 @@
+<?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" android:shape="rectangle">
+ <solid android:color="@color/bottom_sheet_background_color"/>
+ <corners android:radius="@dimen/bottom_sheet_corner_radius"/>
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml b/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml
new file mode 100644
index 000000000000..cd2aa9c751d9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml
@@ -0,0 +1,20 @@
+<?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" android:shape="rectangle">
+ <solid android:color="@color/bottom_sheet_background_color_with_blur"/>
+ <corners android:radius="@dimen/bottom_sheet_corner_radius"/>
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml b/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml
new file mode 100644
index 000000000000..585a6bc771f7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml
@@ -0,0 +1,20 @@
+<?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" android:shape="rectangle">
+ <solid android:color="@color/bottom_sheet_button_background_color"/>
+ <corners android:radius="@dimen/bottom_sheet_button_corner_radius"/>
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/tv_bottom_sheet.xml b/packages/SystemUI/res/layout/tv_bottom_sheet.xml
new file mode 100644
index 000000000000..b69cdc76ae40
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_bottom_sheet.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/bottom_sheet"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ android:minHeight="@dimen/bottom_sheet_min_height"
+ android:paddingHorizontal="@dimen/bottom_sheet_padding_horizontal"
+ android:paddingVertical="@dimen/bottom_sheet_padding_vertical">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="80dp"
+ android:gravity="center"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/bottom_sheet_icon"
+ android:layout_width="@dimen/bottom_sheet_icon_size"
+ android:layout_height="@dimen/bottom_sheet_icon_size"
+ android:layout_gravity="center_vertical"
+ android:tint="@color/bottom_sheet_icon_color"/>
+ <ImageView
+ android:id="@+id/bottom_sheet_second_icon"
+ android:layout_width="@dimen/bottom_sheet_icon_size"
+ android:layout_height="@dimen/bottom_sheet_icon_size"
+ android:layout_marginStart="@dimen/bottom_sheet_icon_margin"
+ android:layout_gravity="center_vertical"
+ android:tint="@color/bottom_sheet_icon_color"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/bottom_sheet_padding_horizontal"
+ android:layout_weight="1"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/bottom_sheet_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/bottom_sheet_title_margin_bottom"
+ android:textAppearance="@style/BottomSheet.TitleText"/>
+
+ <TextView
+ android:id="@+id/bottom_sheet_body"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/bottom_sheet_details_margin_bottom"
+ android:textAppearance="@style/BottomSheet.BodyText" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="@dimen/bottom_sheet_actions_width"
+ android:layout_height="match_parent"
+ android:gravity="center">
+ <Button
+ android:id="@+id/bottom_sheet_positive_button"
+ style="@style/BottomSheet.ActionItem" />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="@dimen/bottom_sheet_actions_spacing" />
+ <Button
+ android:id="@+id/bottom_sheet_negative_button"
+ style="@style/BottomSheet.ActionItem" />
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-television/colors.xml b/packages/SystemUI/res/values-television/colors.xml
index e5f3b4768fe2..e13c42e9ca3d 100644
--- a/packages/SystemUI/res/values-television/colors.xml
+++ b/packages/SystemUI/res/values-television/colors.xml
@@ -19,4 +19,19 @@
<resources>
<color name="volume_dialog_background_color">#E61F232B</color>
<color name="volume_dialog_background_color_above_blur">#C71F232B</color>
+
+ <color name="bottom_sheet_icon_color">#D2E3FC</color>
+
+ <color name="bottom_sheet_title_color">#E8F0FE</color>
+ <color name="bottom_sheet_body_color">#D2E3FC</color>
+
+ <color name="bottom_sheet_background_color">#1F232C</color>
+ <color name="bottom_sheet_background_color_with_blur">#AA1A2734</color>
+
+ <color name="bottom_sheet_button_background_color_focused">#E8F0FE</color>
+ <color name="bottom_sheet_button_background_color_unfocused">#0FE8EAED</color>
+
+ <color name="bottom_sheet_button_text_color_focused">#DB202124</color>
+ <color name="bottom_sheet_button_text_color_unfocused">#B5E8EAED</color>
+
</resources>
diff --git a/packages/SystemUI/res/values-television/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml
index 7626db93dd76..3a1a3d923fcc 100644
--- a/packages/SystemUI/res/values-television/dimens.xml
+++ b/packages/SystemUI/res/values-television/dimens.xml
@@ -21,4 +21,27 @@
<dimen name="privacy_chip_icon_margin">3dp</dimen>
<dimen name="privacy_chip_icon_padding">8dp</dimen>
<dimen name="privacy_chip_icon_size">13dp</dimen>
+
+ <dimen name="bottom_sheet_padding_horizontal">32dp</dimen>
+ <dimen name="bottom_sheet_padding_vertical">24dp</dimen>
+
+ <dimen name="bottom_sheet_icon_size">42dp</dimen>
+ <dimen name="bottom_sheet_icon_margin">8dp</dimen>
+ <dimen name="bottom_sheet_title_margin_bottom">18dp</dimen>
+ <dimen name="bottom_sheet_details_margin_bottom">8dp</dimen>
+
+ <dimen name="bottom_sheet_actions_width">296dp</dimen>
+ <dimen name="bottom_sheet_actions_spacing">12dp</dimen>
+ <item name="bottom_sheet_button_selection_scaled" format="float" type="dimen">1.1</item>
+ <dimen name="bottom_sheet_button_width">232dp</dimen>
+ <dimen name="bottom_sheet_button_padding_horizontal">20dp</dimen>
+ <dimen name="bottom_sheet_button_padding_vertical">16dp</dimen>
+
+ <dimen name="bottom_sheet_corner_radius">24dp</dimen>
+ <dimen name="bottom_sheet_button_corner_radius">10dp</dimen>
+
+ <dimen name="bottom_sheet_min_height">208dp</dimen>
+ <dimen name="bottom_sheet_margin">24dp</dimen>
+ <dimen name="bottom_sheet_background_blur_radius">120dp</dimen>
+
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-television/styles.xml b/packages/SystemUI/res/values-television/styles.xml
index 00217fb39b93..0fb789863bd9 100644
--- a/packages/SystemUI/res/values-television/styles.xml
+++ b/packages/SystemUI/res/values-television/styles.xml
@@ -28,4 +28,34 @@
<item name="android:textColorPrimaryInverse">@color/tv_volume_dialog_accent</item>
<item name="android:dialogCornerRadius">@dimen/volume_dialog_panel_width_half</item>
</style>
+
+ <style name="BottomSheet" parent="Theme.Leanback">
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:windowActivityTransitions">true</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:backgroundDimAmount">0.2</item>
+ </style>
+
+ <style name="BottomSheet.TitleText">
+ <item name="android:textSize">28sp</item>
+ <item name="android:textColor">@color/bottom_sheet_title_color</item>
+ </style>
+
+ <style name="BottomSheet.BodyText">
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">@color/bottom_sheet_body_color</item>
+ </style>
+
+ <style name="BottomSheet.ActionItem">
+ <item name="android:layout_width">@dimen/bottom_sheet_button_width</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">left|center_vertical</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">@color/bottom_sheet_button_text_color</item>
+ <item name="android:background">@drawable/bottom_sheet_button_background</item>
+ <item name="android:paddingHorizontal">@dimen/bottom_sheet_button_padding_horizontal</item>
+ <item name="android:paddingVertical">@dimen/bottom_sheet_button_padding_vertical</item>
+ <item name="android:stateListAnimator">@anim/tv_bottom_sheet_button_state_list_animator</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index ec930b0c41d1..11412f41f578 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -67,6 +67,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -111,6 +112,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
+ @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Nullable private final UdfpsHbmProvider mHbmProvider;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@@ -507,6 +509,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull FalsingManager falsingManager,
@NonNull PowerManager powerManager,
@NonNull AccessibilityManager accessibilityManager,
+ @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController,
@NonNull ScreenLifecycle screenLifecycle,
@Nullable Vibrator vibrator,
@NonNull Optional<UdfpsHbmProvider> hbmProvider) {
@@ -529,6 +532,7 @@ public class UdfpsController implements DozeReceiver {
mFalsingManager = falsingManager;
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
+ mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mHbmProvider = hbmProvider.orElse(null);
screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
@@ -716,6 +720,7 @@ public class UdfpsController implements DozeReceiver {
mFgExecutor,
mDumpManager,
mKeyguardViewMediator,
+ mLockscreenShadeTransitionController,
this
);
case IUdfpsOverlayController.REASON_AUTH_BP:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 00888dfe48d3..35ca470df523 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -31,6 +31,7 @@ import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -54,6 +55,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final DelayableExecutor mExecutor;
@NonNull private final KeyguardViewMediator mKeyguardViewMediator;
+ @NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
@NonNull private final UdfpsController mUdfpsController;
@Nullable private Runnable mCancelDelayedHintRunnable;
@@ -63,6 +65,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
private boolean mFaceDetectRunning;
private boolean mHintShown;
private int mStatusBarState;
+ private float mTransitionToFullShadeProgress;
/**
* hidden amount of pin/pattern/password bouncer
@@ -81,12 +84,14 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
@NonNull DelayableExecutor mainDelayableExecutor,
@NonNull DumpManager dumpManager,
@NonNull KeyguardViewMediator keyguardViewMediator,
+ @NonNull LockscreenShadeTransitionController transitionController,
@NonNull UdfpsController udfpsController) {
super(view, statusBarStateController, statusBar, dumpManager);
mKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mExecutor = mainDelayableExecutor;
mKeyguardViewMediator = keyguardViewMediator;
+ mLockScreenShadeTransitionController = transitionController;
mUdfpsController = udfpsController;
}
@@ -116,6 +121,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
updatePauseAuth();
mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this);
}
@Override
@@ -127,6 +133,9 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
mStatusBarStateController.removeCallback(mStateListener);
mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor);
mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
+ if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
+ mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
+ }
if (mCancelDelayedHintRunnable != null) {
mCancelDelayedHintRunnable.run();
@@ -256,11 +265,21 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
}
}
+ /**
+ * Set the progress we're currently transitioning to the full shade. 0.0f means we're not
+ * transitioning yet, while 1.0f means we've fully dragged down.
+ */
+ public void setTransitionToFullShadeProgress(float progress) {
+ mTransitionToFullShadeProgress = progress;
+ updateAlpha();
+ }
+
private void updateAlpha() {
// fade icon on transition to showing bouncer
int alpha = mShowingUdfpsBouncer ? 255
: Math.abs((int) MathUtils.constrainedMap(0f, 255f, .4f, .7f,
mInputBouncerHiddenAmount));
+ alpha *= (1.0f - mTransitionToFullShadeProgress);
mView.setUnpausedAlpha(alpha);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 2dbf30fdd289..de8ed7013ab2 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -25,6 +25,7 @@ import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.screenrecord.ScreenRecordDialog;
import com.android.systemui.screenshot.LongScreenshotActivity;
import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
+import com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity;
import com.android.systemui.settings.brightness.BrightnessDialog;
import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
import com.android.systemui.tuner.TunerActivity;
@@ -120,4 +121,10 @@ public abstract class DefaultActivityBinder {
@IntoMap
@ClassKey(SensorUseStartedActivity.class)
public abstract Activity bindSensorUseStartedActivity(SensorUseStartedActivity activity);
+
+ /** Inject into TvUnblockSensorActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(TvUnblockSensorActivity.class)
+ public abstract Activity bindTvUnblockSensorActivity(TvUnblockSensorActivity activity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 746621dfda27..d85c9a718871 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -231,7 +231,8 @@ public class DependencyProvider {
@Main Handler mainHandler,
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ UserTracker userTracker) {
return new NavigationBarController(context,
windowManager,
assistManagerLazy,
@@ -256,7 +257,8 @@ public class DependencyProvider {
mainHandler,
uiEventLogger,
navBarOverlayController,
- configurationController);
+ configurationController,
+ userTracker);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 053d75d96720..954ba797e164 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -63,6 +63,7 @@ import android.service.dreams.IDreamManager;
import android.telecom.TelecomManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.view.CrossWindowBlurListeners;
import android.view.IWindowManager;
import android.view.ViewConfiguration;
import android.view.WindowManager;
@@ -139,6 +140,12 @@ public class FrameworkServicesModule {
}
@Provides
+ @Singleton
+ static CrossWindowBlurListeners provideCrossWindowBlurListeners() {
+ return CrossWindowBlurListeners.getInstance();
+ }
+
+ @Provides
@DisplayId
static int provideDisplayId(Context context) {
return context.getDisplayId();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index da09793580bb..c6d7e7c46abb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -129,6 +129,7 @@ import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.AutoHideUiElement;
@@ -199,6 +200,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
+ private final UserTracker mUserTracker;
private Bundle mSavedState;
private NavigationBarView mNavigationBarView;
@@ -459,7 +461,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
SystemActions systemActions,
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ UserTracker userTracker) {
mContext = context;
mWindowManager = windowManager;
mAccessibilityManager = accessibilityManager;
@@ -484,6 +487,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
+ mUserTracker = userTracker;
mNavBarMode = mNavigationModeController.addListener(this);
mAccessibilityButtonModeObserver.addListener(this);
@@ -1450,12 +1454,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
.getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
boolean longPressDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
- mLongPressHomeEnabled = Settings.Secure.getInt(mContentResolver,
- Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0) != 0;
+ mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
boolean gestureDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
- mAssistantTouchGestureEnabled = Settings.Secure.getInt(mContentResolver,
- Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0) != 0;
+ mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
if (mOverviewProxyService.getProxy() != null) {
try {
mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 8b5a537ba242..53592101c3ea 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -57,6 +57,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -116,6 +117,7 @@ public class NavigationBarController implements Callbacks,
private final TaskbarDelegate mTaskbarDelegate;
private int mNavMode;
private boolean mIsTablet;
+ private final UserTracker mUserTracker;
/** A displayId - nav bar maps. */
@VisibleForTesting
@@ -151,7 +153,8 @@ public class NavigationBarController implements Callbacks,
@Main Handler mainHandler,
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ UserTracker userTracker) {
mContext = context;
mWindowManager = windowManager;
mAssistManagerLazy = assistManagerLazy;
@@ -184,6 +187,7 @@ public class NavigationBarController implements Callbacks,
mNavigationModeController.addListener(this);
mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService);
mIsTablet = isTablet(mContext.getResources().getConfiguration());
+ mUserTracker = userTracker;
}
@Override
@@ -361,7 +365,8 @@ public class NavigationBarController implements Callbacks,
mSystemActions,
mHandler,
mNavBarOverlayController,
- mUiEventLogger);
+ mUiEventLogger,
+ mUserTracker);
mNavigationBars.put(displayId, navBar);
View navigationBarView = navBar.createView(savedState);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index edfbed04f70d..6660081006cd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -297,7 +297,7 @@ public class QSContainerImpl extends FrameLayout {
// start margin of next page).
qsPanelController.setPageMargin(mSideMargins);
} else if (view == mHeader) {
- // No content padding for the header.
+ quickStatusBarHeaderController.setContentMargins(mContentPadding, mContentPadding);
} else {
view.setPaddingRelative(
mContentPadding,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 2d0d87dcadee..bcce87a51097 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -46,6 +46,7 @@ import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
public class QSDetail extends LinearLayout {
@@ -83,6 +84,8 @@ public class QSDetail extends LinearLayout {
private boolean mSwitchState;
private QSFooter mFooter;
+ private NotificationsQuickSettingsContainer mContainer;
+
public QSDetail(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@@ -115,6 +118,10 @@ public class QSDetail extends LinearLayout {
mClipper = new QSDetailClipper(this);
}
+ public void setContainer(NotificationsQuickSettingsContainer container) {
+ mContainer = container;
+ }
+
/** */
public void setQsPanel(QSPanelController panelController, QuickStatusBarHeader header,
QSFooter footer) {
@@ -242,6 +249,9 @@ public class QSDetail extends LinearLayout {
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
animateDetailVisibleDiff(x, y, visibleDiff, listener);
+ if (mContainer != null) {
+ mContainer.setDetailShowing(showingDetail);
+ }
}
protected void animateDetailVisibleDiff(int x, int y, boolean visibleDiff, AnimatorListener listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 7b8a6a0a8d0e..c28c649b0306 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -293,6 +293,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
public void setContainer(ViewGroup container) {
if (container instanceof NotificationsQuickSettingsContainer) {
mQSCustomizerController.setContainer((NotificationsQuickSettingsContainer) container);
+ mQSDetail.setContainer((NotificationsQuickSettingsContainer) container);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0bb0a3f7bad4..c70eaffcaeb6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -425,7 +425,7 @@ public class QSPanel extends LinearLayout implements Tunable {
LinearLayout.LayoutParams layoutParams = (LayoutParams) hostView.getLayoutParams();
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.width = horizontal ? 0 : ViewGroup.LayoutParams.MATCH_PARENT;
- layoutParams.weight = horizontal ? 1.2f : 0;
+ layoutParams.weight = horizontal ? 1f : 0;
// Add any bottom margin, such that the total spacing is correct. This is only
// necessary if the view isn't horizontal, since otherwise the padding is
// carried in the parent of this view (to ensure correct vertical alignment)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index cbdcad5cf385..76076f6c2761 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -177,7 +177,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
mView.onAttach(mIconManager, mQSExpansionPathInterpolator);
mDemoModeController.addCallback(mDemoModeReceiver);
- mHeaderQsPanelController.setContentMargins(0, 0);
}
@Override
@@ -253,6 +252,10 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
}
+ public void setContentMargins(int marginStart, int marginEnd) {
+ mHeaderQsPanelController.setContentMargins(marginStart, marginEnd);
+ }
+
private static class ClockDemoModeReceiver implements DemoMode {
private Clock mClockView;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
index 730702ec8685..51cc32ad39c1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
@@ -20,6 +20,7 @@ import android.graphics.Bitmap;
import android.graphics.HardwareRenderer;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
+import android.graphics.Region;
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -46,7 +47,6 @@ class ImageTileSet {
private static final String TAG = "ImageTileSet";
- private CallbackRegistry<OnBoundsChangedListener, ImageTileSet, Rect> mOnBoundsListeners;
private CallbackRegistry<OnContentChangedListener, ImageTileSet, Rect> mContentListeners;
@Inject
@@ -54,14 +54,6 @@ class ImageTileSet {
mHandler = handler;
}
- interface OnBoundsChangedListener {
- /**
- * Reports an update to the bounding box that contains all active tiles. These are virtual
- * (capture) coordinates which can be either negative or positive.
- */
- void onBoundsChanged(int left, int top, int right, int bottom);
- }
-
interface OnContentChangedListener {
/**
* Mark as dirty and rebuild display list.
@@ -70,25 +62,9 @@ class ImageTileSet {
}
private final List<ImageTile> mTiles = new ArrayList<>();
- private final Rect mBounds = new Rect();
+ private final Region mRegion = new Region();
private final Handler mHandler;
- void addOnBoundsChangedListener(OnBoundsChangedListener listener) {
- if (mOnBoundsListeners == null) {
- mOnBoundsListeners = new CallbackRegistry<>(
- new NotifierCallback<OnBoundsChangedListener, ImageTileSet, Rect>() {
- @Override
- public void onNotifyCallback(OnBoundsChangedListener callback,
- ImageTileSet sender,
- int arg, Rect newBounds) {
- callback.onBoundsChanged(newBounds.left, newBounds.top, newBounds.right,
- newBounds.bottom);
- }
- });
- }
- mOnBoundsListeners.add(listener);
- }
-
void addOnContentChangedListener(OnContentChangedListener listener) {
if (mContentListeners == null) {
mContentListeners = new CallbackRegistry<>(
@@ -110,14 +86,8 @@ class ImageTileSet {
mHandler.post(() -> addTile(tile));
return;
}
- final Rect newBounds = new Rect(mBounds);
- final Rect newRect = tile.getLocation();
mTiles.add(tile);
- newBounds.union(newRect);
- if (!newBounds.equals(mBounds)) {
- mBounds.set(newBounds);
- notifyBoundsChanged(mBounds);
- }
+ mRegion.op(tile.getLocation(), mRegion, Region.Op.UNION);
notifyContentChanged();
}
@@ -127,12 +97,6 @@ class ImageTileSet {
}
}
- private void notifyBoundsChanged(Rect bounds) {
- if (mOnBoundsListeners != null) {
- mOnBoundsListeners.notifyCallbacks(this, 0, bounds);
- }
- }
-
/**
* Returns a drawable to paint the combined contents of the tiles. Drawable dimensions are
* zero-based and map directly to {@link #getLeft()}, {@link #getTop()}, {@link #getRight()},
@@ -153,6 +117,15 @@ class ImageTileSet {
return mTiles.size();
}
+ /**
+ * @return the bounding rect around any gaps in the tiles.
+ */
+ Rect getGaps() {
+ Region difference = new Region();
+ difference.op(mRegion.getBounds(), mRegion, Region.Op.DIFFERENCE);
+ return difference.getBounds();
+ }
+
ImageTile get(int i) {
return mTiles.get(i);
}
@@ -182,41 +155,40 @@ class ImageTileSet {
}
int getLeft() {
- return mBounds.left;
+ return mRegion.getBounds().left;
}
int getTop() {
- return mBounds.top;
+ return mRegion.getBounds().top;
}
int getRight() {
- return mBounds.right;
+ return mRegion.getBounds().right;
}
int getBottom() {
- return mBounds.bottom;
+ return mRegion.getBounds().bottom;
}
int getWidth() {
- return mBounds.width();
+ return mRegion.getBounds().width();
}
int getHeight() {
- return mBounds.height();
+ return mRegion.getBounds().height();
}
void clear() {
if (mTiles.isEmpty()) {
return;
}
- mBounds.setEmpty();
+ mRegion.setEmpty();
Iterator<ImageTile> i = mTiles.iterator();
while (i.hasNext()) {
ImageTile next = i.next();
next.close();
i.remove();
}
- notifyBoundsChanged(mBounds);
notifyContentChanged();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index 94e314948779..ce6e46937c25 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -56,6 +56,7 @@ import javax.inject.Inject;
public class ScrollCaptureClient {
private static final int TILE_SIZE_PX_MAX = 4 * (1024 * 1024);
private static final int TILES_PER_PAGE = 2; // increase once b/174571735 is addressed
+ private static final int MAX_TILES = 30;
@VisibleForTesting
static final int MATCH_ANY_TASK = ActivityTaskManager.INVALID_TASK_ID;
@@ -83,11 +84,12 @@ public class ScrollCaptureClient {
int getMaxTiles();
/**
- * @return the maximum combined capture height for this session, in pixels.
+ * Target pixel height for acquisition this session. Session may yield more or less data
+ * than this, but acquiring this height is considered sufficient for completion.
+ *
+ * @return target height in pixels.
*/
- default int getMaxHeight() {
- return getMaxTiles() * getTileHeight();
- }
+ int getTargetHeight();
/**
* @return the height of each image tile
@@ -234,11 +236,11 @@ public class ScrollCaptureClient {
private final int mTileWidth;
private Rect mRequestRect;
private boolean mStarted;
+ private final int mTargetHeight;
private ICancellationSignal mCancellationSignal;
private final Rect mWindowBounds;
private final Rect mBoundsInWindow;
- private final int mMaxTiles;
private Completer<Session> mStartCompleter;
private Completer<CaptureResult> mTileRequestCompleter;
@@ -256,7 +258,7 @@ public class ScrollCaptureClient {
mTileWidth = mBoundsInWindow.width();
mTileHeight = pxPerTile / mBoundsInWindow.width();
- mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE);
+ mTargetHeight = (int) (mBoundsInWindow.height() * maxPages);
if (DEBUG_SCROLL) {
Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
@@ -285,7 +287,7 @@ public class ScrollCaptureClient {
private void start(Completer<Session> completer) {
mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
- mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
+ MAX_TILES, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
mStartCompleter = completer;
try {
mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this);
@@ -410,8 +412,13 @@ public class ScrollCaptureClient {
}
@Override
+ public int getTargetHeight() {
+ return mTargetHeight;
+ }
+
+ @Override
public int getMaxTiles() {
- return mMaxTiles;
+ return MAX_TILES;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index bbcfdbd99bef..4c1f6a19b96c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -199,22 +199,20 @@ public class ScrollCaptureController {
Log.d(TAG, "onCaptureResult: " + result + " scrolling " + (mScrollingUp ? "UP" : "DOWN")
+ " finish on boundary: " + mFinishOnBoundary);
boolean emptyResult = result.captured.height() == 0;
- boolean partialResult = !emptyResult
- && result.captured.height() < result.requested.height();
- boolean finish = false;
if (emptyResult) {
// Potentially reached a vertical boundary. Extend in the other direction.
if (mFinishOnBoundary) {
- Log.d(TAG, "Partial/empty: finished!");
- finish = true;
+ Log.d(TAG, "Empty: finished!");
+ finishCapture();
+ return;
} else {
// We hit a boundary, clear the tiles, capture everything in the opposite direction,
// then finish.
mImageTileSet.clear();
mFinishOnBoundary = true;
mScrollingUp = !mScrollingUp;
- Log.d(TAG, "Partial/empty: cleared, switch direction to finish");
+ Log.d(TAG, "Empty: cleared, switch direction to finish");
}
} else {
// Got a non-empty result, but may already have enough bitmap data now
@@ -223,12 +221,14 @@ public class ScrollCaptureController {
Log.d(TAG, "Hit max tiles: finished");
// If we ever hit the max tiles, we've got enough bitmap data to finish
// (even if we weren't sure we'd finish on this pass).
- finish = true;
+ finishCapture();
+ return;
} else {
if (mScrollingUp && !mFinishOnBoundary) {
// During the initial scroll up, we only want to acquire the portion described
// by IDEAL_PORTION_ABOVE.
- if (expectedTiles >= mSession.getMaxTiles() * IDEAL_PORTION_ABOVE) {
+ if (mImageTileSet.getHeight() + result.captured.height()
+ >= mSession.getTargetHeight() * IDEAL_PORTION_ABOVE) {
Log.d(TAG, "Hit ideal portion above: clear and switch direction");
// We got enough above the start point, now see how far down it can go.
mImageTileSet.clear();
@@ -246,15 +246,15 @@ public class ScrollCaptureController {
+ " - " + mImageTileSet.getRight() + "," + mImageTileSet.getBottom()
+ " (" + mImageTileSet.getWidth() + "x" + mImageTileSet.getHeight() + ")");
-
- // Stop when "too tall"
- if (mImageTileSet.getHeight() > MAX_HEIGHT) {
- Log.d(TAG, "Max height reached.");
- finish = true;
+ Rect gapBounds = mImageTileSet.getGaps();
+ if (!gapBounds.isEmpty()) {
+ Log.d(TAG, "Found gaps in tileset: " + gapBounds + ", requesting " + gapBounds.top);
+ requestNextTile(gapBounds.top);
+ return;
}
- if (finish) {
- Log.d(TAG, "Stop.");
+ if (mImageTileSet.getHeight() >= mSession.getTargetHeight()) {
+ Log.d(TAG, "Target height reached.");
finishCapture();
return;
}
@@ -268,8 +268,8 @@ public class ScrollCaptureController {
: result.requested.bottom;
} else {
nextTop = (mScrollingUp)
- ? result.captured.top - mSession.getTileHeight()
- : result.captured.bottom;
+ ? mImageTileSet.getTop() - mSession.getTileHeight()
+ : mImageTileSet.getBottom();
}
requestNextTile(nextTop);
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 06c1c6f8cefa..e6d48676dc03 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -218,7 +218,12 @@ class SensorUseStartedActivity @Inject constructor(
}
private fun disableSensorPrivacy() {
- sensorPrivacyController.setSensorBlocked(sensor, false)
+ if (sensor == ALL_SENSORS) {
+ sensorPrivacyController.setSensorBlocked(MICROPHONE, false)
+ sensorPrivacyController.setSensorBlocked(CAMERA, false)
+ } else {
+ sensorPrivacyController.setSensorBlocked(sensor, false)
+ }
unsuppressImmediately = true
setResult(RESULT_OK)
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
new file mode 100644
index 000000000000..9d101effa99f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.sensorprivacy.television;
+
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
+
+import android.hardware.SensorPrivacyManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.tv.TvBottomSheetActivity;
+
+import javax.inject.Inject;
+
+/**
+ * Bottom sheet that is shown when the camera/mic sensors are blocked by the global toggle and
+ * allows the user to re-enable them.
+ */
+public class TvUnblockSensorActivity extends TvBottomSheetActivity {
+
+ private static final String TAG = TvUnblockSensorActivity.class.getSimpleName();
+
+ private static final int ALL_SENSORS = Integer.MAX_VALUE;
+ private int mSensor = -1;
+
+ private final IndividualSensorPrivacyController mSensorPrivacyController;
+ private IndividualSensorPrivacyController.Callback mSensorPrivacyCallback;
+
+ @Inject
+ public TvUnblockSensorActivity(
+ IndividualSensorPrivacyController individualSensorPrivacyController) {
+ mSensorPrivacyController = individualSensorPrivacyController;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ boolean allSensors = getIntent().getBooleanExtra(SensorPrivacyManager.EXTRA_ALL_SENSORS,
+ false);
+ if (allSensors) {
+ mSensor = ALL_SENSORS;
+ } else {
+ mSensor = getIntent().getIntExtra(SensorPrivacyManager.EXTRA_SENSOR, -1);
+ }
+
+ if (mSensor == -1) {
+ Log.v(TAG, "Invalid extras");
+ finish();
+ return;
+ }
+
+ mSensorPrivacyCallback = (sensor, blocked) -> {
+ if (mSensor == ALL_SENSORS) {
+ if (!mSensorPrivacyController.isSensorBlocked(CAMERA)
+ && !mSensorPrivacyController.isSensorBlocked(MICROPHONE)) {
+ finish();
+ }
+ } else if (this.mSensor == sensor && !blocked) {
+ finish();
+ }
+ };
+
+ initUI();
+ }
+
+ private void initUI() {
+ TextView title = findViewById(R.id.bottom_sheet_title);
+ TextView content = findViewById(R.id.bottom_sheet_body);
+ ImageView icon = findViewById(R.id.bottom_sheet_icon);
+ // mic icon if both icons are shown
+ ImageView secondIcon = findViewById(R.id.bottom_sheet_second_icon);
+ Button unblockButton = findViewById(R.id.bottom_sheet_positive_button);
+ Button cancelButton = findViewById(R.id.bottom_sheet_negative_button);
+
+ switch (mSensor) {
+ case MICROPHONE:
+ title.setText(R.string.sensor_privacy_start_use_mic_dialog_title);
+ content.setText(R.string.sensor_privacy_start_use_mic_dialog_content);
+ icon.setImageResource(com.android.internal.R.drawable.perm_group_microphone);
+ secondIcon.setVisibility(View.GONE);
+ break;
+ case CAMERA:
+ title.setText(R.string.sensor_privacy_start_use_camera_dialog_title);
+ content.setText(R.string.sensor_privacy_start_use_camera_dialog_content);
+ icon.setImageResource(com.android.internal.R.drawable.perm_group_camera);
+ secondIcon.setVisibility(View.GONE);
+ break;
+ case ALL_SENSORS:
+ default:
+ title.setText(R.string.sensor_privacy_start_use_mic_camera_dialog_title);
+ content.setText(R.string.sensor_privacy_start_use_mic_camera_dialog_content);
+ icon.setImageResource(com.android.internal.R.drawable.perm_group_camera);
+ secondIcon.setImageResource(com.android.internal.R.drawable.perm_group_microphone);
+ break;
+ }
+ unblockButton.setText(
+ com.android.internal.R.string.sensor_privacy_start_use_dialog_turn_on_button);
+ unblockButton.setOnClickListener(v -> {
+ if (mSensor == ALL_SENSORS) {
+ mSensorPrivacyController.setSensorBlocked(CAMERA, false);
+ mSensorPrivacyController.setSensorBlocked(MICROPHONE, false);
+ } else {
+ mSensorPrivacyController.setSensorBlocked(mSensor, false);
+ }
+ });
+
+ cancelButton.setText(android.R.string.cancel);
+ cancelButton.setOnClickListener(v -> finish());
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mSensorPrivacyController.addCallback(mSensorPrivacyCallback);
+ }
+
+ @Override
+ public void onPause() {
+ mSensorPrivacyController.removeCallback(mSensorPrivacyCallback);
+ super.onPause();
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index ce796d9789cf..89dda9c52651 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -22,6 +22,7 @@ import android.app.ActivityManager
import android.content.res.Resources
import android.util.IndentingPrintWriter
import android.util.MathUtils
+import android.view.CrossWindowBlurListeners
import android.view.SurfaceControl
import android.view.ViewRootImpl
import androidx.annotation.VisibleForTesting
@@ -37,6 +38,7 @@ import javax.inject.Inject
@SysUISingleton
open class BlurUtils @Inject constructor(
@Main private val resources: Resources,
+ private val crossWindowBlurListeners: CrossWindowBlurListeners,
dumpManager: DumpManager
) : Dumpable {
val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
@@ -97,7 +99,8 @@ open class BlurUtils @Inject constructor(
* @return {@code true} when supported.
*/
open fun supportsBlursOnWindows(): Boolean {
- return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx()
+ return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx() &&
+ crossWindowBlurListeners.isCrossWindowBlurEnabled()
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 4a4e990728f3..6f4a73ec4516 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -18,6 +18,7 @@ import com.android.systemui.ExpandHelper
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
+import com.android.systemui.biometrics.UdfpsKeyguardViewController
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
@@ -109,6 +110,11 @@ class LockscreenShadeTransitionController @Inject constructor(
private var nextHideKeyguardNeedsNoAnimation = false
/**
+ * The udfpsKeyguardViewController if it exists.
+ */
+ var udfpsKeyguardViewController: UdfpsKeyguardViewController? = null
+
+ /**
* The touch helper responsible for the drag down animation.
*/
val touchHelper = DragDownHelper(falsingManager, falsingCollector, this, context)
@@ -291,6 +297,7 @@ class LockscreenShadeTransitionController @Inject constructor(
// Fade out all content only visible on the lockscreen
notificationPanelController.setKeyguardOnlyContentAlpha(1.0f - scrimProgress)
depthController.transitionToFullShadeProgress = scrimProgress
+ udfpsKeyguardViewController?.setTransitionToFullShadeProgress(scrimProgress)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 89005513d3d0..452d69369d71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -186,7 +186,7 @@ class NotificationShadeDepthController @Inject constructor(
var blur = max(shadeRadius.toInt(), globalActionsRadius)
// Make blur be 0 if it is necessary to stop blur effect.
- if (scrimsVisible) {
+ if (scrimsVisible || !blurUtils.supportsBlursOnWindows()) {
blur = 0
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
index f1479a149a41..f19cf5d8d9c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
@@ -22,11 +22,27 @@ class ExpandAnimationParameters(
)
var startTranslationZ = 0f
+
+ /**
+ * The top position of the notification at the start of the animation. This is needed in order
+ * to keep the notification at its place when launching a notification that is clipped rounded.
+ */
+ var startNotificationTop = 0f
var startClipTopAmount = 0
var parentStartClipTopAmount = 0
var progress = 0f
var linearProgress = 0f
+ /**
+ * The rounded top clipping at the beginning.
+ */
+ var startRoundedTopClipping = 0
+
+ /**
+ * The rounded top clipping of the parent notification at the start.
+ */
+ var parentStartRoundedTopClipping = 0
+
override val topChange: Int
get() {
// We need this compensation to ensure that the QS moves in sync.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index c248670c48db..1bbef2562d21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -40,6 +40,11 @@ class NotificationLaunchAnimatorController(
private val headsUpManager: HeadsUpManagerPhone,
private val notification: ExpandableNotificationRow
) : ActivityLaunchAnimator.Controller {
+
+ companion object {
+ const val ANIMATION_DURATION_TOP_ROUNDING = 100L
+ }
+
private val notificationEntry = notification.entry
private val notificationKey = notificationEntry.sbn.key
@@ -54,18 +59,37 @@ class NotificationLaunchAnimatorController(
val height = max(0, notification.actualHeight - notification.clipBottomAmount)
val location = notification.locationOnScreen
+ val clipStartLocation = notificationListContainer.getTopClippingStartLocation()
+ val roundedTopClipping = Math.max(clipStartLocation - location[1], 0)
+ val windowTop = location[1] + roundedTopClipping
+ val topCornerRadius = if (roundedTopClipping > 0) {
+ // Because the rounded Rect clipping is complex, we start the top rounding at
+ // 0, which is pretty close to matching the real clipping.
+ // We'd have to clipOut the overlaid drawable too with the outer rounded rect in case
+ // if we'd like to have this perfect, but this is close enough.
+ 0f
+ } else {
+ notification.currentBackgroundRadiusTop
+ }
val params = ExpandAnimationParameters(
- top = location[1],
+ top = windowTop,
bottom = location[1] + height,
left = location[0],
right = location[0] + notification.width,
- topCornerRadius = notification.currentBackgroundRadiusTop,
+ topCornerRadius = topCornerRadius,
bottomCornerRadius = notification.currentBackgroundRadiusBottom
)
params.startTranslationZ = notification.translationZ
+ params.startNotificationTop = notification.translationY
+ params.startRoundedTopClipping = roundedTopClipping
params.startClipTopAmount = notification.clipTopAmount
if (notification.isChildInGroup) {
+ params.startNotificationTop += notification.notificationParent.translationY
+ val parentRoundedClip = Math.max(clipStartLocation
+ - notification.notificationParent.locationOnScreen[1], 0)
+ params.parentStartRoundedTopClipping = parentRoundedClip
+
val parentClip = notification.notificationParent.clipTopAmount
params.parentStartClipTopAmount = parentClip
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index fe75739d33c4..76f9fe728f2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -35,6 +35,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.AnimationDrawable;
@@ -85,6 +86,7 @@ import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -252,6 +254,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private OnExpandClickListener mOnExpandClickListener;
private View.OnClickListener mOnAppClickListener;
private View.OnClickListener mOnFeedbackClickListener;
+ private Path mExpandingClipPath;
// Listener will be called when receiving a long click event.
// Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -836,6 +839,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {
if (mExpandAnimationRunning && !isChildInGroup && mNotificationParent != null) {
mNotificationParent.setChildIsExpanding(false);
+ mNotificationParent.setExpandingClipPath(null);
mNotificationParent.setExtraWidthForClipping(0.0f);
mNotificationParent.setMinimumHeightForClipping(0);
}
@@ -2032,7 +2036,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
setTranslationZ(translationZ);
float extraWidthForClipping = params.getWidth() - getWidth();
setExtraWidthForClipping(extraWidthForClipping);
- int top = params.getTop();
+ int top;
+ if (params.getStartRoundedTopClipping() > 0) {
+ // If we were clipping initially, let's interpolate from the start position to the
+ // top. Otherwise, we just take the top directly.
+ float expandProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+ params.getProgress(0,
+ NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING));
+ float startTop = params.getStartNotificationTop();
+ top = (int) Math.min(MathUtils.lerp(startTop,
+ params.getTop(), expandProgress),
+ startTop);
+ } else {
+ top = params.getTop();
+ }
+ int actualHeight = params.getBottom() - top;
+ setActualHeight(actualHeight);
int startClipTopAmount = params.getStartClipTopAmount();
int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, params.getProgress());
if (mNotificationParent != null) {
@@ -2061,13 +2080,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
setClipTopAmount(clipTopAmount);
}
setTranslationY(top);
- setActualHeight(params.getHeight());
mTopRoundnessDuringExpandAnimation = params.getTopCornerRadius() / mOutlineRadius;
mBottomRoundnessDuringExpandAnimation = params.getBottomCornerRadius() / mOutlineRadius;
invalidateOutline();
- mBackgroundNormal.setExpandAnimationParams(params);
+ mBackgroundNormal.setExpandAnimationSize(params.getWidth(), actualHeight);
}
public void setExpandAnimationRunning(boolean expandAnimationRunning) {
@@ -3081,6 +3099,26 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return super.childNeedsClipping(child);
}
+ /**
+ * Set a clip path to be set while expanding the notification. This is needed to nicely
+ * clip ourselves during the launch if we were clipped rounded in the beginning
+ */
+ public void setExpandingClipPath(Path path) {
+ mExpandingClipPath = path;
+ invalidate();
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ canvas.save();
+ if (mExpandingClipPath != null && (mExpandAnimationRunning || mChildIsExpanding)) {
+ // If we're launching a notification, let's clip if a clip rounded to the clipPath
+ canvas.clipPath(mExpandingClipPath);
+ }
+ super.dispatchDraw(canvas);
+ canvas.restore();
+ }
+
@Override
protected void applyRoundness() {
super.applyRoundness();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 754de580cd61..0f615aa9356f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -240,10 +240,10 @@ public class NotificationBackgroundView extends View {
invalidate();
}
- /** Set the current expand animation parameters. */
- public void setExpandAnimationParams(ExpandAnimationParameters params) {
- mActualHeight = params.getHeight();
- mActualWidth = params.getWidth();
+ /** Set the current expand animation size. */
+ public void setExpandAnimationSize(int actualWidth, int actualHeight) {
+ mActualHeight = actualHeight;
+ mActualWidth = actualWidth;
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 26606cda5582..197920f7be19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -72,8 +72,6 @@ public class AmbientState {
private boolean mUnlockHintRunning;
private boolean mQsCustomizerShowing;
private int mIntrinsicPadding;
- private int mExpandAnimationTopChange;
- private ExpandableNotificationRow mExpandingNotification;
private float mHideAmount;
private boolean mAppearing;
private float mPulseHeight = MAX_PULSE_HEIGHT;
@@ -518,22 +516,6 @@ public class AmbientState {
return isDozing() && !isPulsing(row.getEntry());
}
- public void setExpandAnimationTopChange(int expandAnimationTopChange) {
- mExpandAnimationTopChange = expandAnimationTopChange;
- }
-
- public void setExpandingNotification(ExpandableNotificationRow row) {
- mExpandingNotification = row;
- }
-
- public ExpandableNotificationRow getExpandingNotification() {
- return mExpandingNotification;
- }
-
- public int getExpandAnimationTopChange() {
- return mExpandAnimationTopChange;
- }
-
/**
* @return {@code true } when shade is completely hidden: in AOD, ambient display or when
* bypassing.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 2a2e733f78a1..7a5c18896e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -200,4 +200,11 @@ public interface NotificationListContainer extends
default void setWillExpand(boolean willExpand) {}
void setNotificationActivityStarter(NotificationActivityStarter notificationActivityStarter);
+
+ /**
+ * @return the start location where we start clipping notifications.
+ */
+ default int getTopClippingStartLocation() {
+ return 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 9ba04bf2f49f..4e6d376919e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -77,6 +77,7 @@ import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.CommandQueue;
@@ -90,6 +91,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
@@ -139,6 +141,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
// adb shell setprop persist.debug.nssl true && adb reboot
private static final boolean DEBUG = SystemProperties.getBoolean("persist.debug.nssl",
false /* default */);
+ // TODO(b/187291379) disable again before release
+ private static final boolean DEBUG_REMOVE_ANIMATION = SystemProperties.getBoolean(
+ "persist.debug.nssl.dismiss", true /* default */);
private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
@@ -471,6 +476,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private final Path mRoundedClipPath = new Path();
/**
+ * The clip Path used to clip the launching notification. This may be different
+ * from the normal path, as the views launch animation could start clipped.
+ */
+ private final Path mLaunchedNotificationClipPath = new Path();
+
+ /**
* Should we use rounded rect clipping right now
*/
private boolean mShouldUseRoundedRectClipping = false;
@@ -493,6 +504,26 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private boolean mLaunchingNotification;
/**
+ * Does the launching notification need to be clipped
+ */
+ private boolean mLaunchingNotificationNeedsToBeClipped;
+
+ /**
+ * The current launch animation params when launching a notification
+ */
+ private ExpandAnimationParameters mLaunchAnimationParams;
+
+ /**
+ * Corner radii of the launched notification if it's clipped
+ */
+ private float[] mLaunchedNotificationRadii = new float[8];
+
+ /**
+ * The notification that is being launched currently.
+ */
+ private ExpandableNotificationRow mExpandingNotificationRow;
+
+ /**
* Do notifications dismiss with normal transitioning
*/
private boolean mDismissUsingRowTranslationX = true;
@@ -2647,7 +2678,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
boolean generateRemoveAnimation(ExpandableView child) {
+ String key = "";
+ if (DEBUG_REMOVE_ANIMATION) {
+ if (child instanceof ExpandableNotificationRow) {
+ key = ((ExpandableNotificationRow) child).getEntry().getKey();
+ }
+ Log.d(TAG, "generateRemoveAnimation " + key);
+ }
if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
+ if (DEBUG_REMOVE_ANIMATION) {
+ Log.d(TAG, "removedBecauseOfHeadsUp " + key);
+ }
mAddedHeadsUpChildren.remove(child);
return false;
}
@@ -2656,8 +2697,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mClearTransientViewsWhenFinished.add(child);
return true;
}
+ if (DEBUG_REMOVE_ANIMATION) {
+ Log.d(TAG, "generateRemove " + key
+ + "\nmIsExpanded " + mIsExpanded
+ + "\nmAnimationsEnabled " + mAnimationsEnabled
+ + "\n!invisible group " + !isChildInInvisibleGroup(child));
+ }
if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
if (!mChildrenToAddAnimated.contains(child)) {
+ if (DEBUG_REMOVE_ANIMATION) {
+ Log.d(TAG, "needsAnimation = true " + key);
+ }
// Generate Animations
mChildrenToRemoveAnimated.add(child);
mNeedsAnimation = true;
@@ -2679,7 +2729,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/**
* Remove a removed child view from the heads up animations if it was just added there
*
- * @return whether any child was removed from the list to animate
+ * @return whether any child was removed from the list to animate and the view was just added
*/
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
@@ -2698,7 +2748,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
((ExpandableNotificationRow) child).setHeadsUpAnimatingAway(false);
}
mTmpList.clear();
- return hasAddEvent;
+ return hasAddEvent && mAddedHeadsUpChildren.contains(child);
}
// TODO (b/162832756): remove since this won't happen in new pipeline (we prune groups in
@@ -2749,6 +2799,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* @return the amount of scrolling needed to start clipping notifications.
*/
private int getScrollAmountToScrollBoundary() {
+ if (mShouldUseSplitNotificationShade) {
+ return mSidePaddings;
+ }
return mTopPadding - mQsScrollBoundaryPosition;
}
@@ -2892,7 +2945,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
void setExpandingNotification(ExpandableNotificationRow row) {
- mAmbientState.setExpandingNotification(row);
+ if (mExpandingNotificationRow != null && row == null) {
+ // Let's unset the clip path being set during launch
+ mExpandingNotificationRow.setExpandingClipPath(null);
+ ExpandableNotificationRow parent = mExpandingNotificationRow.getNotificationParent();
+ if (parent != null) {
+ parent.setExpandingClipPath(null);
+ }
+ }
+ mExpandingNotificationRow = row;
+ updateLaunchedNotificationClipPath();
requestChildrenUpdate();
}
@@ -2902,10 +2964,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
- mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
-
- // Disable clipping for launches
+ // Modify the clipping for launching notifications
+ mLaunchAnimationParams = params;
setLaunchingNotification(params != null);
+ updateLaunchedNotificationClipPath();
requestChildrenUpdate();
}
@@ -5353,7 +5415,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return;
}
mLaunchingNotification = launching;
- updateUseRoundedRectClipping();
+ mLaunchingNotificationNeedsToBeClipped = mLaunchAnimationParams != null
+ && (mLaunchAnimationParams.getStartRoundedTopClipping() > 0
+ || mLaunchAnimationParams.getParentStartRoundedTopClipping() > 0);
+ if (!mLaunchingNotificationNeedsToBeClipped || !mLaunchingNotification) {
+ mLaunchedNotificationClipPath.reset();
+ }
+ // When launching notifications, we're clipping the children individually instead of in
+ // dispatchDraw
+ invalidate();
}
/**
@@ -5363,22 +5433,97 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
// We don't want to clip notifications when QS is expanded, because incoming heads up on
// the bottom would be clipped otherwise
boolean qsAllowsClipping = mQsExpansionFraction < 0.5f || mShouldUseSplitNotificationShade;
- boolean clip = !mLaunchingNotification && mIsExpanded && qsAllowsClipping;
+ boolean clip = mIsExpanded && qsAllowsClipping;
if (clip != mShouldUseRoundedRectClipping) {
mShouldUseRoundedRectClipping = clip;
invalidate();
}
}
+ /**
+ * Update the clip path for launched notifications in case they were originally clipped
+ */
+ private void updateLaunchedNotificationClipPath() {
+ if (!mLaunchingNotificationNeedsToBeClipped || !mLaunchingNotification
+ || mExpandingNotificationRow == null) {
+ return;
+ }
+ int left = Math.min(mLaunchAnimationParams.getLeft(), mRoundedRectClippingLeft);
+ int right = Math.max(mLaunchAnimationParams.getRight(), mRoundedRectClippingRight);
+ int bottom = Math.max(mLaunchAnimationParams.getBottom(), mRoundedRectClippingBottom);
+ float expandProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+ mLaunchAnimationParams.getProgress(0,
+ NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING));
+ int top = (int) Math.min(MathUtils.lerp(mRoundedRectClippingTop,
+ mLaunchAnimationParams.getTop(), expandProgress),
+ mRoundedRectClippingTop);
+ float topRadius = mLaunchAnimationParams.getTopCornerRadius();
+ float bottomRadius = mLaunchAnimationParams.getBottomCornerRadius();
+ mLaunchedNotificationRadii[0] = topRadius;
+ mLaunchedNotificationRadii[1] = topRadius;
+ mLaunchedNotificationRadii[2] = topRadius;
+ mLaunchedNotificationRadii[3] = topRadius;
+ mLaunchedNotificationRadii[4] = bottomRadius;
+ mLaunchedNotificationRadii[5] = bottomRadius;
+ mLaunchedNotificationRadii[6] = bottomRadius;
+ mLaunchedNotificationRadii[7] = bottomRadius;
+ mLaunchedNotificationClipPath.reset();
+ mLaunchedNotificationClipPath.addRoundRect(left, top, right, bottom,
+ mLaunchedNotificationRadii, Path.Direction.CW);
+ // Offset into notification clip coordinates instead of parent ones.
+ // This is needed since the notification changes in translationZ, where clipping via
+ // canvas dispatching won't work.
+ ExpandableNotificationRow expandingRow = mExpandingNotificationRow;
+ if (expandingRow.getNotificationParent() != null) {
+ expandingRow = expandingRow.getNotificationParent();
+ }
+ mLaunchedNotificationClipPath.offset(
+ -expandingRow.getLeft() - expandingRow.getTranslationX(),
+ -expandingRow.getTop() - expandingRow.getTranslationY());
+ expandingRow.setExpandingClipPath(mLaunchedNotificationClipPath);
+ if (mShouldUseRoundedRectClipping) {
+ invalidate();
+ }
+ }
+
@Override
protected void dispatchDraw(Canvas canvas) {
- if (mShouldUseRoundedRectClipping) {
+ if (mShouldUseRoundedRectClipping && !mLaunchingNotification) {
+ // When launching notifications, we're clipping the children individually instead of in
+ // dispatchDraw
// Let's clip rounded.
canvas.clipPath(mRoundedClipPath);
}
super.dispatchDraw(canvas);
}
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ if (mShouldUseRoundedRectClipping && mLaunchingNotification) {
+ // Let's clip children individually during notification launch
+ canvas.save();
+ ExpandableView expandableView = (ExpandableView) child;
+ Path clipPath;
+ if (expandableView.isExpandAnimationRunning()
+ || ((ExpandableView) child).hasExpandingChild()) {
+ // When launching the notification, it is not clipped by this layout, but by the
+ // view itself. This is because the view is Translating in Z, where this clipPath
+ // wouldn't apply.
+ clipPath = null;
+ } else {
+ clipPath = mRoundedClipPath;
+ }
+ if (clipPath != null) {
+ canvas.clipPath(clipPath);
+ }
+ boolean result = super.drawChild(canvas, child, drawingTime);
+ canvas.restore();
+ return result;
+ } else {
+ return super.drawChild(canvas, child, drawingTime);
+ }
+ }
+
/**
* Calculate the total translation needed when dismissing.
*/
@@ -5393,6 +5538,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
/**
+ * @return the start location where we start clipping notifications.
+ */
+ public int getTopClippingStartLocation() {
+ return mIsExpanded ? mQsScrollBoundaryPosition : 0;
+ }
+
+ /**
* A listener that is notified when the empty space below the notifications is clicked on
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 495eda772219..e71f7dbb008b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1533,6 +1533,11 @@ public class NotificationStackScrollLayoutController {
}
@Override
+ public int getTopClippingStartLocation() {
+ return mView.getTopClippingStartLocation();
+ }
+
+ @Override
public View getContainerChildAt(int i) {
return mView.getContainerChildAt(i);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 54ef623e95ab..b148eeba2cf5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -27,6 +27,7 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController {
private val listeners: MutableList<ConfigurationController.ConfigurationListener> = ArrayList()
private val lastConfig = Configuration()
private var density: Int = 0
+ private var smallestScreenWidth: Int = 0
private var fontScale: Float = 0.toFloat()
private val inCarMode: Boolean
private var uiMode: Int = 0
@@ -38,6 +39,7 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController {
this.context = context
fontScale = currentConfig.fontScale
density = currentConfig.densityDpi
+ smallestScreenWidth = currentConfig.smallestScreenWidthDp
inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
Configuration.UI_MODE_TYPE_CAR
uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
@@ -72,6 +74,14 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController {
this.fontScale = fontScale
}
+ val smallestScreenWidth = newConfig.smallestScreenWidthDp
+ if (smallestScreenWidth != this.smallestScreenWidth) {
+ this.smallestScreenWidth = smallestScreenWidth
+ listeners.filterForEach({ this.listeners.contains(it) }) {
+ it.onSmallestScreenWidthChanged()
+ }
+ }
+
val localeList = newConfig.locales
if (localeList != this.localeList) {
this.localeList = localeList
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index f461d944d2c2..5d31786dd638 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -36,9 +36,11 @@ import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.Fragment;
import android.app.StatusBarManager;
+import android.content.ContentResolver;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -50,10 +52,12 @@ import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Bundle;
+import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserManager;
import android.os.VibrationEffect;
+import android.provider.Settings;
import android.util.Log;
import android.util.MathUtils;
import android.view.LayoutInflater;
@@ -210,6 +214,8 @@ public class NotificationPanelViewController extends PanelViewController {
new MyOnHeadsUpChangedListener();
private final HeightListener mHeightListener = new HeightListener();
private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
+ private final SettingsChangeObserver mSettingsChangeObserver;
+
@VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
new StatusBarStateListener();
private final BiometricUnlockController mBiometricUnlockController;
@@ -594,6 +600,8 @@ public class NotificationPanelViewController extends PanelViewController {
private int mScreenCornerRadius;
private boolean mQSAnimatingHiddenFromCollapsed;
+ private final ContentResolver mContentResolver;
+
private final Executor mUiExecutor;
private final SecureSettings mSecureSettings;
@@ -635,6 +643,7 @@ public class NotificationPanelViewController extends PanelViewController {
@Inject
public NotificationPanelViewController(NotificationPanelView view,
@Main Resources resources,
+ @Main Handler handler,
LayoutInflater layoutInflater,
NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
DynamicPrivacyController dynamicPrivacyController,
@@ -678,6 +687,7 @@ public class NotificationPanelViewController extends PanelViewController {
TapAgainViewController tapAgainViewController,
NavigationModeController navigationModeController,
FragmentService fragmentService,
+ ContentResolver contentResolver,
QuickAccessWalletController quickAccessWalletController,
@Main Executor uiExecutor,
SecureSettings secureSettings,
@@ -704,15 +714,12 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
mDepthController = notificationShadeDepthController;
mFeatureFlags = featureFlags;
+ mContentResolver = contentResolver;
mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
mQSDetailDisplayer = qsDetailDisplayer;
mFragmentService = fragmentService;
- mKeyguardUserSwitcherEnabled = mResources.getBoolean(
- com.android.internal.R.bool.config_keyguardUserSwitcher);
- mKeyguardQsUserSwitchEnabled =
- mKeyguardUserSwitcherEnabled && mResources.getBoolean(
- R.bool.config_keyguard_user_switch_opens_qs_details);
+ mSettingsChangeObserver = new SettingsChangeObserver(handler);
mShouldUseSplitNotificationShade =
Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources);
mView.setWillNotDraw(!DEBUG);
@@ -795,6 +802,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
+ updateUserSwitcherFlags();
onFinishInflate();
}
@@ -1034,6 +1042,10 @@ public class NotificationPanelViewController extends PanelViewController {
view = mLayoutInflater.inflate(layoutId, mView, false);
mView.addView(view, index);
} else {
+ // Add the stub back so we can re-inflate it again if necessary
+ ViewStub stub = new ViewStub(mView.getContext(), layoutId);
+ stub.setId(stubId);
+ mView.addView(stub, index);
view = null;
}
} else if (enabled) {
@@ -1061,6 +1073,7 @@ public class NotificationPanelViewController extends PanelViewController {
updateResources();
// Re-inflate the keyguard user switcher group.
+ updateUserSwitcherFlags();
boolean isUserSwitcherEnabled = mUserManager.isUserSwitcherEnabled();
boolean showQsUserSwitch = mKeyguardQsUserSwitchEnabled && isUserSwitcherEnabled;
boolean showKeyguardUserSwitcher =
@@ -1443,7 +1456,6 @@ public class NotificationPanelViewController extends PanelViewController {
private void setQsExpansionEnabled() {
mQsExpansionEnabled = mQsExpansionEnabledPolicy && mQsExpansionEnabledAmbient;
- Log.d(TAG, "Set qsExpansionEnabled: " + mQsExpansionEnabled);
if (mQs == null) return;
mQs.setHeaderClickable(mQsExpansionEnabled);
}
@@ -3874,6 +3886,26 @@ public class NotificationPanelViewController extends PanelViewController {
return false;
}
+ private void updateUserSwitcherFlags() {
+ mKeyguardUserSwitcherEnabled = mResources.getBoolean(
+ com.android.internal.R.bool.config_keyguardUserSwitcher);
+ mKeyguardQsUserSwitchEnabled =
+ mKeyguardUserSwitcherEnabled && mResources.getBoolean(
+ R.bool.config_keyguard_user_switch_opens_qs_details);
+ }
+
+ private void registerSettingsChangeListener() {
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED),
+ /* notifyForDescendants */ false,
+ mSettingsChangeObserver
+ );
+ }
+
+ private void unregisterSettingsChangeListener() {
+ mContentResolver.unregisterContentObserver(mSettingsChangeObserver);
+ }
+
private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
@Override
public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
@@ -4195,6 +4227,15 @@ public class NotificationPanelViewController extends PanelViewController {
}
@Override
+ public void onSmallestScreenWidthChanged() {
+ if (DEBUG) Log.d(TAG, "onSmallestScreenWidthChanged");
+
+ // Can affect multi-user switcher visibility as it depends on screen size by default:
+ // it is enabled only for devices with large screens (see config_keyguardUserSwitcher)
+ reInflateViews();
+ }
+
+ @Override
public void onOverlayChanged() {
if (DEBUG) Log.d(TAG, "onOverlayChanged");
reInflateViews();
@@ -4207,6 +4248,21 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
+ private class SettingsChangeObserver extends ContentObserver {
+
+ SettingsChangeObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ if (DEBUG) Log.d(TAG, "onSettingsChanged");
+
+ // Can affect multi-user switcher visibility
+ reInflateViews();
+ }
+ }
+
private class StatusBarStateListener implements StateListener {
@Override
public void onStateChanged(int statusBarState) {
@@ -4322,10 +4378,12 @@ public class NotificationPanelViewController extends PanelViewController {
mConfigurationListener.onThemeChanged();
mFalsingManager.addTapListener(mFalsingTapListener);
mKeyguardIndicationController.init();
+ registerSettingsChangeListener();
}
@Override
public void onViewDetachedFromWindow(View v) {
+ unregisterSettingsChangeListener();
mFragmentService.getFragmentHostManager(mView)
.removeTagListener(QS.TAG, mFragmentListener);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 1cb0be0efc90..ed8fb31aea32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -47,6 +47,8 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
private View mKeyguardStatusBar;
private boolean mQsExpanded;
private boolean mCustomizerAnimating;
+ private boolean mCustomizing;
+ private boolean mDetailShowing;
private int mBottomPadding;
private int mStackScrollerMargin;
@@ -140,7 +142,18 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
}
public void setCustomizerShowing(boolean isShowing) {
- if (isShowing) {
+ mCustomizing = isShowing;
+ updateBottomMargin();
+ mStackScroller.setQsCustomizerShowing(isShowing);
+ }
+
+ public void setDetailShowing(boolean isShowing) {
+ mDetailShowing = isShowing;
+ updateBottomMargin();
+ }
+
+ private void updateBottomMargin() {
+ if (mCustomizing || mDetailShowing) {
// Clear out bottom paddings/margins so the qs customization can be full height.
setPadding(0, 0, 0, 0);
setBottomMargin(mStackScroller, 0);
@@ -148,7 +161,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
setPadding(0, 0, 0, mBottomPadding);
setBottomMargin(mStackScroller, mStackScrollerMargin);
}
- mStackScroller.setQsCustomizerShowing(isShowing);
}
private void setBottomMargin(View v, int bottomMargin) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index 0a6cf7be736f..c2bd87c6276f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -33,6 +33,7 @@ public interface ConfigurationController extends CallbackController<Configuratio
interface ConfigurationListener {
default void onConfigChanged(Configuration newConfig) {}
default void onDensityOrFontScaleChanged() {}
+ default void onSmallestScreenWidthChanged() {}
default void onOverlayChanged() {}
default void onUiModeChanged() {}
default void onThemeChanged() {}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index ca1f55e95ff4..11ddbd045cd4 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -241,7 +241,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
@Override
public void onReceive(Context context, Intent intent) {
boolean newWorkProfile = Intent.ACTION_MANAGED_PROFILE_ADDED.equals(intent.getAction());
- boolean userStarted = Intent.ACTION_USER_STARTED.equals(intent.getAction());
+ boolean userStarted = Intent.ACTION_USER_SWITCHED.equals(intent.getAction());
boolean isManagedProfile = mUserManager.isManagedProfile(
intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
if (userStarted || newWorkProfile) {
@@ -288,7 +288,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
public void start() {
if (DEBUG) Log.d(TAG, "Start");
final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_STARTED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor,
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java b/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java
new file mode 100644
index 000000000000..2b7a33260248
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.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 com.android.systemui.tv;
+
+import android.app.Activity;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+
+import java.util.function.Consumer;
+
+/**
+ * Generic bottom sheet with up to two icons in the beginning and two buttons.
+ */
+public abstract class TvBottomSheetActivity extends Activity {
+
+ private static final String TAG = TvBottomSheetActivity.class.getSimpleName();
+ private Drawable mBackgroundWithBlur;
+ private Drawable mBackgroundWithoutBlur;
+
+ private final Consumer<Boolean> mBlurConsumer = this::onBlurChanged;
+
+ private void onBlurChanged(boolean enabled) {
+ Log.v(TAG, "blur enabled: " + enabled);
+ getWindow().setBackgroundDrawable(enabled ? mBackgroundWithBlur : mBackgroundWithoutBlur);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.tv_bottom_sheet);
+
+ overridePendingTransition(R.anim.tv_bottom_sheet_enter, 0);
+
+ mBackgroundWithBlur = getResources()
+ .getDrawable(R.drawable.bottom_sheet_background_with_blur);
+ mBackgroundWithoutBlur = getResources().getDrawable(R.drawable.bottom_sheet_background);
+
+ DisplayMetrics metrics = getResources().getDisplayMetrics();
+ int screenWidth = metrics.widthPixels;
+ int screenHeight = metrics.heightPixels;
+ int marginPx = getResources().getDimensionPixelSize(R.dimen.bottom_sheet_margin);
+
+ WindowManager.LayoutParams windowParams = getWindow().getAttributes();
+ windowParams.width = screenWidth - marginPx * 2;
+ windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ windowParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+ windowParams.horizontalMargin = 0f;
+ windowParams.verticalMargin = (float) marginPx / screenHeight;
+ windowParams.format = PixelFormat.TRANSPARENT;
+ windowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+ windowParams.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+ windowParams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ getWindow().setAttributes(windowParams);
+ getWindow().setElevation(getWindow().getElevation() + 5);
+ getWindow().setBackgroundBlurRadius(getResources().getDimensionPixelSize(
+ R.dimen.bottom_sheet_background_blur_radius));
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getWindowManager().addCrossWindowBlurEnabledListener(mBlurConsumer);
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ getWindowManager().removeCrossWindowBlurEnabledListener(mBlurConsumer);
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+ overridePendingTransition(0, R.anim.tv_bottom_sheet_exit);
+ }
+
+}
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 41f987727860..0c750a179358 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -53,6 +53,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -117,6 +118,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private AccessibilityManager mAccessibilityManager;
@Mock
+ private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ @Mock
private ScreenLifecycle mScreenLifecycle;
@Mock
private Vibrator mVibrator;
@@ -176,6 +179,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mFalsingManager,
mPowerManager,
mAccessibilityManager,
+ mLockscreenShadeTransitionController,
mScreenLifecycle,
mVibrator,
Optional.of(mHbmProvider));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 5923de6719a8..f62587c6e87c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -35,6 +35,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -65,6 +66,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
@Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock
+ private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ @Mock
private DumpManager mDumpManager;
@Mock
private DelayableExecutor mExecutor;
@@ -106,6 +109,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
mExecutor,
mDumpManager,
mKeyguardViewMediator,
+ mLockscreenShadeTransitionController,
mUdfpsController);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index da63b8a3ca4b..4980f7406cee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -52,6 +52,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -108,7 +109,8 @@ public class NavigationBarControllerTest extends SysuiTestCase {
Dependency.get(Dependency.MAIN_HANDLER),
mock(UiEventLogger.class),
mock(NavigationBarOverlayController.class),
- mock(ConfigurationController.class)));
+ mock(ConfigurationController.class),
+ mock(UserTracker.class)));
initializeNavigationBars();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 4ec45b444c46..b1afeecf39f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -76,6 +76,7 @@ import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -276,7 +277,8 @@ public class NavigationBarTest extends SysuiTestCase {
mock(SystemActions.class),
mHandler,
mock(NavigationBarOverlayController.class),
- mUiEventLogger));
+ mUiEventLogger,
+ mock(UserTracker.class)));
}
private void processAllMessages() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java
index 9c68f0d8a7ff..478658eb232d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java
@@ -57,16 +57,19 @@ class FakeSession implements ScrollCaptureClient.Session {
private int mScrollDelta;
private int mPageHeight;
+ private int mTargetHeight;
FakeSession(int pageHeight, float maxPages, int tileHeight, int visiblePageTop,
- int visiblePageBottom, int availableTop, int availableBottom) {
+ int visiblePageBottom, int availableTop, int availableBottom,
+ int maxTiles) {
mPageHeight = pageHeight;
mTileHeight = tileHeight;
mAvailable = new Rect(0, availableTop, getPageWidth(), availableBottom);
mAvailableTop = new Rect(mAvailable);
mAvailableTop.inset(0, 0, 0, pageHeight);
mVisiblePage = new Rect(0, visiblePageTop, getPageWidth(), visiblePageBottom);
- mMaxTiles = (int) Math.ceil((pageHeight * maxPages) / mTileHeight);
+ mTargetHeight = (int) (pageHeight * maxPages);
+ mMaxTiles = maxTiles;
}
private static Image mockImage() {
@@ -158,6 +161,11 @@ class FakeSession implements ScrollCaptureClient.Session {
}
@Override
+ public int getTargetHeight() {
+ return mTargetHeight;
+ }
+
+ @Override
public int getTileHeight() {
return mTileHeight;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
index 2520af94380c..4c8a4b0f8f61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
@@ -42,21 +42,6 @@ import org.junit.runner.RunWith;
@RunWith(AndroidTestingRunner.class)
public class FakeSessionTest extends SysuiTestCase {
@Test
- public void testMaxTiles() {
- FakeSession session = new FakeSession(
- /* pageHeight */ 100,
- /* maxPages */ 2.25f,
- /* tileHeight */ 10,
- /* visiblePageTop */ 0,
- /* visiblePageBottom */ 100,
- /* availableTop */ -250,
- /* availableBottom */ 250);
-
- // (pageHeight * maxPages) / tileHeight
- assertEquals("getMaxTiles()", 23, session.getMaxTiles());
- }
-
- @Test
public void testNonEmptyResult_hasImage() {
FakeSession session = new FakeSession(
/* pageHeight */ 100,
@@ -65,7 +50,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ 0,
- /* availableBottom */ 100);
+ /* availableBottom */ 100,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
assertNotNull("result.image", result.image);
assertNotNull("result.image.getHardwareBuffer()", result.image.getHardwareBuffer());
@@ -80,7 +66,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ 0,
- /* availableBottom */ 100);
+ /* availableBottom */ 100,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-100));
assertNull("result.image", result.image);
}
@@ -94,7 +81,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -250,
- /* availableBottom */ 250);
+ /* availableBottom */ 250,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
assertEquals("requested top", 0, result.requested.top);
@@ -113,7 +101,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -250,
- /* availableBottom */ 250);
+ /* availableBottom */ 250,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(90));
assertEquals("requested top", 90, result.requested.top);
@@ -132,7 +121,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -250,
- /* availableBottom */ 250);
+ /* availableBottom */ 250,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-100));
assertEquals("requested top", -100, result.requested.top);
@@ -151,7 +141,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -250,
- /* availableBottom */ 250);
+ /* availableBottom */ 250,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(150));
assertEquals("requested top", 150, result.requested.top);
@@ -170,7 +161,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -100,
- /* availableBottom */ 100);
+ /* availableBottom */ 100,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-125));
assertEquals("requested top", -125, result.requested.top);
@@ -189,7 +181,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -100,
- /* availableBottom */ 100);
+ /* availableBottom */ 100,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(75));
assertEquals("requested top", 75, result.requested.top);
@@ -211,7 +204,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 25, // <<--
/* visiblePageBottom */ 100,
/* availableTop */ -150,
- /* availableBottom */ 150);
+ /* availableBottom */ 150,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-150));
assertEquals("requested top", -150, result.requested.top);
@@ -233,7 +227,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 0,
/* visiblePageBottom */ 75,
/* availableTop */ -150,
- /* availableBottom */ 150);
+ /* availableBottom */ 150,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(50));
assertEquals("requested top", 50, result.requested.top);
@@ -252,7 +247,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -100,
- /* availableBottom */ 200);
+ /* availableBottom */ 200,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-150));
assertTrue("captured rect is empty", result.captured.isEmpty());
}
@@ -266,7 +262,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -100,
- /* availableBottom */ 200);
+ /* availableBottom */ 200,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(200));
assertTrue("captured rect is empty", result.captured.isEmpty());
}
@@ -280,7 +277,8 @@ public class FakeSessionTest extends SysuiTestCase {
/* visiblePageTop */ 60, // <<---
/* visiblePageBottom */ 0,
/* availableTop */ -150,
- /* availableBottom */ 150);
+ /* availableBottom */ 150,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
assertEquals("requested top", 0, result.requested.top);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
index 5bab1bcddb6a..10c878a92745 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
@@ -92,14 +92,16 @@ public class ScrollCaptureControllerTest extends SysuiTestCase {
// Each tile is cropped to the visible page size, which is inset 5px from the TOP
// requested result
// 0, 50 5, 50
- // -45, 5 -40, 5 <-- clear previous / top
- // 5, 55 5, 55 (not cropped, target is positioned fully within visible range)
- // 55, 105 55, 105
- // 105, 155 105, 155
- // 155, 205 155, 205 <-- bottom
-
- assertEquals("top", -40, screenshot.getTop());
- assertEquals("bottom", 205, screenshot.getBottom());
+ // -45, 5 -40, 5
+ // -90, -40 -85, -40 <-- clear previous / top
+ // -40, 10 -40, 10 (not cropped, target is positioned fully within visible range)
+ // 10, 60 10, 60
+ // 60, 110 60, 110
+ // 110, 160 110, 160
+ // 160, 210 160, 210 <-- bottom
+
+ assertEquals("top", -85, screenshot.getTop());
+ assertEquals("bottom", 210, screenshot.getBottom());
}
@Test
@@ -119,13 +121,14 @@ public class ScrollCaptureControllerTest extends SysuiTestCase {
// requested result
// 0, 50 0, 50 // not cropped, positioned within visible range
// -50, 0 -50, 0 <-- clear previous/reverse
- // 0, 50 - 0, 45 // target now positioned at page bottom, bottom cropped
+ // 0, 50 0, 45 // target now positioned at page bottom, bottom cropped
// 45, 95, 45, 90
// 90, 140, 140, 135
- // 135, 185 185, 180 <-- bottom
+ // 135, 185 185, 180
+ // 180, 230 180, 225 <-- bottom
assertEquals("top", -50, screenshot.getTop());
- assertEquals("bottom", 180, screenshot.getBottom());
+ assertEquals("bottom", 225, screenshot.getBottom());
}
@Test
@@ -265,7 +268,8 @@ public class ScrollCaptureControllerTest extends SysuiTestCase {
mLocalVisibleBottom = mPageHeight;
}
Session session = new FakeSession(mPageHeight, mMaxPages, mTileHeight,
- mLocalVisibleTop, mLocalVisibleBottom, mAvailableTop, mAvailableBottom);
+ mLocalVisibleTop, mLocalVisibleBottom, mAvailableTop, mAvailableBottom,
+ /* maxTiles */ 30);
ScrollCaptureClient client = mock(ScrollCaptureClient.class);
when(client.start(/* response */ any(), /* maxPages */ anyFloat()))
.thenReturn(immediateFuture(session));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
index 7c7d2dc16c03..2b44d8bbd364 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar
import android.content.res.Resources
+import android.view.CrossWindowBlurListeners
import android.view.SurfaceControl
import android.view.ViewRootImpl
import androidx.test.filters.SmallTest
@@ -37,11 +38,13 @@ class BlurUtilsTest : SysuiTestCase() {
@Mock lateinit var resources: Resources
@Mock lateinit var dumpManager: DumpManager
@Mock lateinit var transaction: SurfaceControl.Transaction
+ @Mock lateinit var corssWindowBlurListeners: CrossWindowBlurListeners
lateinit var blurUtils: BlurUtils
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ `when`(corssWindowBlurListeners.isCrossWindowBlurEnabled).thenReturn(true)
blurUtils = TestableBlurUtils()
}
@@ -71,7 +74,7 @@ class BlurUtilsTest : SysuiTestCase() {
verify(transaction).apply()
}
- inner class TestableBlurUtils() : BlurUtils(resources, dumpManager) {
+ inner class TestableBlurUtils() : BlurUtils(resources, corssWindowBlurListeners, dumpManager) {
override fun supportsBlursOnWindows(): Boolean {
return true
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 61c3835a88d2..0fb259575c87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -47,6 +47,7 @@ import org.mockito.Mockito.anyString
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doThrow
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -90,7 +91,8 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
`when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
(answer.arguments[0] as Float * maxBlur).toInt()
}
- `when`(blurUtils.minBlurRadius).thenReturn(0)
+ `when`(blurUtils.supportsBlursOnWindows()).thenReturn(true)
+ `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
`when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
notificationShadeDepthController = NotificationShadeDepthController(
@@ -191,6 +193,14 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
}
@Test
+ fun setFullShadeTransition_appliesBlur_onlyIfSupported() {
+ reset(blurUtils)
+ notificationShadeDepthController.transitionToFullShadeProgress = 1f
+ notificationShadeDepthController.updateBlurCallback.doFrame(0)
+ verify(blurUtils).applyBlur(any(), eq(0), eq(false))
+ }
+
+ @Test
fun updateGlobalDialogVisibility_animatesBlur() {
notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
verify(globalActionsSpring).animateTo(eq(maxBlur / 2), eq(root))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 2d51683c8ae5..2693b949f6ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -26,9 +26,12 @@ import static com.android.systemui.statusbar.notification.ViewGroupFadeHelper.re
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.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -37,9 +40,13 @@ import static org.mockito.Mockito.when;
import android.annotation.IdRes;
import android.app.ActivityManager;
+import android.content.ContentResolver;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.hardware.biometrics.BiometricSourceType;
+import android.os.Handler;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.UserManager;
import android.testing.AndroidTestingRunner;
@@ -50,6 +57,7 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
+import android.view.ViewStub;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -60,6 +68,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardClockSwitchController;
@@ -140,6 +149,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
private KeyguardBottomAreaView mKeyguardBottomArea;
@Mock
private KeyguardBottomAreaView mQsFrame;
+ private KeyguardStatusView mKeyguardStatusView;
@Mock
private ViewGroup mBigClockContainer;
@Mock
@@ -153,6 +163,10 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private KeyguardStatusBarView mKeyguardStatusBar;
@Mock
+ private View mUserSwitcherView;
+ @Mock
+ private ViewStub mUserSwitcherStubView;
+ @Mock
private HeadsUpTouchHelper.Callback mHeadsUpCallback;
@Mock
private PanelBar mPanelBar;
@@ -204,7 +218,6 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private KeyguardClockSwitch mKeyguardClockSwitch;
private PanelViewController.TouchHandler mTouchHandler;
- @Mock
private ConfigurationController mConfigurationController;
@Mock
private MediaHierarchyManager mMediaHiearchyManager;
@@ -265,6 +278,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private SecureSettings mSecureSettings;
@Mock
+ private ContentResolver mContentResolver;
+ @Mock
private TapAgainViewController mTapAgainViewController;
@Mock
private KeyguardIndicationController mKeyguardIndicationController;
@@ -287,6 +302,9 @@ public class NotificationPanelViewTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger);
+ mKeyguardStatusView = new KeyguardStatusView(mContext);
+ mKeyguardStatusView.setId(R.id.keyguard_status_view);
+
when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
when(mHeadsUpCallback.getContext()).thenReturn(mContext);
when(mView.getResources()).thenReturn(mResources);
@@ -301,6 +319,9 @@ public class NotificationPanelViewTest extends SysuiTestCase {
when(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)).thenReturn(400);
when(mView.getContext()).thenReturn(getContext());
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
+ when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView);
+ when(mView.findViewById(R.id.keyguard_user_switcher_stub)).thenReturn(
+ mUserSwitcherStubView);
when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
when(mView.findViewById(R.id.notification_stack_scroller))
.thenReturn(mNotificationStackScrollLayout);
@@ -320,7 +341,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null);
mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
- mNotificationContainerParent.addView(newViewWithId(R.id.keyguard_status_view));
+ mNotificationContainerParent.addView(mKeyguardStatusView);
when(mView.findViewById(R.id.notification_container_parent))
.thenReturn(mNotificationContainerParent);
when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager);
@@ -348,6 +369,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mFalsingManager,
mLockscreenShadeTransitionController,
new FalsingCollectorFake());
+ mConfigurationController = new ConfigurationControllerImpl(mContext);
when(mKeyguardStatusViewComponentFactory.build(any()))
.thenReturn(mKeyguardStatusViewComponent);
when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
@@ -358,10 +380,16 @@ public class NotificationPanelViewTest extends SysuiTestCase {
.thenReturn(mKeyguardStatusBarViewComponent);
when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController())
.thenReturn(mKeyguardStatusBarViewController);
+ when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean()))
+ .thenReturn(mKeyguardStatusView);
+ when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean()))
+ .thenReturn(mKeyguardBottomArea);
reset(mView);
+
mNotificationPanelViewController = new NotificationPanelViewController(mView,
mResources,
+ new Handler(Looper.getMainLooper()),
mLayoutInflater,
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
mFalsingManager, new FalsingCollectorFake(),
@@ -395,6 +423,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mTapAgainViewController,
mNavigationModeController,
mFragmentService,
+ mContentResolver,
mQuickAccessWalletController,
new FakeExecutor(new FakeSystemClock()),
mSecureSettings,
@@ -549,6 +578,38 @@ public class NotificationPanelViewTest extends SysuiTestCase {
}
@Test
+ public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() {
+ givenViewAttached();
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
+ updateMultiUserSetting(true);
+ clearInvocations(mView);
+
+ updateMultiUserSetting(false);
+
+ ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
+ verify(mView, atLeastOnce()).addView(captor.capture(), anyInt());
+ final View userSwitcherStub = CollectionUtils.find(captor.getAllValues(),
+ view -> view.getId() == R.id.keyguard_user_switcher_stub);
+ assertThat(userSwitcherStub).isNotNull();
+ assertThat(userSwitcherStub).isInstanceOf(ViewStub.class);
+ }
+
+ @Test
+ public void testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() {
+ givenViewAttached();
+ when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(null);
+ updateSmallestScreenWidth(300);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
+ when(mUserManager.isUserSwitcherEnabled()).thenReturn(true);
+
+ updateSmallestScreenWidth(800);
+
+ verify(mUserSwitcherStubView).inflate();
+ }
+
+ @Test
public void testSplitShadeLayout_isAlignedToGuideline() {
enableSplitShade();
@@ -682,6 +743,12 @@ public class NotificationPanelViewTest extends SysuiTestCase {
return mFalsingManager.getTapListeners().get(0);
}
+ private void givenViewAttached() {
+ for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
+ listener.onViewAttachedToWindow(mView);
+ }
+ }
+
private View newViewWithId(int id) {
View view = new View(mContext);
view.setId(id);
@@ -704,6 +771,21 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mNotificationPanelViewController.updateResources();
}
+ private void updateMultiUserSetting(boolean enabled) {
+ when(mUserManager.isUserSwitcherEnabled()).thenReturn(enabled);
+ final ArgumentCaptor<ContentObserver> observerCaptor =
+ ArgumentCaptor.forClass(ContentObserver.class);
+ verify(mContentResolver)
+ .registerContentObserver(any(), anyBoolean(), observerCaptor.capture());
+ observerCaptor.getValue().onChange(/* selfChange */ false);
+ }
+
+ private void updateSmallestScreenWidth(int smallestScreenWidthDp) {
+ Configuration configuration = new Configuration();
+ configuration.smallestScreenWidthDp = smallestScreenWidthDp;
+ mConfigurationController.onConfigurationChanged(configuration);
+ }
+
private void onTouchEvent(MotionEvent ev) {
mTouchHandler.onTouch(mView, ev);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 208790b24d8a..1a24c113a0df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -415,13 +415,6 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
}
@Test
- public void onUserSwitch_setsTheme() {
- mBroadcastReceiver.getValue().onReceive(null,
- new Intent(Intent.ACTION_USER_STARTED));
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
- }
-
- @Test
public void onProfileAdded_setsTheme() {
mBroadcastReceiver.getValue().onReceive(null,
new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
diff --git a/packages/services/CameraExtensionsProxy/AndroidManifest.xml b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
index e5f460e593b3..d35689413749 100644
--- a/packages/services/CameraExtensionsProxy/AndroidManifest.xml
+++ b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.camera">
+ package="com.android.cameraextensions">
<application
android:label="@string/app_name"
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 7b9ca2ddc345..69874f8a305a 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.camera;
+package com.android.cameraextensions;
import android.app.Service;
import android.content.Context;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 241a0dbac758..f63198866b08 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -872,18 +872,32 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
"userId=" + userId);
}
+ final int resolvedUserId;
+ final List<AccessibilityServiceInfo> serviceInfos;
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
- final int resolvedUserId = mSecurityPolicy
+ resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
+ serviceInfos = new ArrayList<>(
+ getUserStateLocked(resolvedUserId).mInstalledServices);
+ }
- if (Binder.getCallingPid() == OWN_PROCESS_ID) {
- return new ArrayList<>(getUserStateLocked(resolvedUserId).mInstalledServices);
+ if (Binder.getCallingPid() == OWN_PROCESS_ID) {
+ return serviceInfos;
+ }
+ final PackageManagerInternal pm = LocalServices.getService(
+ PackageManagerInternal.class);
+ final int callingUid = Binder.getCallingUid();
+ for (int i = serviceInfos.size() - 1; i >= 0; i--) {
+ final AccessibilityServiceInfo serviceInfo = serviceInfos.get(i);
+ if (pm.filterAppAccess(serviceInfo.getComponentName().getPackageName(), callingUid,
+ resolvedUserId)) {
+ serviceInfos.remove(i);
}
- return getUserStateLocked(resolvedUserId).mInstalledServices;
}
+ return serviceInfos;
}
@Override
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index acbf4875ae8d..5aec6aa99c12 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -409,6 +409,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// If the set of providers has been modified, notify each active AppWidgetHost
scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
+ // Possibly notify any new components of widget id changes
+ mBackupRestoreController.widgetComponentsChanged(userId);
}
}
}
@@ -2471,8 +2473,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
@Override
- public void restoreStarting(int userId) {
- mBackupRestoreController.restoreStarting(userId);
+ public void systemRestoreStarting(int userId) {
+ mBackupRestoreController.systemRestoreStarting(userId);
}
@Override
@@ -2481,8 +2483,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
@Override
- public void restoreFinished(int userId) {
- mBackupRestoreController.restoreFinished(userId);
+ public void systemRestoreFinished(int userId) {
+ mBackupRestoreController.systemRestoreFinished(userId);
}
@SuppressWarnings("deprecation")
@@ -4272,6 +4274,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private final HashMap<Host, ArrayList<RestoreUpdateRecord>> mUpdatesByHost =
new HashMap<>();
+ @GuardedBy("mLock")
+ private boolean mHasSystemRestoreFinished;
+
public List<String> getWidgetParticipants(int userId) {
if (DEBUG) {
Slog.i(TAG, "Getting widget participants for user: " + userId);
@@ -4375,12 +4380,13 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
return stream.toByteArray();
}
- public void restoreStarting(int userId) {
+ public void systemRestoreStarting(int userId) {
if (DEBUG) {
- Slog.i(TAG, "Restore starting for user: " + userId);
+ Slog.i(TAG, "System restore starting for user: " + userId);
}
synchronized (mLock) {
+ mHasSystemRestoreFinished = false;
// We're starting a new "system" restore operation, so any widget restore
// state that we see from here on is intended to replace the current
// widget configuration of any/all of the affected apps.
@@ -4542,26 +4548,90 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
- // Called once following the conclusion of a restore operation. This is when we
+ // Called once following the conclusion of a system restore operation. This is when we
// send out updates to apps involved in widget-state restore telling them about
- // the new widget ID space.
- public void restoreFinished(int userId) {
+ // the new widget ID space. Apps that are not yet installed will be notifed when they are.
+ public void systemRestoreFinished(int userId) {
if (DEBUG) {
- Slog.i(TAG, "restoreFinished for " + userId);
+ Slog.i(TAG, "systemRestoreFinished for " + userId);
}
+ synchronized (mLock) {
+ mHasSystemRestoreFinished = true;
+ maybeSendWidgetRestoreBroadcastsLocked(userId);
+ }
+ }
- final UserHandle userHandle = new UserHandle(userId);
+ // Called when widget components (hosts or providers) are added or changed. If system
+ // restore has completed, we use this opportunity to tell the apps to update to the new
+ // widget ID space. If system restore is still in progress, we delay the updates until
+ // the end, to allow all participants to restore their state before updating widget IDs.
+ public void widgetComponentsChanged(int userId) {
synchronized (mLock) {
- // Build the providers' broadcasts and send them off
- Set<Map.Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
- = mUpdatesByProvider.entrySet();
- for (Map.Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
- // For each provider there's a list of affected IDs
- Provider provider = e.getKey();
+ if (mHasSystemRestoreFinished) {
+ maybeSendWidgetRestoreBroadcastsLocked(userId);
+ }
+ }
+ }
+
+ // Called following the conclusion of a restore operation and when widget components
+ // are added or changed. This is when we send out updates to apps involved in widget-state
+ // restore telling them about the new widget ID space.
+ @GuardedBy("mLock")
+ private void maybeSendWidgetRestoreBroadcastsLocked(int userId) {
+ if (DEBUG) {
+ Slog.i(TAG, "maybeSendWidgetRestoreBroadcasts for " + userId);
+ }
+
+ final UserHandle userHandle = new UserHandle(userId);
+ // Build the providers' broadcasts and send them off
+ Set<Map.Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
+ = mUpdatesByProvider.entrySet();
+ for (Map.Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
+ // For each provider there's a list of affected IDs
+ Provider provider = e.getKey();
+ if (provider.zombie) {
+ // Provider not installed, we can't send them broadcasts yet.
+ // We'll be called again when the provider is installed.
+ continue;
+ }
+ ArrayList<RestoreUpdateRecord> updates = e.getValue();
+ final int pending = countPendingUpdates(updates);
+ if (DEBUG) {
+ Slog.i(TAG, "Provider " + provider + " pending: " + pending);
+ }
+ if (pending > 0) {
+ int[] oldIds = new int[pending];
+ int[] newIds = new int[pending];
+ final int N = updates.size();
+ int nextPending = 0;
+ for (int i = 0; i < N; i++) {
+ RestoreUpdateRecord r = updates.get(i);
+ if (!r.notified) {
+ r.notified = true;
+ oldIds[nextPending] = r.oldId;
+ newIds[nextPending] = r.newId;
+ nextPending++;
+ if (DEBUG) {
+ Slog.i(TAG, " " + r.oldId + " => " + r.newId);
+ }
+ }
+ }
+ sendWidgetRestoreBroadcastLocked(
+ AppWidgetManager.ACTION_APPWIDGET_RESTORED,
+ provider, null, oldIds, newIds, userHandle);
+ }
+ }
+
+ // same thing per host
+ Set<Map.Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
+ = mUpdatesByHost.entrySet();
+ for (Map.Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
+ Host host = e.getKey();
+ if (host.id.uid != UNKNOWN_UID) {
ArrayList<RestoreUpdateRecord> updates = e.getValue();
final int pending = countPendingUpdates(updates);
if (DEBUG) {
- Slog.i(TAG, "Provider " + provider + " pending: " + pending);
+ Slog.i(TAG, "Host " + host + " pending: " + pending);
}
if (pending > 0) {
int[] oldIds = new int[pending];
@@ -4581,43 +4651,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
sendWidgetRestoreBroadcastLocked(
- AppWidgetManager.ACTION_APPWIDGET_RESTORED,
- provider, null, oldIds, newIds, userHandle);
- }
- }
-
- // same thing per host
- Set<Map.Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
- = mUpdatesByHost.entrySet();
- for (Map.Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
- Host host = e.getKey();
- if (host.id.uid != UNKNOWN_UID) {
- ArrayList<RestoreUpdateRecord> updates = e.getValue();
- final int pending = countPendingUpdates(updates);
- if (DEBUG) {
- Slog.i(TAG, "Host " + host + " pending: " + pending);
- }
- if (pending > 0) {
- int[] oldIds = new int[pending];
- int[] newIds = new int[pending];
- final int N = updates.size();
- int nextPending = 0;
- for (int i = 0; i < N; i++) {
- RestoreUpdateRecord r = updates.get(i);
- if (!r.notified) {
- r.notified = true;
- oldIds[nextPending] = r.oldId;
- newIds[nextPending] = r.newId;
- nextPending++;
- if (DEBUG) {
- Slog.i(TAG, " " + r.oldId + " => " + r.newId);
- }
- }
- }
- sendWidgetRestoreBroadcastLocked(
- AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED,
- null, host, oldIds, newIds, userHandle);
- }
+ AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED,
+ null, host, oldIds, newIds, userHandle);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 261ebe69a15e..f07bac8cd762 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -381,7 +381,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// If we're starting a full-system restore, set up to begin widget ID remapping
if (mIsSystemRestore) {
- AppWidgetBackupBridge.restoreStarting(mUserId);
+ AppWidgetBackupBridge.systemRestoreStarting(mUserId);
}
try {
@@ -1133,8 +1133,10 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
restoreAgentTimeoutMillis);
}
- // Kick off any work that may be needed regarding app widget restores
- AppWidgetBackupBridge.restoreFinished(mUserId);
+ if (mIsSystemRestore) {
+ // Kick off any work that may be needed regarding app widget restores
+ AppWidgetBackupBridge.systemRestoreFinished(mUserId);
+ }
// If this was a full-system restore, record the ancestral
// dataset information
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 92996241a3a6..f7ddd2922b25 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1151,6 +1151,8 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
/**
* Deletes the OAT artifacts of a package.
+ * @param packageName a specific package
+ * @return the number of freed bytes or -1 if there was an error in the process.
*/
- public abstract void deleteOatArtifactsOfPackage(String packageName);
+ public abstract long deleteOatArtifactsOfPackage(String packageName);
}
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 26ecee8f21ab..70176a0fefeb 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -38,6 +38,7 @@ import android.net.VpnManager;
import android.net.VpnService;
import android.net.util.NetdService;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.INetworkManagementService;
@@ -348,9 +349,17 @@ public class VpnManagerService extends IVpnManager.Stub {
/**
* Start legacy VPN, controlling native daemons as needed. Creates a
* secondary thread to perform connection work, returning quickly.
+ *
+ * Legacy VPN is deprecated starting from Android S. So this API shouldn't be called if the
+ * initial SDK version of device is Android S+. Otherwise, UnsupportedOperationException will be
+ * thrown.
*/
+ @SuppressWarnings("AndroidFrameworkCompatChange") // This is not an app-visible API.
@Override
public void startLegacyVpn(VpnProfile profile) {
+ if (Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.S) {
+ throw new UnsupportedOperationException("Legacy VPN is deprecated");
+ }
int user = UserHandle.getUserId(mDeps.getCallingUid());
// Note that if the caller is not system (uid >= Process.FIRST_APPLICATION_UID),
// the code might not work well since getActiveNetwork might return null if the uid is
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 966e746b3c73..317b7757c239 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -203,8 +203,6 @@ public final class CachedAppOptimizer {
updateMinOomAdjThrottle();
} else if (KEY_COMPACT_THROTTLE_MAX_OOM_ADJ.equals(name)) {
updateMaxOomAdjThrottle();
- } else if (KEY_FREEZER_DEBOUNCE_TIMEOUT.equals(name)) {
- updateFreezerDebounceTimeout();
}
}
}
@@ -344,7 +342,6 @@ public final class CachedAppOptimizer {
updateUseFreezer();
updateMinOomAdjThrottle();
updateMaxOomAdjThrottle();
- updateFreezerDebounceTimeout();
}
}
@@ -656,6 +653,7 @@ public final class CachedAppOptimizer {
|| DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {
mUseFreezer = isFreezerSupported();
+ updateFreezerDebounceTimeout();
}
final boolean useFreezer = mUseFreezer;
@@ -834,7 +832,8 @@ public final class CachedAppOptimizer {
@GuardedBy("mPhenotypeFlagLock")
private void updateFreezerDebounceTimeout() {
- mFreezerDebounceTimeout = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ mFreezerDebounceTimeout = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
KEY_FREEZER_DEBOUNCE_TIMEOUT, DEFAULT_FREEZER_DEBOUNCE_TIMEOUT);
if (mFreezerDebounceTimeout < 0) {
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 4cb2e9b5cb97..7f1402d83a0c 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -456,7 +456,9 @@ public final class AppHibernationService extends SystemService {
private void hibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally");
if (mOatArtifactDeletionEnabled) {
- mPackageManagerInternal.deleteOatArtifactsOfPackage(packageName);
+ state.savedByte = Math.max(
+ mPackageManagerInternal.deleteOatArtifactsOfPackage(packageName),
+ 0);
}
state.hibernated = true;
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -469,6 +471,7 @@ public final class AppHibernationService extends SystemService {
private void unhibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally");
state.hibernated = false;
+ state.savedByte = 0;
state.lastUnhibernatedMs = System.currentTimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
@@ -943,6 +946,9 @@ public final class AppHibernationService extends SystemService {
}
private final class StatsPullAtomCallbackImpl implements StatsPullAtomCallback {
+
+ private static final int MEGABYTE_IN_BYTES = 1000000;
+
@Override
public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
if (!isAppHibernationEnabled()
@@ -969,12 +975,21 @@ public final class AppHibernationService extends SystemService {
break;
case FrameworkStatsLog.GLOBAL_HIBERNATED_APPS:
int hibernatedAppCount = 0;
+ long storage_saved_byte = 0;
synchronized (mLock) {
for (GlobalLevelState state : mGlobalHibernationStates.values()) {
- if (state.hibernated) hibernatedAppCount++;
+ if (state.hibernated) {
+ hibernatedAppCount++;
+ storage_saved_byte += state.savedByte;
+ }
}
}
- data.add(FrameworkStatsLog.buildStatsEvent(atomTag, hibernatedAppCount));
+ data.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ hibernatedAppCount,
+ storage_saved_byte / MEGABYTE_IN_BYTES)
+ );
break;
default:
return StatsManager.PULL_SKIP;
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
index 79e995b038fa..018d6023f083 100644
--- a/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
@@ -41,6 +41,7 @@ final class GlobalLevelHibernationProto implements ProtoReadWriter<List<GlobalLe
GlobalLevelState state = data.get(i);
stream.write(GlobalLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
stream.write(GlobalLevelHibernationStateProto.HIBERNATED, state.hibernated);
+ stream.write(GlobalLevelHibernationStateProto.SAVED_BYTE, state.savedByte);
stream.end(token);
}
}
@@ -66,6 +67,10 @@ final class GlobalLevelHibernationProto implements ProtoReadWriter<List<GlobalLe
state.hibernated =
stream.readBoolean(GlobalLevelHibernationStateProto.HIBERNATED);
break;
+ case (int) GlobalLevelHibernationStateProto.SAVED_BYTE:
+ state.savedByte =
+ stream.readLong(GlobalLevelHibernationStateProto.SAVED_BYTE);
+ break;
default:
Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
}
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelState.java b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
index f4433a7fa5f6..104ecbc17d8a 100644
--- a/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
@@ -29,6 +29,8 @@ final class GlobalLevelState {
public String packageName;
public boolean hibernated;
+ // The number of saved bytes from the current hibernation. It will be 0 if not in hibernation.
+ public long savedByte;
@CurrentTimeMillisLong
public long lastUnhibernatedMs;
@@ -37,6 +39,7 @@ final class GlobalLevelState {
return "GlobalLevelState{"
+ "packageName='" + packageName + '\''
+ ", hibernated=" + hibernated + '\''
+ + ", savedByte=" + savedByte + '\''
+ ", lastUnhibernated=" + DATE_FORMAT.format(lastUnhibernatedMs)
+ '}';
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 99a33e4462e2..5ba75d3ac312 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -19,11 +19,13 @@ package com.android.server.appop;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP;
import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
import static android.app.AppOpsManager.FILTER_BY_UID;
+import static android.app.AppOpsManager.HISTORY_FLAG_GET_ATTRIBUTION_CHAINS;
import static android.app.AppOpsManager.HistoricalOpsRequestFilter;
import static android.app.AppOpsManager.KEY_BG_STATE_SETTLE_TIME;
import static android.app.AppOpsManager.KEY_FG_SERVICE_STATE_SETTLE_TIME;
@@ -130,6 +132,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManagerInternal;
+import android.permission.PermissionManager;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -200,8 +203,8 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Scanner;
+import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
public class AppOpsService extends IAppOpsService.Stub {
@@ -2357,10 +2360,21 @@ public class AppOpsService extends IAppOpsService.Stub {
final String[] opNamesArray = (opNames != null)
? opNames.toArray(new String[opNames.size()]) : null;
+ Set<String> attributionChainExemptPackages = null;
+ if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) {
+ attributionChainExemptPackages =
+ PermissionManager.getIndicatorExemptedPackages(mContext);
+ }
+
+ final String[] chainExemptPkgArray = attributionChainExemptPackages != null
+ ? attributionChainExemptPackages.toArray(
+ new String[attributionChainExemptPackages.size()]) : null;
+
// Must not hold the appops lock
mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps,
mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
- filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
+ filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
+ callback).recycleOnUse());
}
@Override
@@ -2377,10 +2391,21 @@ public class AppOpsService extends IAppOpsService.Stub {
final String[] opNamesArray = (opNames != null)
? opNames.toArray(new String[opNames.size()]) : null;
+ Set<String> attributionChainExemptPackages = null;
+ if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) {
+ attributionChainExemptPackages =
+ PermissionManager.getIndicatorExemptedPackages(mContext);
+ }
+
+ final String[] chainExemptPkgArray = attributionChainExemptPackages != null
+ ? attributionChainExemptPackages.toArray(
+ new String[attributionChainExemptPackages.size()]) : null;
+
// Must not hold the appops lock
mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw,
mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
- filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
+ filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
+ callback).recycleOnUse());
}
@Override
@@ -3838,7 +3863,8 @@ public class AppOpsService extends IAppOpsService.Stub {
final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
final boolean isProxyTrusted = mContext.checkPermission(
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
- == PackageManager.PERMISSION_GRANTED || isSelfBlame;
+ == PackageManager.PERMISSION_GRANTED || isSelfBlame
+ || attributionChainId != ATTRIBUTION_CHAIN_ID_NONE;
String resolvedProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
proxiedPackageName);
@@ -5174,8 +5200,6 @@ public class AppOpsService extends IAppOpsService.Stub {
}
static class Shell extends ShellCommand {
- static final AtomicInteger sAttributionChainIds = new AtomicInteger(0);
-
final IAppOpsService mInterface;
final AppOpsService mInternal;
@@ -5645,8 +5669,7 @@ public class AppOpsService extends IAppOpsService.Stub {
shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid,
shell.packageName, shell.attributionTag, true, true,
"appops start shell command", true,
- AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR,
- shell.sAttributionChainIds.incrementAndGet());
+ AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR, ATTRIBUTION_CHAIN_ID_NONE);
} else {
return -1;
}
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 49469cc8a597..8b2d9e7d071d 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -16,6 +16,9 @@
package com.android.server.appop;
+import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
@@ -58,7 +61,9 @@ import com.android.internal.util.XmlUtils;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.time.Duration;
@@ -69,6 +74,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
/**
* This class manages information about recent accesses to ops for permission usage timeline.
@@ -138,6 +145,7 @@ final class DiscreteRegistry {
private static final String TAG_HISTORY = "h";
private static final String ATTR_VERSION = "v";
+ private static final String ATTR_LARGEST_CHAIN_ID = "lc";
private static final int CURRENT_VERSION = 1;
private static final String TAG_UID = "u";
@@ -182,6 +190,16 @@ final class DiscreteRegistry {
DiscreteRegistry(Object inMemoryLock) {
mInMemoryLock = inMemoryLock;
+ synchronized (mOnDiskLock) {
+ mDiscreteAccessDir = new File(
+ new File(Environment.getDataSystemDirectory(), "appops"),
+ "discrete");
+ createDiscreteAccessDirLocked();
+ int largestChainId = readLargestChainIdFromDiskLocked();
+ synchronized (mInMemoryLock) {
+ mDiscreteOps = new DiscreteOps(largestChainId);
+ }
+ }
}
void systemReady() {
@@ -190,15 +208,6 @@ final class DiscreteRegistry {
setDiscreteHistoryParameters(p);
});
setDiscreteHistoryParameters(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_PRIVACY));
- synchronized (mOnDiskLock) {
- synchronized (mInMemoryLock) {
- mDiscreteAccessDir = new File(
- new File(Environment.getDataSystemDirectory(), "appops"),
- "discrete");
- createDiscreteAccessDirLocked();
- mDiscreteOps = new DiscreteOps();
- }
- }
}
private void setDiscreteHistoryParameters(DeviceConfig.Properties p) {
@@ -251,7 +260,7 @@ final class DiscreteRegistry {
DiscreteOps discreteOps;
synchronized (mInMemoryLock) {
discreteOps = mDiscreteOps;
- mDiscreteOps = new DiscreteOps();
+ mDiscreteOps = new DiscreteOps(discreteOps.mChainIdOffset);
mCachedOps = null;
}
deleteOldDiscreteHistoryFilesLocked();
@@ -265,16 +274,109 @@ final class DiscreteRegistry {
long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ Set<String> attributionExemptPkgs) {
+ boolean assembleChains = attributionExemptPkgs != null;
DiscreteOps discreteOps = getAllDiscreteOps();
+ ArrayMap<Integer, AttributionChain> attributionChains = new ArrayMap<>();
+ if (assembleChains) {
+ attributionChains = createAttributionChains(discreteOps, attributionExemptPkgs);
+ }
beginTimeMillis = max(beginTimeMillis, Instant.now().minus(sDiscreteHistoryCutoff,
ChronoUnit.MILLIS).toEpochMilli());
discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
- opNamesFilter, attributionTagFilter, flagsFilter);
- discreteOps.applyToHistoricalOps(result);
+ opNamesFilter, attributionTagFilter, flagsFilter, attributionChains);
+ discreteOps.applyToHistoricalOps(result, attributionChains);
return;
}
+ private int readLargestChainIdFromDiskLocked() {
+ final File[] files = mDiscreteAccessDir.listFiles();
+ if (files != null && files.length > 0) {
+ File latestFile = null;
+ long latestFileTimestamp = 0;
+ for (File f : files) {
+ final String fileName = f.getName();
+ if (!fileName.endsWith(DISCRETE_HISTORY_FILE_SUFFIX)) {
+ continue;
+ }
+ long timestamp = Long.valueOf(fileName.substring(0,
+ fileName.length() - DISCRETE_HISTORY_FILE_SUFFIX.length()));
+ if (latestFileTimestamp < timestamp) {
+ latestFile = f;
+ latestFileTimestamp = timestamp;
+ }
+ }
+ if (latestFile == null) {
+ return 0;
+ }
+ FileInputStream stream;
+ try {
+ stream = new FileInputStream(latestFile);
+ } catch (FileNotFoundException e) {
+ return 0;
+ }
+ try {
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
+ XmlUtils.beginDocument(parser, TAG_HISTORY);
+
+ final int largestChainId = parser.getAttributeInt(null, ATTR_LARGEST_CHAIN_ID, 0);
+ return largestChainId;
+ } catch (Throwable t) {
+ return 0;
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ private ArrayMap<Integer, AttributionChain> createAttributionChains(
+ DiscreteOps discreteOps, Set<String> attributionExemptPkgs) {
+ ArrayMap<Integer, AttributionChain> chains = new ArrayMap<>();
+ int nUids = discreteOps.mUids.size();
+ for (int uidNum = 0; uidNum < nUids; uidNum++) {
+ ArrayMap<String, DiscretePackageOps> pkgs = discreteOps.mUids.valueAt(uidNum).mPackages;
+ int uid = discreteOps.mUids.keyAt(uidNum);
+ int nPackages = pkgs.size();
+ for (int pkgNum = 0; pkgNum < nPackages; pkgNum++) {
+ ArrayMap<Integer, DiscreteOp> ops = pkgs.valueAt(pkgNum).mPackageOps;
+ String pkg = pkgs.keyAt(pkgNum);
+ int nOps = ops.size();
+ for (int opNum = 0; opNum < nOps; opNum++) {
+ ArrayMap<String, List<DiscreteOpEvent>> attrOps =
+ ops.valueAt(opNum).mAttributedOps;
+ int op = ops.keyAt(opNum);
+ int nAttrOps = attrOps.size();
+ for (int attrOpNum = 0; attrOpNum < nAttrOps; attrOpNum++) {
+ List<DiscreteOpEvent> opEvents = attrOps.valueAt(attrOpNum);
+ String attributionTag = attrOps.keyAt(attrOpNum);
+ int nOpEvents = opEvents.size();
+ for (int opEventNum = 0; opEventNum < nOpEvents; opEventNum++) {
+ DiscreteOpEvent event = opEvents.get(opEventNum);
+ if (event == null
+ || event.mAttributionChainId == ATTRIBUTION_CHAIN_ID_NONE) {
+ continue;
+ }
+
+ if (!chains.containsKey(event.mAttributionChainId)) {
+ chains.put(event.mAttributionChainId,
+ new AttributionChain(attributionExemptPkgs));
+ }
+ chains.get(event.mAttributionChainId)
+ .addEvent(pkg, uid, attributionTag, op, event);
+ }
+ }
+ }
+ }
+ }
+ return chains;
+ }
+
private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) {
synchronized (mOnDiskLock) {
long beginTimeMillis = Instant.now().minus(sDiscreteHistoryCutoff,
@@ -301,7 +403,7 @@ final class DiscreteRegistry {
void clearHistory() {
synchronized (mOnDiskLock) {
synchronized (mInMemoryLock) {
- mDiscreteOps = new DiscreteOps();
+ mDiscreteOps = new DiscreteOps(0);
}
clearOnDiskHistoryLocked();
}
@@ -340,7 +442,11 @@ final class DiscreteRegistry {
String[] opNamesFilter = dumpOp == OP_NONE ? null
: new String[]{AppOpsManager.opToPublicName(dumpOp)};
discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter,
- opNamesFilter, attributionTagFilter, OP_FLAGS_ALL);
+ opNamesFilter, attributionTagFilter, OP_FLAGS_ALL, new ArrayMap<>());
+ pw.print(prefix);
+ pw.print("Largest chain id: ");
+ pw.print(mDiscreteOps.mLargestChainId);
+ pw.println();
discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
}
@@ -351,14 +457,14 @@ final class DiscreteRegistry {
}
private DiscreteOps getAllDiscreteOps() {
- DiscreteOps discreteOps = new DiscreteOps();
+ DiscreteOps discreteOps = new DiscreteOps(0);
synchronized (mOnDiskLock) {
synchronized (mInMemoryLock) {
discreteOps.merge(mDiscreteOps);
}
if (mCachedOps == null) {
- mCachedOps = new DiscreteOps();
+ mCachedOps = new DiscreteOps(0);
readDiscreteOpsFromDisk(mCachedOps);
}
discreteOps.merge(mCachedOps);
@@ -366,11 +472,143 @@ final class DiscreteRegistry {
}
}
+ /**
+ * Represents a chain of usages, each attributing its usage to the one before it
+ */
+ private static final class AttributionChain {
+ private static final class OpEvent {
+ String mPkgName;
+ int mUid;
+ String mAttributionTag;
+ int mOpCode;
+ DiscreteOpEvent mOpEvent;
+
+ OpEvent(String pkgName, int uid, String attributionTag, int opCode,
+ DiscreteOpEvent event) {
+ mPkgName = pkgName;
+ mUid = uid;
+ mAttributionTag = attributionTag;
+ mOpCode = opCode;
+ mOpEvent = event;
+ }
+
+ public boolean matches(String pkgName, int uid, String attributionTag, int opCode,
+ DiscreteOpEvent event) {
+ return Objects.equals(pkgName, mPkgName) && mUid == uid
+ && Objects.equals(attributionTag, mAttributionTag) && mOpCode == opCode
+ && mOpEvent.mAttributionChainId == event.mAttributionChainId
+ && mOpEvent.mAttributionFlags == event.mAttributionFlags
+ && mOpEvent.mNoteTime == event.mNoteTime;
+ }
+
+ public boolean packageOpEquals(OpEvent other) {
+ return Objects.equals(other.mPkgName, mPkgName) && other.mUid == mUid
+ && Objects.equals(other.mAttributionTag, mAttributionTag)
+ && mOpCode == other.mOpCode;
+ }
+
+ public boolean equalsExceptDuration(OpEvent other) {
+ if (other.mOpEvent.mNoteDuration == mOpEvent.mNoteDuration) {
+ return false;
+ }
+ return packageOpEquals(other) && mOpEvent.equalsExceptDuration(other.mOpEvent);
+ }
+ }
+
+ ArrayList<OpEvent> mChain = new ArrayList<>();
+ Set<String> mExemptPkgs;
+ OpEvent mStartEvent = null;
+ OpEvent mLastVisibleEvent = null;
+
+ AttributionChain(Set<String> exemptPkgs) {
+ mExemptPkgs = exemptPkgs;
+ }
+
+ boolean isComplete() {
+ return !mChain.isEmpty() && getStart() != null && isEnd(mChain.get(mChain.size() - 1));
+ }
+
+ boolean isStart(String pkgName, int uid, String attributionTag, int op,
+ DiscreteOpEvent opEvent) {
+ if (mStartEvent == null || opEvent == null) {
+ return false;
+ }
+ return mStartEvent.matches(pkgName, uid, attributionTag, op, opEvent);
+ }
+
+ private OpEvent getStart() {
+ return mChain.isEmpty() || !isStart(mChain.get(0)) ? null : mChain.get(0);
+ }
+
+ private OpEvent getLastVisible() {
+ // Search all nodes but the first one, which is the start node
+ for (int i = mChain.size() - 1; i > 0; i++) {
+ OpEvent event = mChain.get(i);
+ if (!mExemptPkgs.contains(event.mPkgName)) {
+ return event;
+ }
+ }
+ return null;
+ }
+
+ void addEvent(String pkgName, int uid, String attributionTag, int op,
+ DiscreteOpEvent opEvent) {
+ OpEvent event = new OpEvent(pkgName, uid, attributionTag, op, opEvent);
+
+ // check if we have a matching event, without duration, replacing duration otherwise
+ for (int i = 0; i < mChain.size(); i++) {
+ OpEvent item = mChain.get(i);
+ if (item.equalsExceptDuration(event)) {
+ if (event.mOpEvent.mNoteDuration != -1) {
+ item.mOpEvent = event.mOpEvent;
+ }
+ return;
+ }
+ }
+
+ if (mChain.isEmpty() || isEnd(event)) {
+ mChain.add(event);
+ } else if (isStart(event)) {
+ mChain.add(0, event);
+
+ } else {
+ for (int i = 0; i < mChain.size(); i++) {
+ OpEvent currEvent = mChain.get(i);
+ if ((!isStart(currEvent)
+ && currEvent.mOpEvent.mNoteTime > event.mOpEvent.mNoteTime)
+ || i == mChain.size() - 1 && isEnd(currEvent)) {
+ mChain.add(i, event);
+ break;
+ } else if (i == mChain.size() - 1) {
+ mChain.add(event);
+ break;
+ }
+ }
+ }
+ mStartEvent = isComplete() ? getStart() : null;
+ mLastVisibleEvent = isComplete() ? getLastVisible() : null;
+ }
+
+ private boolean isEnd(OpEvent event) {
+ return event != null
+ && (event.mOpEvent.mAttributionFlags & ATTRIBUTION_FLAG_ACCESSOR) != 0;
+ }
+
+ private boolean isStart(OpEvent event) {
+ return event != null
+ && (event.mOpEvent.mAttributionFlags & ATTRIBUTION_FLAG_RECEIVER) != 0;
+ }
+ }
+
private final class DiscreteOps {
ArrayMap<Integer, DiscreteUidOps> mUids;
+ int mChainIdOffset;
+ int mLargestChainId;
- DiscreteOps() {
+ DiscreteOps(int chainIdOffset) {
mUids = new ArrayMap<>();
+ mChainIdOffset = chainIdOffset;
+ mLargestChainId = chainIdOffset;
}
boolean isEmpty() {
@@ -378,6 +616,7 @@ final class DiscreteRegistry {
}
void merge(DiscreteOps other) {
+ mLargestChainId = max(mLargestChainId, other.mLargestChainId);
int nUids = other.mUids.size();
for (int i = 0; i < nUids; i++) {
int uid = other.mUids.keyAt(i);
@@ -390,14 +629,27 @@ final class DiscreteRegistry {
@Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
@AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
@AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
+ int offsetChainId = attributionChainId;
+ if (attributionChainId != ATTRIBUTION_CHAIN_ID_NONE) {
+ offsetChainId = attributionChainId + mChainIdOffset;
+ if (offsetChainId > mLargestChainId) {
+ mLargestChainId = offsetChainId;
+ } else if (offsetChainId < 0) {
+ // handle overflow
+ offsetChainId = 0;
+ mLargestChainId = 0;
+ mChainIdOffset = -1 * attributionChainId;
+ }
+ }
getOrCreateDiscreteUidOps(uid).addDiscreteAccess(op, packageName, attributionTag, flags,
- uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
+ uidState, accessTime, accessDuration, attributionFlags, offsetChainId);
}
private void filter(long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
if ((filter & FILTER_BY_UID) != 0) {
ArrayMap<Integer, DiscreteUidOps> uids = new ArrayMap<>();
uids.put(uidFilter, getOrCreateDiscreteUidOps(uidFilter));
@@ -406,7 +658,8 @@ final class DiscreteRegistry {
int nUids = mUids.size();
for (int i = nUids - 1; i >= 0; i--) {
mUids.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, packageNameFilter,
- opNamesFilter, attributionTagFilter, flagsFilter);
+ opNamesFilter, attributionTagFilter, flagsFilter, mUids.keyAt(i),
+ attributionChains);
if (mUids.valueAt(i).isEmpty()) {
mUids.removeAt(i);
}
@@ -429,10 +682,11 @@ final class DiscreteRegistry {
}
}
- private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) {
+ private void applyToHistoricalOps(AppOpsManager.HistoricalOps result,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
int nUids = mUids.size();
for (int i = 0; i < nUids; i++) {
- mUids.valueAt(i).applyToHistory(result, mUids.keyAt(i));
+ mUids.valueAt(i).applyToHistory(result, mUids.keyAt(i), attributionChains);
}
}
@@ -442,6 +696,7 @@ final class DiscreteRegistry {
out.startDocument(null, true);
out.startTag(null, TAG_HISTORY);
out.attributeInt(null, ATTR_VERSION, CURRENT_VERSION);
+ out.attributeInt(null, ATTR_LARGEST_CHAIN_ID, mLargestChainId);
int nUids = mUids.size();
for (int i = 0; i < nUids; i++) {
@@ -476,8 +731,13 @@ final class DiscreteRegistry {
}
private void readFromFile(File f, long beginTimeMillis) {
+ FileInputStream stream;
+ try {
+ stream = new FileInputStream(f);
+ } catch (FileNotFoundException e) {
+ return;
+ }
try {
- FileInputStream stream = new FileInputStream(f);
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
XmlUtils.beginDocument(parser, TAG_HISTORY);
@@ -487,7 +747,6 @@ final class DiscreteRegistry {
if (version != CURRENT_VERSION) {
throw new IllegalStateException("Dropping unsupported discrete history " + f);
}
-
int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_UID.equals(parser.getName())) {
@@ -498,8 +757,12 @@ final class DiscreteRegistry {
} catch (Throwable t) {
Slog.e(TAG, "Failed to read file " + f.getName() + " " + t.getMessage() + " "
+ Arrays.toString(t.getStackTrace()));
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
}
-
}
}
@@ -590,7 +853,8 @@ final class DiscreteRegistry {
private void filter(long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter,
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ int currentUid, ArrayMap<Integer, AttributionChain> attributionChains) {
if ((filter & FILTER_BY_PACKAGE_NAME) != 0) {
ArrayMap<String, DiscretePackageOps> packages = new ArrayMap<>();
packages.put(packageNameFilter, getOrCreateDiscretePackageOps(packageNameFilter));
@@ -599,7 +863,8 @@ final class DiscreteRegistry {
int nPackages = mPackages.size();
for (int i = nPackages - 1; i >= 0; i--) {
mPackages.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, opNamesFilter,
- attributionTagFilter, flagsFilter);
+ attributionTagFilter, flagsFilter, currentUid, mPackages.keyAt(i),
+ attributionChains);
if (mPackages.valueAt(i).isEmpty()) {
mPackages.removeAt(i);
}
@@ -634,10 +899,12 @@ final class DiscreteRegistry {
return result;
}
- private void applyToHistory(AppOpsManager.HistoricalOps result, int uid) {
+ private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
+ @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
int nPackages = mPackages.size();
for (int i = 0; i < nPackages; i++) {
- mPackages.valueAt(i).applyToHistory(result, uid, mPackages.keyAt(i));
+ mPackages.valueAt(i).applyToHistory(result, uid, mPackages.keyAt(i),
+ attributionChains);
}
}
@@ -705,7 +972,8 @@ final class DiscreteRegistry {
private void filter(long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter,
@Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
- @AppOpsManager.OpFlags int flagsFilter) {
+ @AppOpsManager.OpFlags int flagsFilter, int currentUid, String currentPkgName,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
int nOps = mPackageOps.size();
for (int i = nOps - 1; i >= 0; i--) {
int opId = mPackageOps.keyAt(i);
@@ -715,7 +983,8 @@ final class DiscreteRegistry {
continue;
}
mPackageOps.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter,
- attributionTagFilter, flagsFilter);
+ attributionTagFilter, flagsFilter, currentUid, currentPkgName,
+ mPackageOps.keyAt(i), attributionChains);
if (mPackageOps.valueAt(i).isEmpty()) {
mPackageOps.removeAt(i);
}
@@ -739,11 +1008,12 @@ final class DiscreteRegistry {
}
private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
- @NonNull String packageName) {
+ @NonNull String packageName,
+ @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
int nPackageOps = mPackageOps.size();
for (int i = 0; i < nPackageOps; i++) {
mPackageOps.valueAt(i).applyToHistory(result, uid, packageName,
- mPackageOps.keyAt(i));
+ mPackageOps.keyAt(i), attributionChains);
}
}
@@ -802,7 +1072,9 @@ final class DiscreteRegistry {
private void filter(long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ int currentUid, String currentPkgName, int currentOp,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0) {
ArrayMap<String, List<DiscreteOpEvent>> attributedOps = new ArrayMap<>();
attributedOps.put(attributionTagFilter,
@@ -814,7 +1086,9 @@ final class DiscreteRegistry {
for (int i = nTags - 1; i >= 0; i--) {
String tag = mAttributedOps.keyAt(i);
List<DiscreteOpEvent> list = mAttributedOps.valueAt(i);
- list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter);
+ list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter,
+ currentUid, currentPkgName, currentOp, mAttributedOps.keyAt(i),
+ attributionChains);
mAttributedOps.put(tag, list);
if (list.size() == 0) {
mAttributedOps.removeAt(i);
@@ -876,7 +1150,8 @@ final class DiscreteRegistry {
}
private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
- @NonNull String packageName, int op) {
+ @NonNull String packageName, int op,
+ @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
int nOps = mAttributedOps.size();
for (int i = 0; i < nOps; i++) {
String tag = mAttributedOps.keyAt(i);
@@ -884,9 +1159,21 @@ final class DiscreteRegistry {
int nEvents = events.size();
for (int j = 0; j < nEvents; j++) {
DiscreteOpEvent event = events.get(j);
+ AppOpsManager.OpEventProxyInfo proxy = null;
+ if (event.mAttributionChainId != ATTRIBUTION_CHAIN_ID_NONE
+ && attributionChains != null) {
+ AttributionChain chain = attributionChains.get(event.mAttributionChainId);
+ if (chain != null && chain.isComplete()
+ && chain.isStart(packageName, uid, tag, op, event)
+ && chain.mLastVisibleEvent != null) {
+ AttributionChain.OpEvent proxyEvent = chain.mLastVisibleEvent;
+ proxy = new AppOpsManager.OpEventProxyInfo(proxyEvent.mUid,
+ proxyEvent.mPkgName, proxyEvent.mAttributionTag);
+ }
+ }
result.addDiscreteAccess(op, uid, packageName, tag, event.mUidState,
event.mOpFlag, discretizeTimeStamp(event.mNoteTime),
- discretizeDuration(event.mNoteDuration));
+ discretizeDuration(event.mNoteDuration), proxy);
}
}
}
@@ -981,6 +1268,13 @@ final class DiscreteRegistry {
mAttributionChainId = attributionChainId;
}
+ public boolean equalsExceptDuration(DiscreteOpEvent o) {
+ return mNoteTime == o.mNoteTime && mUidState == o.mUidState && mOpFlag == o.mOpFlag
+ && mAttributionFlags == o.mAttributionFlags
+ && mAttributionChainId == o.mAttributionChainId;
+
+ }
+
private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
@NonNull Date date, @NonNull String prefix) {
pw.print(prefix);
@@ -1063,11 +1357,20 @@ final class DiscreteRegistry {
}
private static List<DiscreteOpEvent> filterEventsList(List<DiscreteOpEvent> list,
- long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter) {
+ long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter,
+ int currentUid, String currentPackageName, int currentOp, String currentAttrTag,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
int n = list.size();
List<DiscreteOpEvent> result = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
DiscreteOpEvent event = list.get(i);
+ AttributionChain chain = attributionChains.get(event.mAttributionChainId);
+ // If we have an attribution chain, and this event isn't the beginning node, remove it
+ if (chain != null && !chain.isStart(currentPackageName, currentUid, currentAttrTag,
+ currentOp, event) && chain.isComplete()
+ && event.mAttributionChainId != ATTRIBUTION_CHAIN_ID_NONE) {
+ continue;
+ }
if ((event.mOpFlag & flagsFilter) != 0
&& event.mNoteTime + event.mNoteDuration > beginTimeMillis
&& event.mNoteTime < endTimeMillis) {
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index dd5df503d936..2c68aaf4f3e5 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -209,6 +209,7 @@ final class HistoricalRegistry {
mMode = other.mMode;
mBaseSnapshotInterval = other.mBaseSnapshotInterval;
mIntervalCompressionMultiplier = other.mIntervalCompressionMultiplier;
+ mDiscreteRegistry = other.mDiscreteRegistry;
}
void systemReady(@NonNull ContentResolver resolver) {
@@ -369,7 +370,7 @@ final class HistoricalRegistry {
@Nullable String attributionTag, @Nullable String[] opNames,
@OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter,
long beginTimeMillis, long endTimeMillis, @OpFlags int flags,
- @NonNull RemoteCallback callback) {
+ String[] attributionExemptedPackages, @NonNull RemoteCallback callback) {
if (!isApiEnabled()) {
callback.sendResult(new Bundle());
return;
@@ -395,7 +396,7 @@ final class HistoricalRegistry {
if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis,
endTimeMillis, filter, uid, packageName, opNames, attributionTag,
- flags);
+ flags, new ArraySet<>(attributionExemptedPackages));
}
final Bundle payload = new Bundle();
@@ -406,7 +407,8 @@ final class HistoricalRegistry {
void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag,
@Nullable String[] opNames, @OpHistoryFlags int historyFlags,
@HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis,
- @OpFlags int flags, @NonNull RemoteCallback callback) {
+ @OpFlags int flags, @Nullable String[] attributionExemptPkgs,
+ @NonNull RemoteCallback callback) {
if (!isApiEnabled()) {
callback.sendResult(new Bundle());
return;
@@ -428,7 +430,8 @@ final class HistoricalRegistry {
if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis,
- endTimeMillis, filter, uid, packageName, opNames, attributionTag, flags);
+ endTimeMillis, filter, uid, packageName, opNames, attributionTag, flags,
+ new ArraySet<>(attributionExemptPkgs));
}
if ((historyFlags & HISTORY_FLAG_AGGREGATE) != 0) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index a5bb0f430609..0981f184e143 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -866,7 +866,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
final boolean hasEnrolled = !getEnrolledFaces(mSensorId, targetUserId).isEmpty();
final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
- mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId, mCurrentUserId,
+ mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId,
hasEnrolled, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
index 70e20339ce98..5343d0d17273 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
@@ -34,38 +34,23 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace
private static final String TAG = "FaceUpdateActiveUserClient";
private static final String FACE_DATA_DIR = "facedata";
- private final int mCurrentUserId;
private final boolean mHasEnrolledBiometrics;
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
FaceUpdateActiveUserClient(@NonNull Context context,
- @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, int currentUserId, boolean hasEnrolledBIometrics,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
+ int sensorId, boolean hasEnrolledBiometrics,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
- mCurrentUserId = currentUserId;
- mHasEnrolledBiometrics = hasEnrolledBIometrics;
+ mHasEnrolledBiometrics = hasEnrolledBiometrics;
mAuthenticatorIds = authenticatorIds;
}
@Override
public void start(@NonNull Callback callback) {
super.start(callback);
-
- if (mCurrentUserId == getTargetUserId()) {
- Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId");
- try {
- mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
- ? getFreshDaemon().getAuthenticatorId().value : 0L);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to refresh authenticatorId", e);
- }
- callback.onClientFinished(this, true /* success */);
- return;
- }
-
startHalOperation();
}
@@ -85,7 +70,10 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace
}
try {
- getFreshDaemon().setActiveUser(getTargetUserId(), storePath.getAbsolutePath());
+ final IBiometricsFace daemon = getFreshDaemon();
+ daemon.setActiveUser(getTargetUserId(), storePath.getAbsolutePath());
+ mAuthenticatorIds.put(getTargetUserId(),
+ mHasEnrolledBiometrics ? daemon.getAuthenticatorId().value : 0L);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setActiveUser: " + e);
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index aeb1893c78b6..e6210b2a8cc9 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -45,6 +45,7 @@ import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.Geofence;
@@ -91,6 +92,7 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
@@ -134,6 +136,8 @@ import com.android.server.location.provider.PassiveLocationProvider;
import com.android.server.location.provider.PassiveLocationProviderManager;
import com.android.server.location.provider.StationaryThrottlingLocationProvider;
import com.android.server.location.provider.proxy.ProxyLocationProvider;
+import com.android.server.location.settings.LocationSettings;
+import com.android.server.location.settings.LocationUserSettings;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import java.io.FileDescriptor;
@@ -270,6 +274,8 @@ public class LocationManagerService extends ILocationManager.Stub implements
mGeofenceManager = new GeofenceManager(mContext, injector);
+ mInjector.getLocationSettings().registerLocationUserSettingsListener(
+ this::onLocationUserSettingsChanged);
mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
this::onLocationModeChanged);
mInjector.getSettingsHelper().addIgnoreSettingsAllowlistChangedListener(
@@ -476,6 +482,25 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
}
+ private void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ if (oldSettings.isAdasGnssLocationEnabled() != newSettings.isAdasGnssLocationEnabled()) {
+ boolean enabled = newSettings.isAdasGnssLocationEnabled();
+
+ if (D) {
+ Log.d(TAG, "[u" + userId + "] adas gnss location enabled = " + enabled);
+ }
+
+ EVENT_LOG.logAdasLocationEnabled(userId, enabled);
+
+ Intent intent = new Intent(LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED)
+ .putExtra(LocationManager.EXTRA_ADAS_GNSS_ENABLED, enabled)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+ }
+ }
+
private void onLocationModeChanged(int userId) {
boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
LocationManager.invalidateLocalLocationEnabledCaches();
@@ -661,7 +686,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
// clients in the system process must have an attribution tag set
Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
- request = validateLocationRequest(request, identity);
+ request = validateLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
@@ -687,7 +712,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
new IllegalArgumentException());
}
- request = validateLocationRequest(request, identity);
+ request = validateLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
@@ -725,7 +750,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
}
- request = validateLocationRequest(request, identity);
+ request = validateLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
@@ -734,33 +759,27 @@ public class LocationManagerService extends ILocationManager.Stub implements
manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent);
}
- private LocationRequest validateLocationRequest(LocationRequest request,
+ private LocationRequest validateLocationRequest(String provider, LocationRequest request,
CallerIdentity identity) {
+ // validate unsanitized request
if (!request.getWorkSource().isEmpty()) {
mContext.enforceCallingOrSelfPermission(
permission.UPDATE_DEVICE_STATS,
"setting a work source requires " + permission.UPDATE_DEVICE_STATS);
}
- if (request.isHiddenFromAppOps()) {
- mContext.enforceCallingOrSelfPermission(
- permission.UPDATE_APP_OPS_STATS,
- "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
- }
- if (request.isLocationSettingsIgnored()) {
- mContext.enforceCallingOrSelfPermission(
- permission.WRITE_SECURE_SETTINGS,
- "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
- }
+ // sanitize request
LocationRequest.Builder sanitized = new LocationRequest.Builder(request);
- if (CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
- if (request.isLowPower()) {
- mContext.enforceCallingOrSelfPermission(
- permission.LOCATION_HARDWARE,
- "low power request requires " + permission.LOCATION_HARDWARE);
- }
- } else {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && GPS_PROVIDER.equals(provider)
+ && ArrayUtils.contains(mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+ identity.getPackageName())) {
+ sanitized.setAdasGnssBypass(true);
+ }
+
+ if (!CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE)
!= PERMISSION_GRANTED) {
sanitized.setLowPower(false);
@@ -786,7 +805,52 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
sanitized.setWorkSource(workSource);
- return sanitized.build();
+ request = sanitized.build();
+
+ // validate sanitized request
+ boolean isLocationProvider = mLocalService.isProvider(null, identity);
+
+ if (request.isLowPower() && CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS,
+ identity.getUid())) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.LOCATION_HARDWARE,
+ "low power request requires " + permission.LOCATION_HARDWARE);
+ }
+ if (request.isHiddenFromAppOps()) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.UPDATE_APP_OPS_STATS,
+ "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
+ }
+ if (request.isAdasGnssBypass()) {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalArgumentException(
+ "adas gnss bypass requests are only allowed on automotive devices");
+ }
+ if (!GPS_PROVIDER.equals(provider)) {
+ throw new IllegalArgumentException(
+ "adas gnss bypass requests are only allowed on the \"gps\" provider");
+ }
+ if (!ArrayUtils.contains(mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+ identity.getPackageName())) {
+ throw new SecurityException(
+ "only verified adas packages may use adas gnss bypass requests");
+ }
+ if (!isLocationProvider) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.WRITE_SECURE_SETTINGS,
+ "adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
+ }
+ }
+ if (request.isLocationSettingsIgnored()) {
+ if (!isLocationProvider) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.WRITE_SECURE_SETTINGS,
+ "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+ }
+ }
+
+ return request;
}
@Override
@@ -834,7 +898,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
// clients in the system process must have an attribution tag set
Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
- request = validateLastLocationRequest(request);
+ request = validateLastLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
if (manager == null) {
@@ -844,16 +908,58 @@ public class LocationManagerService extends ILocationManager.Stub implements
return manager.getLastLocation(request, identity, permissionLevel);
}
- private LastLocationRequest validateLastLocationRequest(LastLocationRequest request) {
+ private LastLocationRequest validateLastLocationRequest(String provider,
+ LastLocationRequest request,
+ CallerIdentity identity) {
+ // sanitize request
+ LastLocationRequest.Builder sanitized = new LastLocationRequest.Builder(request);
+
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && GPS_PROVIDER.equals(provider)
+ && ArrayUtils.contains(mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+ identity.getPackageName())) {
+ sanitized.setAdasGnssBypass(true);
+ }
+
+ request = sanitized.build();
+
+ // validate request
+ boolean isLocationProvider = mLocalService.isProvider(null, identity);
+
if (request.isHiddenFromAppOps()) {
mContext.enforceCallingOrSelfPermission(
permission.UPDATE_APP_OPS_STATS,
"hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
}
+
+ if (request.isAdasGnssBypass()) {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalArgumentException(
+ "adas gnss bypass requests are only allowed on automotive devices");
+ }
+ if (!GPS_PROVIDER.equals(provider)) {
+ throw new IllegalArgumentException(
+ "adas gnss bypass requests are only allowed on the \"gps\" provider");
+ }
+ if (!ArrayUtils.contains(mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+ identity.getPackageName())) {
+ throw new SecurityException(
+ "only verified adas packages may use adas gnss bypass requests");
+ }
+ if (!isLocationProvider) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.WRITE_SECURE_SETTINGS,
+ "adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
+ }
+ }
if (request.isLocationSettingsIgnored()) {
- mContext.enforceCallingOrSelfPermission(
- permission.WRITE_SECURE_SETTINGS,
- "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+ if (!isLocationProvider) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.WRITE_SECURE_SETTINGS,
+ "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+ }
}
return request;
@@ -1126,6 +1232,24 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
@Override
+ public void setAdasGnssLocationEnabledForUser(boolean enabled, int userId) {
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, false, "setAdasGnssLocationEnabledForUser", null);
+
+ mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
+
+ mInjector.getLocationSettings().updateUserSettings(userId,
+ settings -> settings.withAdasGnssLocationEnabled(enabled));
+ }
+
+ @Override
+ public boolean isAdasGnssLocationEnabledForUser(int userId) {
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, false, "isAdasGnssLocationEnabledForUser", null);
+ return mInjector.getLocationSettings().getUserSettings(userId).isAdasGnssLocationEnabled();
+ }
+
+ @Override
public boolean isProviderEnabledForUser(String provider, int userId) {
return mLocalService.isProviderEnabledForUser(provider, userId);
}
@@ -1555,11 +1679,12 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
}
- private static class SystemInjector implements Injector {
+ private static final class SystemInjector implements Injector {
private final Context mContext;
private final UserInfoHelper mUserInfoHelper;
+ private final LocationSettings mLocationSettings;
private final AlarmHelper mAlarmHelper;
private final SystemAppOpsHelper mAppOpsHelper;
private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
@@ -1584,6 +1709,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
mContext = context;
mUserInfoHelper = userInfoHelper;
+ mLocationSettings = new LocationSettings(context);
mAlarmHelper = new SystemAlarmHelper(context);
mAppOpsHelper = new SystemAppOpsHelper(context);
mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
@@ -1621,6 +1747,11 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
@Override
+ public LocationSettings getLocationSettings() {
+ return mLocationSettings;
+ }
+
+ @Override
public AlarmHelper getAlarmHelper() {
return mAlarmHelper;
}
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index 937849309f96..b65338d9691d 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -17,6 +17,7 @@
package com.android.server.location;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.provider.ProviderProperties;
@@ -60,6 +61,14 @@ class LocationShellCommand extends BasicShellCommandHandler {
handleSetLocationEnabled();
return 0;
}
+ case "is-adas-gnss-location-enabled": {
+ handleIsAdasGnssLocationEnabled();
+ return 0;
+ }
+ case "set-adas-gnss-location-enabled": {
+ handleSetAdasGnssLocationEnabled();
+ return 0;
+ }
case "providers": {
String command = getNextArgRequired();
return parseProvidersCommand(command);
@@ -134,6 +143,52 @@ class LocationShellCommand extends BasicShellCommandHandler {
mService.setLocationEnabledForUser(enabled, userId);
}
+ private void handleIsAdasGnssLocationEnabled() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalStateException("command only recognized on automotive devices");
+ }
+
+ int userId = UserHandle.USER_CURRENT_OR_SELF;
+
+ do {
+ String option = getNextOption();
+ if (option == null) {
+ break;
+ }
+ if ("--user".equals(option)) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + option);
+ }
+ } while (true);
+
+ getOutPrintWriter().println(mService.isAdasGnssLocationEnabledForUser(userId));
+ }
+
+ private void handleSetAdasGnssLocationEnabled() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalStateException("command only recognized on automotive devices");
+ }
+
+ boolean enabled = Boolean.parseBoolean(getNextArgRequired());
+
+ int userId = UserHandle.USER_CURRENT_OR_SELF;
+
+ do {
+ String option = getNextOption();
+ if (option == null) {
+ break;
+ }
+ if ("--user".equals(option)) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + option);
+ }
+ } while (true);
+
+ mService.setAdasGnssLocationEnabledForUser(enabled, userId);
+ }
+
private void handleAddTestProvider() {
String provider = getNextArgRequired();
@@ -297,6 +352,14 @@ class LocationShellCommand extends BasicShellCommandHandler {
pw.println(" set-location-enabled true|false [--user <USER_ID>]");
pw.println(" Sets the master location switch enabled state. If no user is specified,");
pw.println(" the current user is assumed.");
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ pw.println(" is-adas-gnss-location-enabled [--user <USER_ID>]");
+ pw.println(" Gets the ADAS GNSS location enabled state. If no user is specified,");
+ pw.println(" the current user is assumed.");
+ pw.println(" set-adas-gnss-location-enabled true|false [--user <USER_ID>]");
+ pw.println(" Sets the ADAS GNSS location enabled state. If no user is specified,");
+ pw.println(" the current user is assumed.");
+ }
pw.println(" providers");
pw.println(" The providers command is followed by a subcommand, as listed below:");
pw.println();
@@ -323,9 +386,8 @@ class LocationShellCommand extends BasicShellCommandHandler {
pw.println(" Common commands that may be supported by the gps provider, depending on");
pw.println(" hardware and software configurations:");
pw.println(" delete_aiding_data - requests deletion of any predictive aiding data");
- pw.println(" force_time_injection - requests NTP time injection to chipset");
- pw.println(" force_psds_injection - "
- + "requests predictive aiding data injection to chipset");
- pw.println(" request_power_stats - requests GNSS power stats update from chipset");
+ pw.println(" force_time_injection - requests NTP time injection");
+ pw.println(" force_psds_injection - requests predictive aiding data injection");
+ pw.println(" request_power_stats - requests GNSS power stats update");
}
}
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index e6d25ece93ef..db2a43f7a00d 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -55,19 +55,20 @@ public class LocationEventLog extends LocalEventLog {
private static final int EVENT_USER_SWITCHED = 1;
private static final int EVENT_LOCATION_ENABLED = 2;
- private static final int EVENT_PROVIDER_ENABLED = 3;
- private static final int EVENT_PROVIDER_MOCKED = 4;
- private static final int EVENT_PROVIDER_CLIENT_REGISTER = 5;
- private static final int EVENT_PROVIDER_CLIENT_UNREGISTER = 6;
- private static final int EVENT_PROVIDER_CLIENT_FOREGROUND = 7;
- private static final int EVENT_PROVIDER_CLIENT_BACKGROUND = 8;
- private static final int EVENT_PROVIDER_CLIENT_PERMITTED = 9;
- private static final int EVENT_PROVIDER_CLIENT_UNPERMITTED = 10;
- private static final int EVENT_PROVIDER_UPDATE_REQUEST = 11;
- private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 12;
- private static final int EVENT_PROVIDER_DELIVER_LOCATION = 13;
- private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 14;
- private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 15;
+ private static final int EVENT_ADAS_LOCATION_ENABLED = 3;
+ private static final int EVENT_PROVIDER_ENABLED = 4;
+ private static final int EVENT_PROVIDER_MOCKED = 5;
+ private static final int EVENT_PROVIDER_CLIENT_REGISTER = 6;
+ private static final int EVENT_PROVIDER_CLIENT_UNREGISTER = 7;
+ private static final int EVENT_PROVIDER_CLIENT_FOREGROUND = 8;
+ private static final int EVENT_PROVIDER_CLIENT_BACKGROUND = 9;
+ private static final int EVENT_PROVIDER_CLIENT_PERMITTED = 10;
+ private static final int EVENT_PROVIDER_CLIENT_UNPERMITTED = 11;
+ private static final int EVENT_PROVIDER_UPDATE_REQUEST = 12;
+ private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 13;
+ private static final int EVENT_PROVIDER_DELIVER_LOCATION = 14;
+ private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 15;
+ private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 16;
@GuardedBy("mAggregateStats")
private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
@@ -116,6 +117,11 @@ public class LocationEventLog extends LocalEventLog {
addLogEvent(EVENT_LOCATION_ENABLED, userId, enabled);
}
+ /** Logs a location enabled/disabled event. */
+ public void logAdasLocationEnabled(int userId, boolean enabled) {
+ addLogEvent(EVENT_ADAS_LOCATION_ENABLED, userId, enabled);
+ }
+
/** Logs a location provider enabled/disabled event. */
public void logProviderEnabled(String provider, int userId, boolean enabled) {
addLogEvent(EVENT_PROVIDER_ENABLED, provider, userId, enabled);
@@ -219,6 +225,9 @@ public class LocationEventLog extends LocalEventLog {
return new UserSwitchedEvent(timeDelta, (Integer) args[0], (Integer) args[1]);
case EVENT_LOCATION_ENABLED:
return new LocationEnabledEvent(timeDelta, (Integer) args[0], (Boolean) args[1]);
+ case EVENT_ADAS_LOCATION_ENABLED:
+ return new LocationAdasEnabledEvent(timeDelta, (Integer) args[0],
+ (Boolean) args[1]);
case EVENT_PROVIDER_ENABLED:
return new ProviderEnabledEvent(timeDelta, (String) args[0], (Integer) args[1],
(Boolean) args[2]);
@@ -517,6 +526,23 @@ public class LocationEventLog extends LocalEventLog {
}
}
+ private static final class LocationAdasEnabledEvent extends LogEvent {
+
+ private final int mUserId;
+ private final boolean mEnabled;
+
+ LocationAdasEnabledEvent(long timeDelta, int userId, boolean enabled) {
+ super(timeDelta);
+ mUserId = userId;
+ mEnabled = enabled;
+ }
+
+ @Override
+ public String getLogString() {
+ return "adas location [u" + mUserId + "] " + (mEnabled ? "enabled" : "disabled");
+ }
+ }
+
/**
* Aggregate statistics for a single package under a single provider.
*/
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 1cccf08b0367..f3dcfbbf2c0a 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -771,10 +771,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
boolean enabled = mContext.getSystemService(LocationManager.class)
.isLocationEnabledForUser(UserHandle.CURRENT);
- // .. but enable anyway, if there's an active settings-ignored request (e.g. ELS)
+ // .. but enable anyway, if there's an active bypass request (e.g. ELS or ADAS)
enabled |= (mProviderRequest != null
&& mProviderRequest.isActive()
- && mProviderRequest.isLocationSettingsIgnored());
+ && mProviderRequest.isBypass());
// ... and, finally, disable anyway, if device is being shut down
enabled &= !mShutdown;
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index b0351184ffe5..173fd13c11a1 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -17,6 +17,7 @@
package com.android.server.location.injector;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.settings.LocationSettings;
/**
* Injects various location dependencies so that they may be controlled by tests.
@@ -27,6 +28,9 @@ public interface Injector {
/** Returns a UserInfoHelper. */
UserInfoHelper getUserInfoHelper();
+ /** Returns a LocationSettings. */
+ LocationSettings getLocationSettings();
+
/** Returns an AlarmHelper. */
AlarmHelper getAlarmHelper();
diff --git a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
index c315da476d99..3e8da7d7478a 100644
--- a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
@@ -670,8 +670,6 @@ public class SystemSettingsHelper extends SettingsHelper {
}
}
-
-
private static class PackageTagsListSetting extends DeviceConfigSetting {
private final Supplier<ArrayMap<String, ArraySet<String>>> mBaseValuesSupplier;
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 8d335b83d99c..43886f7cb87b 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -113,6 +113,8 @@ import com.android.server.location.injector.UserInfoHelper;
import com.android.server.location.injector.UserInfoHelper.UserListener;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.listeners.RemoteListenerRegistration;
+import com.android.server.location.settings.LocationSettings;
+import com.android.server.location.settings.LocationUserSettings;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
@@ -549,6 +551,19 @@ public class LocationProviderManager extends
}
@GuardedBy("mLock")
+ final boolean onAdasGnssLocationEnabledChanged(int userId) {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mLock));
+ }
+
+ if (getIdentity().getUserId() == userId) {
+ return onProviderLocationRequestChanged();
+ }
+
+ return false;
+ }
+
+ @GuardedBy("mLock")
final boolean onForegroundChanged(int uid, boolean foreground) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
@@ -592,8 +607,8 @@ public class LocationProviderManager extends
onHighPowerUsageChanged();
updateService();
- // if location settings ignored has changed then the active state may have changed
- return oldRequest.isLocationSettingsIgnored() != newRequest.isLocationSettingsIgnored();
+ // if bypass state has changed then the active state may have changed
+ return oldRequest.isBypass() != newRequest.isBypass();
}
private LocationRequest calculateProviderLocationRequest() {
@@ -616,9 +631,24 @@ public class LocationProviderManager extends
if (!mSettingsHelper.getIgnoreSettingsAllowlist().contains(
getIdentity().getPackageName(), getIdentity().getAttributionTag())
&& !mLocationManagerInternal.isProvider(null, getIdentity())) {
- builder.setLocationSettingsIgnored(false);
locationSettingsIgnored = false;
}
+
+ builder.setLocationSettingsIgnored(locationSettingsIgnored);
+ }
+
+ boolean adasGnssBypass = baseRequest.isAdasGnssBypass();
+ if (adasGnssBypass) {
+ // if we are not currently allowed use adas gnss bypass, disable it
+ if (!GPS_PROVIDER.equals(mName)) {
+ Log.e(TAG, "adas gnss bypass request received in non-gps provider");
+ adasGnssBypass = false;
+ } else if (!mLocationSettings.getUserSettings(
+ getIdentity().getUserId()).isAdasGnssLocationEnabled()) {
+ adasGnssBypass = false;
+ }
+
+ builder.setAdasGnssBypass(adasGnssBypass);
}
if (!locationSettingsIgnored && !isThrottlingExempt()) {
@@ -769,7 +799,7 @@ public class LocationProviderManager extends
Location lastLocation = getLastLocationUnsafe(
getIdentity().getUserId(),
getPermissionLevel(),
- getRequest().isLocationSettingsIgnored(),
+ getRequest().isBypass(),
maxLocationAgeMs);
if (lastLocation != null) {
executeOperation(acceptLocationChange(LocationResult.wrap(lastLocation)));
@@ -1114,7 +1144,7 @@ public class LocationProviderManager extends
Location lastLocation = getLastLocationUnsafe(
getIdentity().getUserId(),
getPermissionLevel(),
- getRequest().isLocationSettingsIgnored(),
+ getRequest().isBypass(),
MAX_CURRENT_LOCATION_AGE_MS);
if (lastLocation != null) {
executeOperation(acceptLocationChange(LocationResult.wrap(lastLocation)));
@@ -1267,6 +1297,7 @@ public class LocationProviderManager extends
private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners;
protected final LocationManagerInternal mLocationManagerInternal;
+ protected final LocationSettings mLocationSettings;
protected final SettingsHelper mSettingsHelper;
protected final UserInfoHelper mUserHelper;
protected final AlarmHelper mAlarmHelper;
@@ -1280,6 +1311,8 @@ public class LocationProviderManager extends
protected final LocationFudger mLocationFudger;
private final UserListener mUserChangedListener = this::onUserChanged;
+ private final LocationSettings.LocationUserSettingsListener mLocationUserSettingsListener =
+ this::onLocationUserSettingsChanged;
private final UserSettingChangedListener mLocationEnabledChangedListener =
this::onLocationEnabledChanged;
private final GlobalSettingChangedListener mBackgroundThrottlePackageWhitelistChangedListener =
@@ -1332,6 +1365,7 @@ public class LocationProviderManager extends
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
+ mLocationSettings = injector.getLocationSettings();
mSettingsHelper = injector.getSettingsHelper();
mUserHelper = injector.getUserInfoHelper();
mAlarmHelper = injector.getAlarmHelper();
@@ -1362,6 +1396,7 @@ public class LocationProviderManager extends
mStateChangedListener = listener;
mUserHelper.addListener(mUserChangedListener);
+ mLocationSettings.registerLocationUserSettingsListener(mLocationUserSettingsListener);
mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
final long identity = Binder.clearCallingIdentity();
@@ -1389,6 +1424,7 @@ public class LocationProviderManager extends
}
mUserHelper.removeListener(mUserChangedListener);
+ mLocationSettings.unregisterLocationUserSettingsListener(mLocationUserSettingsListener);
mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
// if external entities are registering listeners it's their responsibility to
@@ -1550,7 +1586,7 @@ public class LocationProviderManager extends
public @Nullable Location getLastLocation(LastLocationRequest request,
CallerIdentity identity, @PermissionLevel int permissionLevel) {
- if (!isActive(request.isLocationSettingsIgnored(), identity)) {
+ if (!isActive(request.isBypass(), identity)) {
return null;
}
@@ -1564,7 +1600,7 @@ public class LocationProviderManager extends
getLastLocationUnsafe(
identity.getUserId(),
permissionLevel,
- request.isLocationSettingsIgnored(),
+ request.isBypass(),
Long.MAX_VALUE),
permissionLevel);
@@ -1584,7 +1620,7 @@ public class LocationProviderManager extends
* location if necessary.
*/
public @Nullable Location getLastLocationUnsafe(int userId,
- @PermissionLevel int permissionLevel, boolean ignoreLocationSettings,
+ @PermissionLevel int permissionLevel, boolean isBypass,
long maximumAgeMs) {
if (userId == UserHandle.USER_ALL) {
// find the most recent location across all users
@@ -1592,7 +1628,7 @@ public class LocationProviderManager extends
final int[] runningUserIds = mUserHelper.getRunningUserIds();
for (int i = 0; i < runningUserIds.length; i++) {
Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel,
- ignoreLocationSettings, maximumAgeMs);
+ isBypass, maximumAgeMs);
if (lastLocation == null || (next != null && next.getElapsedRealtimeNanos()
> lastLocation.getElapsedRealtimeNanos())) {
lastLocation = next;
@@ -1601,7 +1637,7 @@ public class LocationProviderManager extends
return lastLocation;
} else if (userId == UserHandle.USER_CURRENT) {
return getLastLocationUnsafe(mUserHelper.getCurrentUserId(), permissionLevel,
- ignoreLocationSettings, maximumAgeMs);
+ isBypass, maximumAgeMs);
}
Preconditions.checkArgument(userId >= 0);
@@ -1613,7 +1649,7 @@ public class LocationProviderManager extends
if (lastLocation == null) {
location = null;
} else {
- location = lastLocation.get(permissionLevel, ignoreLocationSettings);
+ location = lastLocation.get(permissionLevel, isBypass);
}
}
@@ -1925,7 +1961,7 @@ public class LocationProviderManager extends
// provider, under the assumption that once we send the request off, the provider will
// immediately attempt to deliver a new location satisfying that request.
long delayMs;
- if (!oldRequest.isLocationSettingsIgnored() && newRequest.isLocationSettingsIgnored()) {
+ if (!oldRequest.isBypass() && newRequest.isBypass()) {
delayMs = 0;
} else if (newRequest.getIntervalMillis() > oldRequest.getIntervalMillis()) {
// if the interval has increased, tell the provider immediately, so it can save power
@@ -2002,12 +2038,12 @@ public class LocationProviderManager extends
return false;
}
- boolean locationSettingsIgnored = registration.getRequest().isLocationSettingsIgnored();
- if (!isActive(locationSettingsIgnored, registration.getIdentity())) {
+ boolean isBypass = registration.getRequest().isBypass();
+ if (!isActive(isBypass, registration.getIdentity())) {
return false;
}
- if (!locationSettingsIgnored) {
+ if (!isBypass) {
switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) {
case LOCATION_MODE_FOREGROUND_ONLY:
if (!registration.isForeground()) {
@@ -2036,15 +2072,15 @@ public class LocationProviderManager extends
return true;
}
- private boolean isActive(boolean locationSettingsIgnored, CallerIdentity identity) {
+ private boolean isActive(boolean isBypass, CallerIdentity identity) {
if (identity.isSystemServer()) {
- if (!locationSettingsIgnored) {
+ if (!isBypass) {
if (!isEnabled(mUserHelper.getCurrentUserId())) {
return false;
}
}
} else {
- if (!locationSettingsIgnored) {
+ if (!isBypass) {
if (!isEnabled(identity.getUserId())) {
return false;
}
@@ -2071,6 +2107,7 @@ public class LocationProviderManager extends
long intervalMs = ProviderRequest.INTERVAL_DISABLED;
int quality = LocationRequest.QUALITY_LOW_POWER;
long maxUpdateDelayMs = Long.MAX_VALUE;
+ boolean adasGnssBypass = false;
boolean locationSettingsIgnored = false;
boolean lowPower = true;
@@ -2086,6 +2123,7 @@ public class LocationProviderManager extends
intervalMs = min(request.getIntervalMillis(), intervalMs);
quality = min(request.getQuality(), quality);
maxUpdateDelayMs = min(request.getMaxUpdateDelayMillis(), maxUpdateDelayMs);
+ adasGnssBypass |= request.isAdasGnssBypass();
locationSettingsIgnored |= request.isLocationSettingsIgnored();
lowPower &= request.isLowPower();
}
@@ -2123,6 +2161,7 @@ public class LocationProviderManager extends
.setIntervalMillis(intervalMs)
.setQuality(quality)
.setMaxUpdateDelayMillis(maxUpdateDelayMs)
+ .setAdasGnssBypass(adasGnssBypass)
.setLocationSettingsIgnored(locationSettingsIgnored)
.setLowPower(lowPower)
.setWorkSource(workSource)
@@ -2191,6 +2230,16 @@ public class LocationProviderManager extends
}
}
+ private void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ if (oldSettings.isAdasGnssLocationEnabled() != newSettings.isAdasGnssLocationEnabled()) {
+ synchronized (mLock) {
+ updateRegistrations(
+ registration -> registration.onAdasGnssLocationEnabledChanged(userId));
+ }
+ }
+ }
+
private void onLocationEnabledChanged(int userId) {
synchronized (mLock) {
if (mState == STATE_STOPPED) {
@@ -2560,16 +2609,16 @@ public class LocationProviderManager extends
}
public @Nullable Location get(@PermissionLevel int permissionLevel,
- boolean ignoreLocationSettings) {
+ boolean isBypass) {
switch (permissionLevel) {
case PERMISSION_FINE:
- if (ignoreLocationSettings) {
+ if (isBypass) {
return mFineBypassLocation;
} else {
return mFineLocation;
}
case PERMISSION_COARSE:
- if (ignoreLocationSettings) {
+ if (isBypass) {
return mCoarseBypassLocation;
} else {
return mCoarseLocation;
diff --git a/services/core/java/com/android/server/location/settings/LocationSettings.java b/services/core/java/com/android/server/location/settings/LocationSettings.java
new file mode 100644
index 000000000000..d52153893970
--- /dev/null
+++ b/services/core/java/com/android/server/location/settings/LocationSettings.java
@@ -0,0 +1,173 @@
+/*
+ * 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.location.settings;
+
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+
+import android.content.Context;
+import android.os.Environment;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.FgThread;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Function;
+
+/**
+ * Accessor for location user settings. Ensure there is only ever one instance as multiple instances
+ * don't play nicely with each other.
+ */
+public class LocationSettings {
+
+ /** Listens for changes to location user settings. */
+ public interface LocationUserSettingsListener {
+ /** Invoked when location user settings have changed for the given user. */
+ void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
+ LocationUserSettings newSettings);
+ }
+
+ private static final String LOCATION_DIRNAME = "location";
+ private static final String LOCATION_SETTINGS_FILENAME = "settings";
+
+ final Context mContext;
+
+ @GuardedBy("mUserSettings")
+ private final SparseArray<LocationUserSettingsStore> mUserSettings;
+ private final CopyOnWriteArrayList<LocationUserSettingsListener> mUserSettingsListeners;
+
+ public LocationSettings(Context context) {
+ mContext = context;
+ mUserSettings = new SparseArray<>(1);
+ mUserSettingsListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /** Registers a listener for changes to location user settings. */
+ public final void registerLocationUserSettingsListener(LocationUserSettingsListener listener) {
+ mUserSettingsListeners.add(listener);
+ }
+
+ /** Unregisters a listener for changes to location user settings. */
+ public final void unregisterLocationUserSettingsListener(
+ LocationUserSettingsListener listener) {
+ mUserSettingsListeners.remove(listener);
+ }
+
+ protected File getUserSettingsDir(int userId) {
+ return Environment.getDataSystemDeDirectory(userId);
+ }
+
+ protected LocationUserSettingsStore createUserSettingsStore(int userId, File file) {
+ return new LocationUserSettingsStore(userId, file);
+ }
+
+ private LocationUserSettingsStore getUserSettingsStore(int userId) {
+ synchronized (mUserSettings) {
+ LocationUserSettingsStore settingsStore = mUserSettings.get(userId);
+ if (settingsStore == null) {
+ File file = new File(new File(getUserSettingsDir(userId), LOCATION_DIRNAME),
+ LOCATION_SETTINGS_FILENAME);
+ settingsStore = createUserSettingsStore(userId, file);
+ mUserSettings.put(userId, settingsStore);
+ }
+ return settingsStore;
+ }
+ }
+
+ /** Retrieves the current state of location user settings. */
+ public final LocationUserSettings getUserSettings(int userId) {
+ return getUserSettingsStore(userId).get();
+ }
+
+ /** Updates the current state of location user settings for the given user. */
+ public final void updateUserSettings(int userId,
+ Function<LocationUserSettings, LocationUserSettings> updater) {
+ getUserSettingsStore(userId).update(updater);
+ }
+
+ @VisibleForTesting
+ final void flushFiles() throws InterruptedException {
+ synchronized (mUserSettings) {
+ int size = mUserSettings.size();
+ for (int i = 0; i < size; i++) {
+ mUserSettings.valueAt(i).flushFile();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ final void deleteFiles() throws InterruptedException {
+ synchronized (mUserSettings) {
+ int size = mUserSettings.size();
+ for (int i = 0; i < size; i++) {
+ mUserSettings.valueAt(i).deleteFile();
+ }
+ }
+ }
+
+ protected final void fireListeners(int userId, LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ for (LocationUserSettingsListener listener : mUserSettingsListeners) {
+ listener.onLocationUserSettingsChanged(userId, oldSettings, newSettings);
+ }
+ }
+
+ class LocationUserSettingsStore extends SettingsStore<LocationUserSettings> {
+
+ protected final int mUserId;
+
+ LocationUserSettingsStore(int userId, File file) {
+ super(file);
+ mUserId = userId;
+ }
+
+ @Override
+ protected LocationUserSettings read(int version, DataInput in) throws IOException {
+ return filterSettings(LocationUserSettings.read(mContext.getResources(), version, in));
+ }
+
+ @Override
+ protected void write(DataOutput out, LocationUserSettings settings) throws IOException {
+ settings.write(out);
+ }
+
+ @Override
+ public void update(Function<LocationUserSettings, LocationUserSettings> updater) {
+ super.update(settings -> filterSettings(updater.apply(settings)));
+ }
+
+ @Override
+ protected void onChange(LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ FgThread.getExecutor().execute(() -> fireListeners(mUserId, oldSettings, newSettings));
+ }
+
+ private LocationUserSettings filterSettings(LocationUserSettings settings) {
+ if (settings.isAdasGnssLocationEnabled()
+ && !mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) {
+ // prevent non-automotive devices from ever enabling this
+ settings = settings.withAdasGnssLocationEnabled(false);
+ }
+ return settings;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/settings/LocationUserSettings.java b/services/core/java/com/android/server/location/settings/LocationUserSettings.java
new file mode 100644
index 000000000000..283255ef4b22
--- /dev/null
+++ b/services/core/java/com/android/server/location/settings/LocationUserSettings.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 com.android.server.location.settings;
+
+import android.content.res.Resources;
+
+import com.android.internal.R;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Objects;
+
+/** Holds the state of location user settings. */
+public final class LocationUserSettings implements SettingsStore.VersionedSettings {
+
+ // remember to bump this version code and add the appropriate upgrade logic whenever the format
+ // is changed.
+ private static final int VERSION = 1;
+
+ private final boolean mAdasGnssLocationEnabled;
+
+ private LocationUserSettings(boolean adasGnssLocationEnabled) {
+ mAdasGnssLocationEnabled = adasGnssLocationEnabled;
+ }
+
+ @Override
+ public int getVersion() {
+ return VERSION;
+ }
+
+ public boolean isAdasGnssLocationEnabled() {
+ return mAdasGnssLocationEnabled;
+ }
+
+ /** Returns an instance with ADAS GNSS location enabled state set as given. */
+ public LocationUserSettings withAdasGnssLocationEnabled(boolean adasEnabled) {
+ if (adasEnabled == mAdasGnssLocationEnabled) {
+ return this;
+ }
+
+ return new LocationUserSettings(adasEnabled);
+ }
+
+ void write(DataOutput out) throws IOException {
+ out.writeBoolean(mAdasGnssLocationEnabled);
+ }
+
+ static LocationUserSettings read(Resources resources, int version, DataInput in)
+ throws IOException {
+ boolean adasGnssLocationEnabled;
+
+ // upgrade code goes here. remember to bump the version field when changing the format
+ switch (version) {
+ default:
+ // set all fields to defaults
+ adasGnssLocationEnabled = resources.getBoolean(
+ R.bool.config_defaultAdasGnssLocationEnabled);
+ break;
+ case 1:
+ adasGnssLocationEnabled = in.readBoolean();
+ // fall through
+ }
+
+ return new LocationUserSettings(adasGnssLocationEnabled);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof LocationUserSettings)) {
+ return false;
+ }
+ LocationUserSettings that = (LocationUserSettings) o;
+ return mAdasGnssLocationEnabled == that.mAdasGnssLocationEnabled;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAdasGnssLocationEnabled);
+ }
+}
diff --git a/services/core/java/com/android/server/location/settings/SettingsStore.java b/services/core/java/com/android/server/location/settings/SettingsStore.java
new file mode 100644
index 000000000000..01338a3129da
--- /dev/null
+++ b/services/core/java/com/android/server/location/settings/SettingsStore.java
@@ -0,0 +1,166 @@
+/*
+ * 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.location.settings;
+
+import static com.android.server.location.LocationManagerService.TAG;
+import static com.android.server.location.settings.SettingsStore.VersionedSettings.VERSION_DOES_NOT_EXIST;
+
+import android.util.AtomicFile;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Function;
+
+/** Base class for read/write/versioning functionality for storing persistent settings to a file. */
+abstract class SettingsStore<T extends SettingsStore.VersionedSettings> {
+
+ interface VersionedSettings {
+ /** Represents that the settings do not exist. */
+ int VERSION_DOES_NOT_EXIST = Integer.MAX_VALUE;
+
+ /** Must always return a version number less than {@link #VERSION_DOES_NOT_EXIST}. */
+ int getVersion();
+ }
+
+ private final AtomicFile mFile;
+
+ @GuardedBy("this")
+ private boolean mInitialized;
+ @GuardedBy("this")
+ private T mCache;
+
+ protected SettingsStore(File file) {
+ mFile = new AtomicFile(file);
+ }
+
+ /**
+ * Must be implemented to read in a settings instance, and upgrade to the appropriate version
+ * where necessary. If the provided version is {@link VersionedSettings#VERSION_DOES_NOT_EXIST}
+ * then the DataInput will be empty, and the method should return a settings instance with all
+ * settings set to the default value.
+ */
+ protected abstract T read(int version, DataInput in) throws IOException;
+
+ /**
+ * Must be implemented to write the given settings to the given DataOutput.
+ */
+ protected abstract void write(DataOutput out, T settings) throws IOException;
+
+ /**
+ * Invoked when settings change, and while holding the internal lock. If used to invoke
+ * listeners, ensure they are not invoked while holding the lock (ie, asynchronously).
+ */
+ protected abstract void onChange(T oldSettings, T newSettings);
+
+ public final synchronized void initializeCache() {
+ if (!mInitialized) {
+ if (mFile.exists()) {
+ try (DataInputStream is = new DataInputStream(mFile.openRead())) {
+ mCache = read(is.readInt(), is);
+ Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
+ } catch (IOException e) {
+ Log.e(TAG, "error reading location settings (" + mFile
+ + "), falling back to defaults", e);
+ }
+ }
+
+ if (mCache == null) {
+ try {
+ mCache = read(VERSION_DOES_NOT_EXIST,
+ new DataInputStream(new ByteArrayInputStream(new byte[0])));
+ Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ mInitialized = true;
+ }
+ }
+
+ public final synchronized T get() {
+ initializeCache();
+ return mCache;
+ }
+
+ public synchronized void update(Function<T, T> updater) {
+ initializeCache();
+
+ T oldSettings = mCache;
+ T newSettings = Objects.requireNonNull(updater.apply(oldSettings));
+ if (oldSettings.equals(newSettings)) {
+ return;
+ }
+
+ mCache = newSettings;
+ Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
+
+ writeLazily(newSettings);
+
+ onChange(oldSettings, newSettings);
+ }
+
+ @VisibleForTesting
+ synchronized void flushFile() throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ BackgroundThread.getExecutor().execute(latch::countDown);
+ latch.await();
+ }
+
+ @VisibleForTesting
+ synchronized void deleteFile() throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ BackgroundThread.getExecutor().execute(() -> {
+ mFile.delete();
+ latch.countDown();
+ });
+ latch.await();
+ }
+
+ private void writeLazily(T settings) {
+ BackgroundThread.getExecutor().execute(() -> {
+ FileOutputStream os = null;
+ try {
+ os = mFile.startWrite();
+ DataOutputStream out = new DataOutputStream(os);
+ out.writeInt(settings.getVersion());
+ write(out, settings);
+ mFile.finishWrite(os);
+ } catch (IOException e) {
+ mFile.failWrite(os);
+ Log.e(TAG, "failure serializing location settings", e);
+ } catch (Throwable e) {
+ mFile.failWrite(os);
+ throw e;
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 21f68aecd25b..d791bd69236c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1235,11 +1235,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final private BroadcastReceiver mWifiReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- synchronized (mUidRulesFirstLock) {
- synchronized (mNetworkPoliciesSecondLock) {
- upgradeWifiMeteredOverrideAL();
- }
- }
+ upgradeWifiMeteredOverride();
// Only need to perform upgrade logic once
mContext.unregisterReceiver(this);
}
@@ -2617,34 +2613,43 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* Perform upgrade step of moving any user-defined meterness overrides over
* into {@link WifiConfiguration}.
*/
- @GuardedBy({"mNetworkPoliciesSecondLock", "mUidRulesFirstLock"})
- private void upgradeWifiMeteredOverrideAL() {
- boolean modified = false;
+ private void upgradeWifiMeteredOverride() {
+ final ArrayMap<String, Boolean> wifiNetworkIds = new ArrayMap<>();
+ synchronized (mNetworkPoliciesSecondLock) {
+ for (int i = 0; i < mNetworkPolicy.size();) {
+ final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
+ if (policy.template.getMatchRule() == NetworkTemplate.MATCH_WIFI
+ && !policy.inferred) {
+ mNetworkPolicy.removeAt(i);
+ wifiNetworkIds.put(policy.template.getNetworkId(), policy.metered);
+ } else {
+ i++;
+ }
+ }
+ }
+
+ if (wifiNetworkIds.isEmpty()) {
+ return;
+ }
final WifiManager wm = mContext.getSystemService(WifiManager.class);
final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
- for (int i = 0; i < mNetworkPolicy.size(); ) {
- final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
- if (policy.template.getMatchRule() == NetworkTemplate.MATCH_WIFI
- && !policy.inferred) {
- mNetworkPolicy.removeAt(i);
- modified = true;
-
- final String networkId = resolveNetworkId(policy.template.getNetworkId());
- for (WifiConfiguration config : configs) {
- if (Objects.equals(resolveNetworkId(config), networkId)) {
- Slog.d(TAG, "Found network " + networkId + "; upgrading metered hint");
- config.meteredOverride = policy.metered
- ? WifiConfiguration.METERED_OVERRIDE_METERED
- : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
- wm.updateNetwork(config);
- }
- }
- } else {
- i++;
+ for (int i = 0; i < configs.size(); ++i) {
+ final WifiConfiguration config = configs.get(i);
+ final String networkId = resolveNetworkId(config);
+ final Boolean metered = wifiNetworkIds.get(networkId);
+ if (metered != null) {
+ Slog.d(TAG, "Found network " + networkId + "; upgrading metered hint");
+ config.meteredOverride = metered
+ ? WifiConfiguration.METERED_OVERRIDE_METERED
+ : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+ wm.updateNetwork(config);
}
}
- if (modified) {
- writePolicyAL();
+
+ synchronized (mUidRulesFirstLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ writePolicyAL();
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a3f3a3a503c8..d78fbdb53d8d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -140,6 +140,7 @@ import android.app.INotificationManager;
import android.app.ITransientNotification;
import android.app.ITransientNotificationCallback;
import android.app.IUriGrantsManager;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -549,6 +550,8 @@ public class NotificationManagerService extends SystemService {
// Used for rate limiting toasts by package.
private MultiRateLimiter mToastRateLimiter;
+ private KeyguardManager mKeyguardManager;
+
// The last key in this list owns the hardware.
ArrayList<String> mLights = new ArrayList<>();
@@ -2008,6 +2011,11 @@ public class NotificationManagerService extends SystemService {
}
@VisibleForTesting
+ void setKeyguardManager(KeyguardManager keyguardManager) {
+ mKeyguardManager = keyguardManager;
+ }
+
+ @VisibleForTesting
ShortcutHelper getShortcutHelper() {
return mShortcutHelper;
}
@@ -2653,6 +2661,7 @@ public class NotificationManagerService extends SystemService {
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+ mKeyguardManager = getContext().getSystemService(KeyguardManager.class);
mZenModeHelper.onSystemReady();
RoleObserver roleObserver = new RoleObserver(getContext(),
getContext().getSystemService(RoleManager.class),
@@ -3806,15 +3815,18 @@ public class NotificationManagerService extends SystemService {
enforceDeletingChannelHasNoFgService(pkg, callingUser, channelId);
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
callingUser, REASON_CHANNEL_REMOVED, null);
- mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId);
- // Remove from both recent notification archive and notification history
- mArchive.removeChannelNotifications(pkg, callingUser, channelId);
- mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId);
- mListeners.notifyNotificationChannelChanged(pkg,
- UserHandle.getUserHandleForUid(callingUid),
- mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
- NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
- handleSavePolicyFile();
+ boolean previouslyExisted = mPreferencesHelper.deleteNotificationChannel(
+ pkg, callingUid, channelId);
+ if (previouslyExisted) {
+ // Remove from both recent notification archive and notification history
+ mArchive.removeChannelNotifications(pkg, callingUser, channelId);
+ mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId);
+ mListeners.notifyNotificationChannelChanged(pkg,
+ UserHandle.getUserHandleForUid(callingUid),
+ mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
+ NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
+ handleSavePolicyFile();
+ }
}
@Override
@@ -7388,7 +7400,6 @@ public class NotificationManagerService extends SystemService {
boolean beep = false;
boolean blink = false;
- final Notification notification = record.getSbn().getNotification();
final String key = record.getKey();
// Should this notification make noise, vibe, or use the LED?
@@ -7410,7 +7421,7 @@ public class NotificationManagerService extends SystemService {
if (!record.isUpdate
&& record.getImportance() > IMPORTANCE_MIN
&& !suppressedByDnd) {
- sendAccessibilityEvent(notification, record.getSbn().getPackageName());
+ sendAccessibilityEvent(record);
sentAccessibilityEvent = true;
}
@@ -7433,7 +7444,7 @@ public class NotificationManagerService extends SystemService {
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
if (!sentAccessibilityEvent) {
- sendAccessibilityEvent(notification, record.getSbn().getPackageName());
+ sendAccessibilityEvent(record);
sentAccessibilityEvent = true;
}
if (DBG) Slog.v(TAG, "Interrupting!");
@@ -8261,17 +8272,30 @@ public class NotificationManagerService extends SystemService {
return (x < low) ? low : ((x > high) ? high : x);
}
- void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
+ void sendAccessibilityEvent(NotificationRecord record) {
if (!mAccessibilityManager.isEnabled()) {
return;
}
- AccessibilityEvent event =
+ final Notification notification = record.getNotification();
+ final CharSequence packageName = record.getSbn().getPackageName();
+ final AccessibilityEvent event =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
event.setPackageName(packageName);
event.setClassName(Notification.class.getName());
- event.setParcelableData(notification);
- CharSequence tickerText = notification.tickerText;
+ final int visibilityOverride = record.getPackageVisibilityOverride();
+ final int notifVisibility = visibilityOverride == NotificationManager.VISIBILITY_NO_OVERRIDE
+ ? notification.visibility : visibilityOverride;
+ final int userId = record.getUser().getIdentifier();
+ final boolean needPublic = userId >= 0 && mKeyguardManager.isDeviceLocked(userId);
+ if (needPublic && notifVisibility != Notification.VISIBILITY_PUBLIC) {
+ // Emit the public version if we're on the lockscreen and this notification isn't
+ // publicly visible.
+ event.setParcelableData(notification.publicVersion);
+ } else {
+ event.setParcelableData(notification);
+ }
+ final CharSequence tickerText = notification.tickerText;
if (!TextUtils.isEmpty(tickerText)) {
event.getText().add(tickerText);
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 03676b5556d9..96bde3df1e68 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1126,20 +1126,21 @@ public class PreferencesHelper implements RankingConfig {
}
@Override
- public void deleteNotificationChannel(String pkg, int uid, String channelId) {
+ public boolean deleteNotificationChannel(String pkg, int uid, String channelId) {
synchronized (mPackagePreferences) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
- return;
+ return false;
}
NotificationChannel channel = r.channels.get(channelId);
if (channel != null) {
- deleteNotificationChannelLocked(channel, pkg, uid);
+ return deleteNotificationChannelLocked(channel, pkg, uid);
}
+ return false;
}
}
- private void deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) {
+ private boolean deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) {
if (!channel.isDeleted()) {
channel.setDeleted(true);
channel.setDeletedTimeMs(System.currentTimeMillis());
@@ -1151,7 +1152,9 @@ public class PreferencesHelper implements RankingConfig {
if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
updateChannelsBypassingDnd();
}
+ return true;
}
+ return false;
}
@Override
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index b1d654671c2d..398259333e16 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -53,7 +53,7 @@ public interface RankingConfig {
NotificationChannel getConversationNotificationChannel(String pkg, int uid, String channelId,
String conversationId, boolean returnParentIfNoConversationChannel,
boolean includeDeleted);
- void deleteNotificationChannel(String pkg, int uid, String channelId);
+ boolean deleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannels(String pkg, int uid);
ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index a98f113a9d57..b144ff27c993 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -417,7 +417,14 @@ public class ZenModeHelper {
newConfig = mConfig.copy();
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
- if (rule.pkg.equals(packageName) && canManageAutomaticZenRule(rule)) {
+ String pkg = rule.pkg != null
+ ? rule.pkg
+ : (rule.component != null)
+ ? rule.component.getPackageName()
+ : (rule.configurationActivity != null)
+ ? rule.configurationActivity.getPackageName()
+ : null;
+ if (Objects.equals(pkg, packageName) && canManageAutomaticZenRule(rule)) {
newConfig.automaticRules.removeAt(i);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index c33130037027..acc83cfd05b6 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2799,7 +2799,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private boolean isApexUpdateAllowed(String apexPackageName) {
return mPm.getModuleInfo(apexPackageName, 0) != null
- || SystemConfig.getInstance().getAllowedPartnerApexes().contains(apexPackageName);
+ || SystemConfig.getInstance().getAllowedVendorApexes().contains(apexPackageName);
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b0a662b3514d..2419873cdc41 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -28148,8 +28148,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public void deleteOatArtifactsOfPackage(String packageName) {
- PackageManagerService.this.deleteOatArtifactsOfPackage(packageName);
+ public long deleteOatArtifactsOfPackage(String packageName) {
+ return PackageManagerService.this.deleteOatArtifactsOfPackage(packageName);
}
@Override
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index 83d4ce7ff057..b26b6940a1af 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -22,6 +22,7 @@ import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATI
import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
+import android.os.SystemClock;
import android.util.Slog;
import android.util.jar.StrictJarFile;
@@ -288,7 +289,7 @@ public class ArtStatsLogUtils {
ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN),
COMPILATION_REASON_MAP.getOrDefault(compilationReason, ArtStatsLog.
ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN),
- /*timestamp_millis=*/ 0L,
+ /*timestamp_millis=*/ SystemClock.uptimeMillis(),
ArtStatsLog.ART_DATUM_REPORTED__THREAD_TYPE__ART_THREAD_MAIN,
kind,
value,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index a391dbc1fa47..38e9d3ec34e3 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -5720,10 +5720,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
boolean fromDatasource, int attributedOp) {
// Now let's check the identity chain...
final int op = AppOpsManager.permissionToOpCode(permission);
- final int attributionChainId = (startDataDelivery)
- ? sAttributionChainIds.incrementAndGet()
- : AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
-
+ final int attributionChainId =
+ getAttributionChainId(startDataDelivery, attributionSource);
AttributionSource current = attributionSource;
AttributionSource next = null;
@@ -5879,9 +5877,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
- final int attributionChainId = (startDataDelivery)
- ? sAttributionChainIds.incrementAndGet()
- : AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+ final int attributionChainId =
+ getAttributionChainId(startDataDelivery, attributionSource);
AttributionSource current = attributionSource;
AttributionSource next = null;
@@ -6064,6 +6061,21 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
+ private static int getAttributionChainId(boolean startDataDelivery,
+ AttributionSource source) {
+ if (source == null || source.getNext() == null || !startDataDelivery) {
+ return AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+ }
+ int attributionChainId = sAttributionChainIds.incrementAndGet();
+
+ // handle overflow
+ if (attributionChainId < 0) {
+ attributionChainId = 0;
+ sAttributionChainIds.set(0);
+ }
+ return attributionChainId;
+ }
+
private static @Nullable String resolvePackageName(@NonNull Context context,
@NonNull AttributionSource attributionSource) {
if (attributionSource.getPackageName() != null) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9a6e4448966d..4471f6c91190 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -683,8 +683,9 @@ class WindowStateAnimator {
}
// We don't apply animation for application main window here since this window type
- // should be controlled by AppWindowToken in general.
- if (mAttrType != TYPE_BASE_APPLICATION) {
+ // should be controlled by ActivityRecord in general. Wallpaper is also excluded because
+ // WallpaperController should handle it.
+ if (mAttrType != TYPE_BASE_APPLICATION && !mIsWallpaper) {
applyAnimationLocked(transit, true);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index e99113d1296f..acf50b4569c6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -323,6 +323,8 @@ public class DeviceIdleControllerTest {
when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
doNothing().when(mWakeLock).acquire();
doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any());
+ doNothing().when(mAlarmManager)
+ .setWindow(anyInt(), anyLong(), anyLong(), anyString(), any(), any());
doReturn(mock(Sensor.class)).when(mSensorManager)
.getDefaultSensor(eq(Sensor.TYPE_SIGNIFICANT_MOTION), eq(true));
doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
@@ -1043,24 +1045,28 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.stepLightIdleStateLocked("testing");
verifyLightStateConditions(LIGHT_STATE_IDLE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT));
+ longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT),
+ longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX));
// Should just alternate between IDLE and IDLE_MAINTENANCE now.
mDeviceIdleController.stepLightIdleStateLocked("testing");
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET));
+ longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET),
+ longThat(l -> l == mConstants.FLEX_TIME_SHORT));
mDeviceIdleController.stepLightIdleStateLocked("testing");
verifyLightStateConditions(LIGHT_STATE_IDLE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT));
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT),
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX));
mDeviceIdleController.stepLightIdleStateLocked("testing");
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET));
+ longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET),
+ longThat(l -> l == mConstants.FLEX_TIME_SHORT));
// Test that motion doesn't reset the idle timeout.
mDeviceIdleController.handleMotionDetectedLocked(50, "test");
@@ -1068,7 +1074,8 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.stepLightIdleStateLocked("testing");
verifyLightStateConditions(LIGHT_STATE_IDLE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT));
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT),
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX));
}
///////////////// EXIT conditions ///////////////////
@@ -1824,9 +1831,9 @@ public class DeviceIdleControllerTest {
.forClass(AlarmManager.OnAlarmListener.class);
final ArgumentCaptor<AlarmManager.OnAlarmListener> motionRegistrationAlarmListener =
ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
- doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion"),
- motionAlarmListener.capture(), any());
- doNothing().when(mAlarmManager).set(anyInt(), anyLong(),
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+ eq("DeviceIdleController.motion"), motionAlarmListener.capture(), any());
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
eq("DeviceIdleController.motion_registration"),
motionRegistrationAlarmListener.capture(), any());
@@ -1900,9 +1907,9 @@ public class DeviceIdleControllerTest {
mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT;
final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
.forClass(AlarmManager.OnAlarmListener.class);
- doNothing().when(mAlarmManager)
- .set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
- doNothing().when(mAlarmManager).set(anyInt(), anyLong(),
+ doNothing().when(mAlarmManager).setWindow(
+ anyInt(), anyLong(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
eq("DeviceIdleController.motion_registration"),
alarmListener.capture(), any());
ArgumentCaptor<TriggerEventListener> listenerCaptor =
@@ -1944,9 +1951,9 @@ public class DeviceIdleControllerTest {
mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT;
final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
.forClass(AlarmManager.OnAlarmListener.class);
- doNothing().when(mAlarmManager)
- .set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
- doNothing().when(mAlarmManager).set(anyInt(), anyLong(),
+ doNothing().when(mAlarmManager).setWindow(
+ anyInt(), anyLong(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
eq("DeviceIdleController.motion_registration"),
alarmListener.capture(), any());
ArgumentCaptor<SensorEventListener> listenerCaptor =
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
index b480f24fb371..5e219a25ed9a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
@@ -18,6 +18,7 @@ package com.android.server.location.gnss;
import static com.google.common.truth.Truth.assertThat;
+import android.content.Context;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -49,6 +50,7 @@ public class GnssGeofenceProxyTest {
private static final int NOTIFICATION_RESPONSIVENESS = 0;
private static final int UNKNOWN_TIMER = 0;
+ private @Mock Context mContext;
private @Mock GnssConfiguration mMockConfiguration;
private @Mock GnssNative.GeofenceCallbacks mGeofenceCallbacks;
@@ -63,7 +65,7 @@ public class GnssGeofenceProxyTest {
GnssNative.setGnssHalForTest(mFakeHal);
GnssNative gnssNative = Objects.requireNonNull(
- GnssNative.create(new TestInjector(), mMockConfiguration));
+ GnssNative.create(new TestInjector(mContext), mMockConfiguration));
gnssNative.setGeofenceCallbacks(mGeofenceCallbacks);
mTestProvider = new GnssGeofenceProxy(gnssNative);
gnssNative.register();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
index 3d0378170c94..d728451d92b9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
@@ -29,9 +29,10 @@ import java.util.HashMap;
public class FakeAppOpsHelper extends AppOpsHelper {
private static class AppOp {
- private boolean mAllowed = true;
- private boolean mStarted = false;
- private int mNoteCount = 0;
+ AppOp() {}
+ boolean mAllowed = true;
+ boolean mStarted = false;
+ int mNoteCount = 0;
}
private final HashMap<String, SparseArray<AppOp>> mAppOps;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java
index f1099f0e8184..cd70020f5c28 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java
@@ -29,8 +29,8 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
/**
- * Version of AppOpsHelper for testing. Settings are initialized to reasonable defaults (location is
- * enabled by default).
+ * Version of SettingsHelper for testing. Settings are initialized to reasonable defaults (location
+ * is enabled by default).
*/
public class FakeSettingsHelper extends SettingsHelper {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index ae70dadba041..bd24cfd78a2c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -16,9 +16,14 @@
package com.android.server.location.injector;
+import android.content.Context;
+
+import com.android.server.location.settings.FakeLocationSettings;
+
public class TestInjector implements Injector {
private final FakeUserInfoHelper mUserInfoHelper;
+ private final FakeLocationSettings mLocationSettings;
private final FakeAlarmHelper mAlarmHelper;
private final FakeAppOpsHelper mAppOpsHelper;
private final FakeLocationPermissionsHelper mLocationPermissionsHelper;
@@ -32,8 +37,9 @@ public class TestInjector implements Injector {
private final FakeEmergencyHelper mEmergencyHelper;
private final LocationUsageLogger mLocationUsageLogger;
- public TestInjector() {
+ public TestInjector(Context context) {
mUserInfoHelper = new FakeUserInfoHelper();
+ mLocationSettings = new FakeLocationSettings(context);
mAlarmHelper = new FakeAlarmHelper();
mAppOpsHelper = new FakeAppOpsHelper();
mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
@@ -54,6 +60,11 @@ public class TestInjector implements Injector {
}
@Override
+ public FakeLocationSettings getLocationSettings() {
+ return mLocationSettings;
+ }
+
+ @Override
public FakeAlarmHelper getAlarmHelper() {
return mAlarmHelper;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 6bc3b6041070..f703e2e59181 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -19,6 +19,8 @@ package com.android.server.location.provider;
import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationRequest.PASSIVE_INTERVAL;
import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
@@ -56,6 +58,8 @@ import static org.mockito.MockitoAnnotations.initMocks;
import static org.testng.Assert.assertThrows;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.location.ILocationCallback;
import android.location.ILocationListener;
import android.location.LastLocationRequest;
@@ -82,6 +86,7 @@ import android.util.Log;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.location.injector.FakeUserInfoHelper;
@@ -139,6 +144,10 @@ public class LocationProviderManagerTest {
@Mock
private Context mContext;
@Mock
+ private Resources mResources;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
private PowerManager mPowerManager;
@Mock
private PowerManager.WakeLock mWakeLock;
@@ -161,20 +170,28 @@ public class LocationProviderManagerTest {
LocalServices.addService(LocationManagerInternal.class, mInternal);
doReturn("android").when(mContext).getPackageName();
+ doReturn(mResources).when(mContext).getResources();
+ doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
- mInjector = new TestInjector();
+ mInjector = new TestInjector(mContext);
mInjector.getUserInfoHelper().startUser(OTHER_USER);
mPassive = new PassiveLocationProviderManager(mContext, mInjector);
mPassive.startManager(null);
mPassive.setRealProvider(new PassiveLocationProvider(mContext));
+ createManager(NAME);
+ }
+
+ private void createManager(String name) {
+ mStateChangedListener = mock(LocationProviderManager.StateChangedListener.class);
+
mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY);
mProvider.setProviderAllowed(true);
- mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive);
+ mManager = new LocationProviderManager(mContext, mInjector, name, mPassive);
mManager.startManager(mStateChangedListener);
mManager.setRealProvider(mProvider);
}
@@ -1017,6 +1034,95 @@ public class LocationProviderManagerTest {
}
@Test
+ public void testProviderRequest_AdasGnssBypass() {
+ doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+ createManager(GPS_PROVIDER);
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(5)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+ assertThat(mProvider.getRequest().isAdasGnssBypass()).isFalse();
+
+ ILocationListener listener2 = createMockLocationListener();
+ LocationRequest request2 = new LocationRequest.Builder(1)
+ .setAdasGnssBypass(true)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
+ assertThat(mProvider.getRequest().isAdasGnssBypass()).isTrue();
+ }
+
+ @Test
+ public void testProviderRequest_AdasGnssBypass_ProviderDisabled() {
+ doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+ createManager(GPS_PROVIDER);
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(1)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+ ILocationListener listener2 = createMockLocationListener();
+ LocationRequest request2 = new LocationRequest.Builder(5)
+ .setAdasGnssBypass(true)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+ mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+ assertThat(mProvider.getRequest().isAdasGnssBypass()).isTrue();
+ }
+
+ @Test
+ public void testProviderRequest_AdasGnssBypass_ProviderDisabled_AdasDisabled() {
+ mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
+ new PackageTagsList.Builder().add(
+ IDENTITY.getPackageName()).build());
+ doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+ createManager(GPS_PROVIDER);
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(5)
+ .setLocationSettingsIgnored(true)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+ ILocationListener listener2 = createMockLocationListener();
+ LocationRequest request2 = new LocationRequest.Builder(1)
+ .setAdasGnssBypass(true)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+ mInjector.getLocationSettings().updateUserSettings(IDENTITY.getUserId(),
+ settings -> settings.withAdasGnssLocationEnabled(false));
+ mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+ assertThat(mProvider.getRequest().isAdasGnssBypass()).isFalse();
+ }
+
+ @Test
public void testProviderRequest_BatterySaver_ScreenOnOff() {
mInjector.getLocationPowerSaveModeHelper().setLocationPowerSaveMode(
LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
index 04e0151e619a..63996f0e021c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
+import android.content.Context;
import android.location.Location;
import android.location.LocationResult;
import android.location.provider.ProviderRequest;
@@ -58,6 +59,7 @@ public class StationaryThrottlingLocationProviderTest {
private TestInjector mInjector;
private FakeProvider mDelegateProvider;
+ private @Mock Context mContext;
private @Mock AbstractLocationProvider.Listener mListener;
private @Mock FakeProvider.FakeProviderInterface mDelegate;
@@ -72,7 +74,7 @@ public class StationaryThrottlingLocationProviderTest {
mRandom = new Random(seed);
- mInjector = new TestInjector();
+ mInjector = new TestInjector(mContext);
mDelegateProvider = new FakeProvider(mDelegate);
mProvider = new StationaryThrottlingLocationProvider("test_provider", mInjector,
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java b/services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java
new file mode 100644
index 000000000000..4d46abaa6733
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java
@@ -0,0 +1,54 @@
+/*
+ * 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.location.settings;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import java.io.File;
+
+public class FakeLocationSettings extends LocationSettings {
+
+ public FakeLocationSettings(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected File getUserSettingsDir(int userId) {
+ return ApplicationProvider.getApplicationContext().getCacheDir();
+ }
+
+ @Override
+ protected LocationUserSettingsStore createUserSettingsStore(int userId, File file) {
+ return new FakeLocationUserSettingsStore(userId, file);
+ }
+
+ private class FakeLocationUserSettingsStore extends LocationUserSettingsStore {
+
+ FakeLocationUserSettingsStore(int userId, File file) {
+ super(userId, file);
+ }
+
+ @Override
+ protected void onChange(LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ fireListeners(mUserId, oldSettings, newSettings);
+ }
+ }
+}
+
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java b/services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java
new file mode 100644
index 000000000000..4b6c79b954cd
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.location.settings;
+
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.io.File;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LocationSettingsTest {
+
+ private @Mock Context mContext;
+ private @Mock Resources mResources;
+ private @Mock PackageManager mPackageManager;
+
+ private LocationSettings mLocationSettings;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+
+ doReturn(mResources).when(mContext).getResources();
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+
+ resetLocationSettings();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mLocationSettings.deleteFiles();
+ }
+
+ private void resetLocationSettings() {
+ mLocationSettings = new LocationSettings(mContext) {
+ @Override
+ protected File getUserSettingsDir(int userId) {
+ return ApplicationProvider.getApplicationContext().getCacheDir();
+ }
+ };
+ }
+
+ @Test
+ public void testLoadDefaults() {
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+
+ doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ assertThat(mLocationSettings.getUserSettings(2).isAdasGnssLocationEnabled()).isFalse();
+ }
+
+ @Test
+ public void testUpdate() {
+ doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(false));
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
+ }
+
+ @Test
+ public void testSerialization() throws Exception {
+ doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+
+ mLocationSettings.flushFiles();
+ resetLocationSettings();
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+ }
+
+ @Test
+ public void testListeners() {
+ doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ LocationSettings.LocationUserSettingsListener listener = mock(
+ LocationSettings.LocationUserSettingsListener.class);
+
+ mLocationSettings.registerLocationUserSettingsListener(listener);
+
+ ArgumentCaptor<LocationUserSettings> oldCaptor = ArgumentCaptor.forClass(
+ LocationUserSettings.class);
+ ArgumentCaptor<LocationUserSettings> newCaptor = ArgumentCaptor.forClass(
+ LocationUserSettings.class);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ verify(listener, timeout(500).times(1)).onLocationUserSettingsChanged(eq(1),
+ oldCaptor.capture(), newCaptor.capture());
+ assertThat(oldCaptor.getValue().isAdasGnssLocationEnabled()).isFalse();
+ assertThat(newCaptor.getValue().isAdasGnssLocationEnabled()).isTrue();
+
+ oldCaptor = ArgumentCaptor.forClass(LocationUserSettings.class);
+ newCaptor = ArgumentCaptor.forClass(LocationUserSettings.class);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(false));
+ verify(listener, timeout(500).times(2)).onLocationUserSettingsChanged(eq(1),
+ oldCaptor.capture(), newCaptor.capture());
+ assertThat(oldCaptor.getValue().isAdasGnssLocationEnabled()).isTrue();
+ assertThat(newCaptor.getValue().isAdasGnssLocationEnabled()).isFalse();
+
+ mLocationSettings.unregisterLocationUserSettingsListener(listener);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ verify(listener, after(500).times(2)).onLocationUserSettingsChanged(anyInt(), any(), any());
+ }
+
+ @Test
+ public void testNonAutomotive() {
+ doReturn(false).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+ LocationSettings.LocationUserSettingsListener listener = mock(
+ LocationSettings.LocationUserSettingsListener.class);
+ mLocationSettings.registerLocationUserSettingsListener(listener);
+
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
+ verify(listener, after(500).never()).onLocationUserSettingsChanged(anyInt(), any(), any());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
index f4f907355aee..3c10789bc792 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -42,7 +42,7 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.compatibility.common.util.SystemUtil;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -67,7 +67,7 @@ public class AppSearchImplPlatformTest {
private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
private Context mContext;
private AppSearchImpl mAppSearchImpl;
- private VisibilityStore mVisibilityStore;
+ private VisibilityStoreImpl mVisibilityStore;
private int mGlobalQuerierUid;
@Before
@@ -93,7 +93,7 @@ public class AppSearchImplPlatformTest {
// Give ourselves global query permissions
mAppSearchImpl = AppSearchImpl.create(
mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
- mVisibilityStore = VisibilityStore.create(mAppSearchImpl, mContext);
+ mVisibilityStore = VisibilityStoreImpl.create(mAppSearchImpl, mContext);
mGlobalQuerierUid =
mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
index 183cb8603c33..07a728bac2a5 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
@@ -14,10 +14,7 @@
* limitations under the License.
*/
-// TODO(b/169883602): This is purposely a different package from the path so that it can access
-// AppSearchImpl methods without having to make methods public. This should be moved into a proper
-// package once AppSearchImpl-VisibilityStore's dependencies are refactored.
-package com.android.server.appsearch.external.localstorage;
+package com.android.server.appsearch.visibilitystore;
import static android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -39,8 +36,10 @@ import android.util.ArrayMap;
import androidx.test.core.app.ApplicationProvider;
+import com.android.server.appsearch.external.localstorage.AppSearchImpl;
+import com.android.server.appsearch.external.localstorage.OptimizeStrategy;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -55,7 +54,7 @@ import org.mockito.Mockito;
import java.util.Collections;
import java.util.Map;
-public class VisibilityStoreTest {
+public class VisibilityStoreImplTest {
/**
* Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class.
*/
@@ -64,7 +63,7 @@ public class VisibilityStoreTest {
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
private Context mContext;
- private VisibilityStore mVisibilityStore;
+ private VisibilityStoreImpl mVisibilityStore;
private int mUid;
@Before
@@ -90,7 +89,7 @@ public class VisibilityStoreTest {
// Give ourselves global query permissions
AppSearchImpl appSearchImpl = AppSearchImpl.create(
mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
- mVisibilityStore = VisibilityStore.create(appSearchImpl, mContext);
+ mVisibilityStore = VisibilityStoreImpl.create(appSearchImpl, mContext);
mUid = mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
}
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index f880563e2880..9044b27d4994 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -222,61 +222,61 @@ public class SystemConfigTest {
}
/**
- * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_APP_CONFIGS}
- * permission flag for the tag: {@code allowed-partner-apex}.
+ * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_VENDOR_APEX}
+ * permission flag for the tag: {@code allowed-vendor-apex}.
*/
@Test
- public void readPermissions_allowAppConfigs_parsesPartnerApexAllowList()
+ public void readPermissions_allowVendorApex_parsesVendorApexAllowList()
throws IOException {
final String contents =
"<config>\n"
- + " <allowed-partner-apex package=\"com.android.apex1\" />\n"
+ + " <allowed-vendor-apex package=\"com.android.apex1\" />\n"
+ "</config>";
final File folder = createTempSubfolder("folder");
- createTempFile(folder, "partner-apex-allowlist.xml", contents);
+ createTempFile(folder, "vendor-apex-allowlist.xml", contents);
mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0);
- assertThat(mSysConfig.getAllowedPartnerApexes()).containsExactly("com.android.apex1");
+ assertThat(mSysConfig.getAllowedVendorApexes()).containsExactly("com.android.apex1");
}
/**
- * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_APP_CONFIGS}
- * permission flag for the tag: {@code allowed-partner-apex}.
+ * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_VENDOR_APEX}
+ * permission flag for the tag: {@code allowed-vendor-apex}.
*/
@Test
- public void readPermissions_allowAppConfigs_parsesPartnerApexAllowList_noPackage()
+ public void readPermissions_allowVendorApex_parsesVendorApexAllowList_noPackage()
throws IOException {
final String contents =
"<config>\n"
- + " <allowed-partner-apex/>\n"
+ + " <allowed-vendor-apex/>\n"
+ "</config>";
final File folder = createTempSubfolder("folder");
- createTempFile(folder, "partner-apex-allowlist.xml", contents);
+ createTempFile(folder, "vendor-apex-allowlist.xml", contents);
mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0);
- assertThat(mSysConfig.getAllowedPartnerApexes()).isEmpty();
+ assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
}
/**
- * Tests that readPermissions works correctly without {@link SystemConfig#ALLOW_APP_CONFIGS}
- * permission flag for the tag: {@code allowed-partner-apex}.
+ * Tests that readPermissions works correctly without {@link SystemConfig#ALLOW_VENDOR_APEX}
+ * permission flag for the tag: {@code allowed-oem-apex}.
*/
@Test
- public void readPermissions_notAllowAppConfigs_doesNotParsePartnerApexAllowList()
+ public void readPermissions_notAllowVendorApex_doesNotParseVendorApexAllowList()
throws IOException {
final String contents =
"<config>\n"
- + " <allowed-partner-apex package=\"com.android.apex1\" />\n"
+ + " <allowed-vendor-apex package=\"com.android.apex1\" />\n"
+ "</config>";
final File folder = createTempSubfolder("folder");
- createTempFile(folder, "partner-apex-allowlist.xml", contents);
+ createTempFile(folder, "vendor-apex-allowlist.xml", contents);
- mSysConfig.readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08);
+ mSysConfig.readPermissions(folder, /* Grant all but ALLOW_VENDOR_APEX flag */ ~0x400);
- assertThat(mSysConfig.getAllowedPartnerApexes()).isEmpty();
+ assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
}
/**
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 3862d754b8f5..71c05b5c46f7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -48,6 +48,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
@@ -111,6 +112,8 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
NotificationUsageStats mUsageStats;
@Mock
IAccessibilityManager mAccessibilityService;
+ @Mock
+ KeyguardManager mKeyguardManager;
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
@@ -153,6 +156,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
long serviceReturnValue = IntPair.of(
AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
@@ -174,6 +178,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.setScreenOn(false);
mService.setUsageStats(mUsageStats);
mService.setAccessibilityManager(accessibilityManager);
+ mService.setKeyguardManager(mKeyguardManager);
mService.mScreenOn = false;
mService.mInCallStateOffHook = false;
mService.mNotificationPulseEnabled = true;
@@ -496,6 +501,94 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
}
@Test
+ public void testLockedPrivateA11yRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ r.getNotification().visibility = Notification.VISIBILITY_PRIVATE;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mService.setAccessibilityManager(accessibilityManager);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification().publicVersion, event.getParcelableData());
+ }
+
+ @Test
+ public void testLockedOverridePrivateA11yRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(Notification.VISIBILITY_PRIVATE);
+ r.getNotification().visibility = Notification.VISIBILITY_PUBLIC;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mService.setAccessibilityManager(accessibilityManager);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification().publicVersion, event.getParcelableData());
+ }
+
+ @Test
+ public void testLockedPublicA11yNoRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ r.getNotification().visibility = Notification.VISIBILITY_PUBLIC;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mService.setAccessibilityManager(accessibilityManager);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification(), event.getParcelableData());
+ }
+
+ @Test
+ public void testUnlockedPrivateA11yNoRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ r.getNotification().visibility = Notification.VISIBILITY_PRIVATE;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mService.setAccessibilityManager(accessibilityManager);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification(), event.getParcelableData());
+ }
+
+ @Test
public void testBeepInsistently() throws Exception {
NotificationRecord r = getInsistentBeepyNotification();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index a522b5c6161e..1f543a17885a 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -94,6 +94,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -2425,6 +2426,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
+ when(mPreferencesHelper.deleteNotificationChannel(eq(PKG), anyInt(),
+ eq(mTestNotificationChannel.getId()))).thenReturn(true);
reset(mListeners);
mBinderService.deleteNotificationChannel(PKG, mTestNotificationChannel.getId());
verify(mListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
@@ -2433,6 +2436,22 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testDeleteChannelOnlyDoExtraWorkIfExisted() throws Exception {
+ List<String> associations = new ArrayList<>();
+ associations.add("a");
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
+ eq(mTestNotificationChannel.getId()), anyBoolean()))
+ .thenReturn(null);
+ reset(mListeners);
+ mBinderService.deleteNotificationChannel(PKG, mTestNotificationChannel.getId());
+ verifyNoMoreInteractions(mListeners);
+ verifyNoMoreInteractions(mHistoryManager);
+ }
+
+ @Test
public void testDeleteChannelGroupNotifyListener() throws Exception {
List<String> associations = new ArrayList<>();
associations.add("a");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 3a51ff2fb5fa..23da02c0e4a1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -3333,6 +3333,17 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testDeleted_twice() throws Exception {
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager, mStatsEventBuilderFactory);
+
+ mHelper.createNotificationChannel(
+ PKG_P, UID_P, createNotificationChannel("id", "id", 2), true, false);
+ assertTrue(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id"));
+ assertFalse(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id"));
+ }
+
+ @Test
public void testDeleted_recentTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
mAppOpsManager, mStatsEventBuilderFactory);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1953af4adee5..e000265f0a2c 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1018,6 +1018,16 @@ public class TelecomManager {
// this magic number is a bug ID
public static final long ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION = 157233955L;
+ /**
+ * Enable READ_PHONE_NUMBERS or READ_PRIVILEGED_PHONE_STATE protections on
+ * {@link TelecomManager#getPhoneAccount(PhoneAccountHandle)}.
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ // bug ID
+ public static final long ENABLE_GET_PHONE_ACCOUNT_PERMISSION_PROTECTION = 183407956L;
+
private static final String TAG = "TelecomManager";
@@ -1351,6 +1361,9 @@ public class TelecomManager {
* Return the {@link PhoneAccount} for a specified {@link PhoneAccountHandle}. Object includes
* resources which can be used in a user interface.
*
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_NUMBERS} for applications targeting API
+ * level 31+.
* @param account The {@link PhoneAccountHandle}.
* @return The {@link PhoneAccount} object.
*/
@@ -1358,7 +1371,7 @@ public class TelecomManager {
ITelecomService service = getTelecomService();
if (service != null) {
try {
- return service.getPhoneAccount(account);
+ return service.getPhoneAccount(account, mContext.getPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getPhoneAccount", e);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 18afde742abb..6f286d9f3006 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -79,7 +79,7 @@ interface ITelecomService {
/**
* @see TelecomManager#getPhoneAccount
*/
- PhoneAccount getPhoneAccount(in PhoneAccountHandle account);
+ PhoneAccount getPhoneAccount(in PhoneAccountHandle account, String callingPackage);
/**
* @see TelecomManager#getAllPhoneAccountsCount