summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java5
-rw-r--r--apex/appsearch/framework/api/current.txt8
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java6
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java6
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java6
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java42
-rw-r--r--apex/appsearch/synced_jetpack_changeid.txt2
-rw-r--r--apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java2
-rw-r--r--core/api/current.txt5
-rw-r--r--core/api/system-current.txt4
-rw-r--r--core/java/android/accessibilityservice/AccessibilityGestureEvent.java8
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java4
-rw-r--r--core/java/android/app/ActivityThread.java22
-rw-r--r--core/java/android/app/ApplicationPackageManager.java2
-rw-r--r--core/java/android/app/ContextImpl.java8
-rw-r--r--core/java/android/app/GameManager.java6
-rw-r--r--core/java/android/app/LoadedApk.java29
-rw-r--r--core/java/android/app/ResourcesManager.java75
-rw-r--r--core/java/android/app/usage/IUsageStatsManager.aidl2
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java4
-rw-r--r--core/java/android/companion/Association.java31
-rw-r--r--core/java/android/companion/ICompanionDeviceManager.aidl2
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java23
-rw-r--r--core/java/android/content/pm/PackageParser.java8
-rw-r--r--core/java/android/content/pm/PackageUserState.java84
-rw-r--r--core/java/android/content/pm/overlay/OverlayPaths.java192
-rw-r--r--core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java7
-rw-r--r--core/java/android/content/res/ResourcesKey.java20
-rw-r--r--core/java/android/os/Process.java11
-rw-r--r--core/java/android/provider/FontsContract.java3
-rw-r--r--core/java/android/view/IWindowSession.aidl8
-rw-r--r--core/java/android/view/View.java6
-rw-r--r--core/java/android/view/ViewRootImpl.java16
-rw-r--r--core/java/android/view/WindowlessWindowManager.java4
-rw-r--r--core/java/android/view/autofill/AutofillManager.java33
-rw-r--r--core/proto/android/content/package_item_info.proto1
-rw-r--r--core/res/AndroidManifest.xml9
-rw-r--r--core/res/res/values/attrs.xml10
-rw-r--r--core/res/res/values/colors_device_defaults.xml2
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/res/res/values/themes_device_defaults.xml86
-rw-r--r--core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java12
-rw-r--r--core/tests/coretests/src/android/content/res/ResourcesManagerTest.java38
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java2
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java18
-rw-r--r--keystore/java/android/security/KeyStoreSecurityLevel.java2
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java3
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKey.java10
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java21
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt93
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt131
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt85
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt123
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt77
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt149
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt205
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt123
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt84
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt83
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt140
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt115
-rw-r--r--libs/hwui/DeviceInfo.h3
-rw-r--r--libs/hwui/Properties.cpp5
-rw-r--r--libs/hwui/Properties.h4
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp4
-rw-r--r--libs/hwui/jni/fonts/Font.cpp2
-rw-r--r--libs/hwui/jni/fonts/FontFamily.cpp4
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp49
-rw-r--r--libs/hwui/renderthread/CanvasContext.h7
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp5
-rw-r--r--libs/hwui/renderthread/RenderProxy.h17
-rw-r--r--libs/hwui/tests/common/TestScene.h1
-rw-r--r--libs/hwui/tests/macrobench/TestSceneRunner.cpp5
-rw-r--r--libs/hwui/tests/macrobench/main.cpp13
-rw-r--r--media/java/android/media/AudioDeviceInfo.java2
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl8
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java222
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java13
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java8
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java17
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java42
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java15
-rw-r--r--services/core/java/com/android/server/EntropyMixer.java51
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java4
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java50
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java2
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java84
-rw-r--r--services/core/java/com/android/server/location/eventlog/LocalEventLog.java130
-rw-r--r--services/core/java/com/android/server/location/eventlog/LocationEventLog.java (renamed from services/core/java/com/android/server/location/injector/LocationEventLog.java)240
-rw-r--r--services/core/java/com/android/server/location/injector/Injector.java3
-rw-r--r--services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java2
-rw-r--r--services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java1
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java40
-rw-r--r--services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java6
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java15
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java15
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerServiceImpl.java12
-rw-r--r--services/core/java/com/android/server/pm/InstantAppRegistry.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java95
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java17
-rw-r--r--services/core/java/com/android/server/pm/Settings.java70
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java89
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java13
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java65
-rw-r--r--services/core/java/com/android/server/utils/Watchable.java56
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java1
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/Session.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java16
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt666
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java15
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/EntropyMixerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java140
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt1
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java27
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java10
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java16
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java63
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl14
-rw-r--r--tests/ApkVerityTest/OWNERS3
-rw-r--r--tools/hiddenapi/Android.bp30
-rwxr-xr-xtools/hiddenapi/generate_hiddenapi_lists.py383
-rwxr-xr-xtools/hiddenapi/generate_hiddenapi_lists_test.py104
-rwxr-xr-xtools/hiddenapi/merge_csv.py69
159 files changed, 3508 insertions, 2274 deletions
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index d3938f4c0926..afd8e2948c41 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -77,7 +77,7 @@ public class ResourcesManagerPerfTest {
}
private void getResourcesForPath(String path) {
- ResourcesManager.getInstance().getResources(null, path, null, null, null,
+ ResourcesManager.getInstance().getResources(null, path, null, null, null, null,
Display.DEFAULT_DISPLAY, null, sContext.getResources().getCompatibilityInfo(),
null, null);
}
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
index f4c0a172710b..45c723bea9db 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
@@ -95,8 +95,9 @@ public class ResourcesThemePerfTest {
? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
Resources destResources = resourcesManager.getResources(null, ai.sourceDir,
- ai.splitSourceDirs, ai.resourceDirs, ai.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
- c, mContext.getResources().getCompatibilityInfo(), null, null);
+ ai.splitSourceDirs, ai.resourceDirs, ai.overlayPaths, ai.sharedLibraryFiles,
+ Display.DEFAULT_DISPLAY, c, mContext.getResources().getCompatibilityInfo(),
+ null, null);
Assert.assertNotEquals(destResources.getAssets(), mContext.getAssets());
Resources.Theme destTheme = destResources.newTheme();
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index a3b80139ced8..905000ac7c3d 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -208,8 +208,8 @@ package android.app.appsearch {
ctor public GetByUriRequest.Builder();
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.lang.String...);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
- method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.lang.String...);
- method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.GetByUriRequest build();
method @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String);
}
@@ -243,8 +243,8 @@ package android.app.appsearch {
public static final class RemoveByUriRequest.Builder {
ctor public RemoveByUriRequest.Builder();
- method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.lang.String...);
- method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.RemoveByUriRequest build();
method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
index 0fcf0613dd38..656608d82ad4 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -122,14 +122,14 @@ public final class GetByUriRequest {
/** Adds one or more URIs to the request. */
@NonNull
- public Builder addUri(@NonNull String... uris) {
+ public Builder addUris(@NonNull String... uris) {
Preconditions.checkNotNull(uris);
- return addUri(Arrays.asList(uris));
+ return addUris(Arrays.asList(uris));
}
/** Adds one or more URIs to the request. */
@NonNull
- public Builder addUri(@NonNull Collection<String> uris) {
+ public Builder addUris(@NonNull Collection<String> uris) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(uris);
mUris.addAll(uris);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
index 2104198d371c..198eee85be53 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -73,14 +73,14 @@ public final class RemoveByUriRequest {
/** Adds one or more URIs to the request. */
@NonNull
- public Builder addUri(@NonNull String... uris) {
+ public Builder addUris(@NonNull String... uris) {
Preconditions.checkNotNull(uris);
- return addUri(Arrays.asList(uris));
+ return addUris(Arrays.asList(uris));
}
/** Adds one or more URIs to the request. */
@NonNull
- public Builder addUri(@NonNull Collection<String> uris) {
+ public Builder addUris(@NonNull Collection<String> uris) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(uris);
mUris.addAll(uris);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
index 0328d5453ab8..4869aa38b5fd 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -163,7 +163,7 @@ public class SetSchemaResponse {
/** Adds deletedTypes to the list of deleted schema types. */
@NonNull
- public Builder addDeletedType(@NonNull Collection<String> deletedTypes) {
+ public Builder addDeletedTypes(@NonNull Collection<String> deletedTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mDeletedTypes.addAll(Preconditions.checkNotNull(deletedTypes));
return this;
@@ -171,7 +171,7 @@ public class SetSchemaResponse {
/** Adds incompatibleTypes to the list of incompatible schema types. */
@NonNull
- public Builder addIncompatibleType(@NonNull Collection<String> incompatibleTypes) {
+ public Builder addIncompatibleTypes(@NonNull Collection<String> incompatibleTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mIncompatibleTypes.addAll(Preconditions.checkNotNull(incompatibleTypes));
return this;
@@ -179,7 +179,7 @@ public class SetSchemaResponse {
/** Adds migratedTypes to the list of migrated schema types. */
@NonNull
- public Builder addMigratedType(@NonNull Collection<String> migratedTypes) {
+ public Builder addMigratedTypes(@NonNull Collection<String> migratedTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mMigratedTypes.addAll(Preconditions.checkNotNull(migratedTypes));
return this;
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
index 1b4d28401ea0..14dd472c9b9c 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
@@ -147,35 +147,41 @@ public final class BundleUtil {
if (bundle == null) {
return 0;
}
- int[] hashCodes = new int[bundle.size()];
- int i = 0;
+ int[] hashCodes = new int[bundle.size() + 1];
+ int hashCodeIdx = 0;
// Bundle inherit its hashCode() from Object.java, which only relative to their memory
// address. Bundle doesn't have an order, so we should iterate all keys and combine
// their value's hashcode into an array. And use the hashcode of the array to be
// the hashcode of the bundle.
- for (String key : bundle.keySet()) {
- Object value = bundle.get(key);
+ // Because bundle.keySet() doesn't guarantee any particular order, we need to sort the keys
+ // in case the iteration order varies from run to run.
+ String[] keys = bundle.keySet().toArray(new String[0]);
+ Arrays.sort(keys);
+ // Hash the keys so we can detect key-only differences
+ hashCodes[hashCodeIdx++] = Arrays.hashCode(keys);
+ for (int keyIdx = 0; keyIdx < keys.length; keyIdx++) {
+ Object value = bundle.get(keys[keyIdx]);
if (value instanceof Bundle) {
- hashCodes[i++] = deepHashCode((Bundle) value);
+ hashCodes[hashCodeIdx++] = deepHashCode((Bundle) value);
} else if (value instanceof int[]) {
- hashCodes[i++] = Arrays.hashCode((int[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((int[]) value);
} else if (value instanceof byte[]) {
- hashCodes[i++] = Arrays.hashCode((byte[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((byte[]) value);
} else if (value instanceof char[]) {
- hashCodes[i++] = Arrays.hashCode((char[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((char[]) value);
} else if (value instanceof long[]) {
- hashCodes[i++] = Arrays.hashCode((long[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((long[]) value);
} else if (value instanceof float[]) {
- hashCodes[i++] = Arrays.hashCode((float[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((float[]) value);
} else if (value instanceof short[]) {
- hashCodes[i++] = Arrays.hashCode((short[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((short[]) value);
} else if (value instanceof double[]) {
- hashCodes[i++] = Arrays.hashCode((double[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((double[]) value);
} else if (value instanceof boolean[]) {
- hashCodes[i++] = Arrays.hashCode((boolean[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((boolean[]) value);
} else if (value instanceof String[]) {
// Optimization to avoid Object[] handler creating an inner array for common cases
- hashCodes[i++] = Arrays.hashCode((String[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((String[]) value);
} else if (value instanceof Object[]) {
Object[] array = (Object[]) value;
int[] innerHashCodes = new int[array.length];
@@ -186,7 +192,7 @@ public final class BundleUtil {
innerHashCodes[j] = array[j].hashCode();
}
}
- hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes);
} else if (value instanceof ArrayList) {
ArrayList<?> list = (ArrayList<?>) value;
int[] innerHashCodes = new int[list.size()];
@@ -198,7 +204,7 @@ public final class BundleUtil {
innerHashCodes[j] = item.hashCode();
}
}
- hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes);
} else if (value instanceof SparseArray) {
SparseArray<?> array = (SparseArray<?>) value;
int[] innerHashCodes = new int[array.size() * 2];
@@ -211,9 +217,9 @@ public final class BundleUtil {
innerHashCodes[j * 2 + 1] = item.hashCode();
}
}
- hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes);
} else {
- hashCodes[i++] = value.hashCode();
+ hashCodes[hashCodeIdx++] = value.hashCode();
}
}
return Arrays.hashCode(hashCodes);
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 12699b7815fb..6ba557224582 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Ibe06fb9c574c8718191f833bb042fa10c300e4e2
+I2bf8bd9db1b71b7da4ab50dd7480e4529678413a
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
index 20fb90986f41..44d5180c3a36 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
@@ -59,7 +59,7 @@ public class AppSearchTestUtils {
session.getByUri(
new GetByUriRequest.Builder()
.setNamespace(namespace)
- .addUri(uris)
+ .addUris(uris)
.build()));
assertThat(result.getSuccesses()).hasSize(uris.length);
assertThat(result.getFailures()).isEmpty();
diff --git a/core/api/current.txt b/core/api/current.txt
index 838dd55e34d5..916d25ec4f81 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -435,6 +435,7 @@ package android {
field public static final int clickable = 16842981; // 0x10100e5
field public static final int clipChildren = 16842986; // 0x10100ea
field public static final int clipOrientation = 16843274; // 0x101020a
+ field public static final int clipToOutline = 16844328; // 0x1010628
field public static final int clipToPadding = 16842987; // 0x10100eb
field public static final int closeIcon = 16843905; // 0x1010481
field @Deprecated public static final int codes = 16843330; // 0x1010242
@@ -2936,12 +2937,12 @@ package android.accessibilityservice {
field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
- field public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; // 0x2b
field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c
field public static final int GESTURE_2_FINGER_SWIPE_UP = 25; // 0x19
field public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; // 0x15
+ field public static final int GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD = 43; // 0x2b
field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29
field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
@@ -40338,6 +40339,7 @@ package android.telephony {
field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
+ field public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL = "store_sim_pin_for_unattended_reboot_bool";
field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
field public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = "support_add_conference_participants_bool";
@@ -50687,6 +50689,7 @@ package android.view.autofill {
method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
+ field public static final String EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET = "android.view.autofill.extra.AUTHENTICATION_RESULT_EPHEMERAL_DATASET";
field public static final String EXTRA_CLIENT_STATE = "android.view.autofill.extra.CLIENT_STATE";
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5bb3e0517032..058e5a872b6a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -11869,6 +11869,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean matchesCurrentSimOperator(@NonNull String, int, @Nullable String);
method public boolean needsOtaServiceProvisioning();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
+ method @RequiresPermission(android.Manifest.permission.REBOOT) public int prepareForUnattendedReboot();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
@@ -11993,6 +11994,9 @@ package android.telephony {
field public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2; // 0x2
field public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3; // 0x3
field public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1; // 0x1
+ field public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2; // 0x2
+ field public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1; // 0x1
+ field public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0; // 0x0
field public static final int RADIO_POWER_OFF = 0; // 0x0
field public static final int RADIO_POWER_ON = 1; // 0x1
field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 4b2d74160006..768ec3851991 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -20,12 +20,12 @@ package android.accessibilityservice;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
-import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
@@ -97,10 +97,10 @@ public final class AccessibilityGestureEvent implements Parcelable {
GESTURE_UNKNOWN,
GESTURE_TOUCH_EXPLORATION,
GESTURE_2_FINGER_SINGLE_TAP,
- GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD,
GESTURE_2_FINGER_DOUBLE_TAP,
GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD,
GESTURE_2_FINGER_TRIPLE_TAP,
+ GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD,
GESTURE_3_FINGER_SINGLE_TAP,
GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD,
GESTURE_3_FINGER_DOUBLE_TAP,
@@ -232,8 +232,8 @@ public final class AccessibilityGestureEvent implements Parcelable {
case GESTURE_PASSTHROUGH: return "GESTURE_PASSTHROUGH";
case GESTURE_TOUCH_EXPLORATION: return "GESTURE_TOUCH_EXPLORATION";
case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP";
- case GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD:
- return "GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD";
+ case GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD:
+ return "GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD";
case GESTURE_2_FINGER_DOUBLE_TAP: return "GESTURE_2_FINGER_DOUBLE_TAP";
case GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD:
return "GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD";
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 1fa7fa2b744f..dab4a5dc316c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -445,8 +445,8 @@ public abstract class AccessibilityService extends Service {
/** The user has performed a three-finger double tap and hold gesture on the touch screen. */
public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41;
- /** The user has performed a two-finger single-tap and hold gesture on the touch screen. */
- public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43;
+ /** The user has performed a two-finger triple-tap and hold gesture on the touch screen. */
+ public static final int GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD = 43;
/** The user has performed a three-finger single-tap and hold gesture on the touch screen. */
public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0774ac134d1b..bb6a774cbee2 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2289,11 +2289,12 @@ public final class ActivityThread extends ClientTransactionHandler {
* Creates the top level resources for the given package. Will return an existing
* Resources if one has already been created.
*/
- Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
- String[] libDirs, LoadedApk pkgInfo, Configuration overrideConfig) {
- return mResourcesManager.getResources(null, resDir, splitResDirs, overlayDirs, libDirs,
- null, overrideConfig, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(),
- null);
+ Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] legacyOverlayDirs,
+ String[] overlayPaths, String[] libDirs, LoadedApk pkgInfo,
+ Configuration overrideConfig) {
+ return mResourcesManager.getResources(null, resDir, splitResDirs, legacyOverlayDirs,
+ overlayPaths, libDirs, null, overrideConfig, pkgInfo.getCompatibilityInfo(),
+ pkgInfo.getClassLoader(), null);
}
@UnsupportedAppUsage
@@ -2462,12 +2463,15 @@ public final class ActivityThread extends ClientTransactionHandler {
private static boolean isLoadedApkResourceDirsUpToDate(LoadedApk loadedApk,
ApplicationInfo appInfo) {
Resources packageResources = loadedApk.mResources;
- String[] overlayDirs = ArrayUtils.defeatNullable(loadedApk.getOverlayDirs());
- String[] resourceDirs = ArrayUtils.defeatNullable(appInfo.resourceDirs);
+ boolean resourceDirsUpToDate = Arrays.equals(
+ ArrayUtils.defeatNullable(appInfo.resourceDirs),
+ ArrayUtils.defeatNullable(loadedApk.getOverlayDirs()));
+ boolean overlayPathsUpToDate = Arrays.equals(
+ ArrayUtils.defeatNullable(appInfo.overlayPaths),
+ ArrayUtils.defeatNullable(loadedApk.getOverlayPaths()));
return (packageResources == null || packageResources.getAssets().isUpToDate())
- && overlayDirs.length == resourceDirs.length
- && ArrayUtils.containsAll(overlayDirs, resourceDirs);
+ && resourceDirsUpToDate && overlayPathsUpToDate;
}
@UnsupportedAppUsage
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 8ac91396a6b0..062cab457ebe 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1728,7 +1728,7 @@ public class ApplicationPackageManager extends PackageManager {
final Resources r = mContext.mMainThread.getTopLevelResources(
sameUid ? app.sourceDir : app.publicSourceDir,
sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
- app.resourceDirs, app.sharedLibraryFiles,
+ app.resourceDirs, app.overlayPaths, app.sharedLibraryFiles,
mContext.mPackageInfo, configuration);
if (r != null) {
return r;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4ddeb8fbfbef..9a20e0fefd33 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2345,6 +2345,7 @@ class ContextImpl extends Context {
pi.getResDir(),
splitResDirs,
pi.getOverlayDirs(),
+ pi.getOverlayPaths(),
pi.getApplicationInfo().sharedLibraryFiles,
overrideDisplayId,
overrideConfig,
@@ -2442,6 +2443,7 @@ class ContextImpl extends Context {
mPackageInfo.getResDir(),
paths,
mPackageInfo.getOverlayDirs(),
+ mPackageInfo.getOverlayPaths(),
mPackageInfo.getApplicationInfo().sharedLibraryFiles,
mForceDisplayOverrideInResources ? getDisplayId() : null,
null,
@@ -2558,7 +2560,8 @@ class ContextImpl extends Context {
Resources createWindowContextResources() {
final String resDir = mPackageInfo.getResDir();
final String[] splitResDirs = mPackageInfo.getSplitResDirs();
- final String[] overlayDirs = mPackageInfo.getOverlayDirs();
+ final String[] legacyOverlayDirs = mPackageInfo.getOverlayDirs();
+ final String[] overlayPaths = mPackageInfo.getOverlayPaths();
final String[] libDirs = mPackageInfo.getApplicationInfo().sharedLibraryFiles;
final int displayId = getDisplayId();
final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
@@ -2567,7 +2570,7 @@ class ContextImpl extends Context {
final List<ResourcesLoader> loaders = mResources.getLoaders();
return mResourcesManager.createBaseTokenResources(mToken, resDir, splitResDirs,
- overlayDirs, libDirs, displayId, null /* overrideConfig */,
+ legacyOverlayDirs, overlayPaths, libDirs, displayId, null /* overrideConfig */,
compatInfo, mClassLoader, loaders);
}
@@ -2855,6 +2858,7 @@ class ContextImpl extends Context {
packageInfo.getResDir(),
splitDirs,
packageInfo.getOverlayDirs(),
+ packageInfo.getOverlayPaths(),
packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 8b6570f1241f..ac1fa1ec6837 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -16,7 +16,9 @@
package android.app;
+import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
import android.content.Context;
@@ -73,8 +75,8 @@ public final class GameManager {
/**
* Returns the game mode for the given package.
*/
- // TODO(b/178111358): Add @RequiresPermission.
@UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public @GameMode int getGameMode(String packageName) {
try {
return mService.getGameMode(packageName, mContext.getUserId());
@@ -86,8 +88,8 @@ public final class GameManager {
/**
* Sets the game mode for the given package.
*/
- // TODO(b/178111358): Add @RequiresPermission.
@UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public void setGameMode(String packageName, @GameMode int gameMode) {
try {
mService.setGameMode(packageName, gameMode, mContext.getUserId());
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index c01b5a32b98b..be426aa7ed2b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -113,7 +113,8 @@ public final class LoadedApk {
private String mAppDir;
@UnsupportedAppUsage
private String mResDir;
- private String[] mOverlayDirs;
+ private String[] mLegacyOverlayDirs;
+ private String[] mOverlayPaths;
@UnsupportedAppUsage
private String mDataDir;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -222,7 +223,8 @@ public final class LoadedApk {
mSplitAppDirs = null;
mSplitResDirs = null;
mSplitClassLoaderNames = null;
- mOverlayDirs = null;
+ mLegacyOverlayDirs = null;
+ mOverlayPaths = null;
mDataDir = null;
mDataDirFile = null;
mDeviceProtectedDataDirFile = null;
@@ -364,8 +366,8 @@ public final class LoadedApk {
}
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
- splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
- null, null, getCompatibilityInfo(),
+ splitPaths, mLegacyOverlayDirs, mOverlayPaths,
+ mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(),
getClassLoader(), mApplication == null ? null
: mApplication.getResources().getLoaders());
}
@@ -379,7 +381,8 @@ public final class LoadedApk {
mApplicationInfo = aInfo;
mAppDir = aInfo.sourceDir;
mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;
- mOverlayDirs = aInfo.resourceDirs;
+ mLegacyOverlayDirs = aInfo.resourceDirs;
+ mOverlayPaths = aInfo.overlayPaths;
mDataDir = aInfo.dataDir;
mLibDir = aInfo.nativeLibraryDir;
mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir);
@@ -1213,9 +1216,19 @@ public final class LoadedApk {
return mSplitResDirs;
}
+ /**
+ * Corresponds to {@link ApplicationInfo#resourceDirs}.
+ */
@UnsupportedAppUsage
public String[] getOverlayDirs() {
- return mOverlayDirs;
+ return mLegacyOverlayDirs;
+ }
+
+ /**
+ * Corresponds to {@link ApplicationInfo#overlayPaths}.
+ */
+ public String[] getOverlayPaths() {
+ return mOverlayPaths;
}
public String getDataDir() {
@@ -1252,8 +1265,8 @@ public final class LoadedApk {
}
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
- splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
- null, null, getCompatibilityInfo(),
+ splitPaths, mLegacyOverlayDirs, mOverlayPaths,
+ mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(),
getClassLoader(), null);
}
return mResources;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 772833cc6d2d..ac8d3a261ac6 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -39,6 +39,7 @@ import android.os.IBinder;
import android.os.Process;
import android.os.Trace;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
@@ -60,6 +61,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.WeakHashMap;
@@ -174,8 +176,8 @@ public class ResourcesManager {
* based on.
*
* @see #activityResources
- * @see #getResources(IBinder, String, String[], String[], String[], Integer, Configuration,
- * CompatibilityInfo, ClassLoader, List)
+ * @see #getResources(IBinder, String, String[], String[], String[], String[], Integer,
+ * Configuration, CompatibilityInfo, ClassLoader, List)
*/
public final Configuration overrideConfig = new Configuration();
@@ -482,8 +484,8 @@ public class ResourcesManager {
}
}
- if (key.mOverlayDirs != null) {
- for (final String idmapPath : key.mOverlayDirs) {
+ if (key.mOverlayPaths != null) {
+ for (final String idmapPath : key.mOverlayPaths) {
apkKeys.add(new ApkKey(idmapPath, false /*sharedLib*/, true /*overlay*/));
}
}
@@ -783,14 +785,16 @@ public class ResourcesManager {
/**
* Creates base resources for a binder token. Calls to
- * {@link #getResources(IBinder, String, String[], String[], String[], Integer, Configuration,
- * CompatibilityInfo, ClassLoader, List)} with the same binder token will have their override
- * configurations merged with the one specified here.
+ *
+ * {@link #getResources(IBinder, String, String[], String[], String[], String[], Integer,
+ * Configuration, CompatibilityInfo, ClassLoader, List)} with the same binder token will have
+ * their override configurations merged with the one specified here.
*
* @param token Represents an {@link Activity} or {@link WindowContext}.
* @param resDir The base resource path. Can be null (only framework resources will be loaded).
* @param splitResDirs An array of split resource paths. Can be null.
- * @param overlayDirs An array of overlay paths. Can be null.
+ * @param legacyOverlayDirs An array of overlay APK paths. Can be null.
+ * @param overlayPaths An array of overlay APK and non-APK paths. Can be null.
* @param libDirs An array of resource library paths. Can be null.
* @param displayId The ID of the display for which to create the resources.
* @param overrideConfig The configuration to apply on top of the base configuration. Can be
@@ -804,7 +808,8 @@ public class ResourcesManager {
public @Nullable Resources createBaseTokenResources(@NonNull IBinder token,
@Nullable String resDir,
@Nullable String[] splitResDirs,
- @Nullable String[] overlayDirs,
+ @Nullable String[] legacyOverlayDirs,
+ @Nullable String[] overlayPaths,
@Nullable String[] libDirs,
int displayId,
@Nullable Configuration overrideConfig,
@@ -817,7 +822,7 @@ public class ResourcesManager {
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
- overlayDirs,
+ combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
libDirs,
displayId,
overrideConfig,
@@ -1043,7 +1048,8 @@ public class ResourcesManager {
* @param activityToken Represents an Activity. If null, global resources are assumed.
* @param resDir The base resource path. Can be null (only framework resources will be loaded).
* @param splitResDirs An array of split resource paths. Can be null.
- * @param overlayDirs An array of overlay paths. Can be null.
+ * @param legacyOverlayDirs An array of overlay APK paths. Can be null.
+ * @param overlayPaths An array of overlay APK and non-APK paths. Can be null.
* @param libDirs An array of resource library paths. Can be null.
* @param overrideDisplayId The ID of the display for which the returned Resources should be
* based. This will cause display-based configuration properties to override those of the base
@@ -1063,7 +1069,8 @@ public class ResourcesManager {
@Nullable IBinder activityToken,
@Nullable String resDir,
@Nullable String[] splitResDirs,
- @Nullable String[] overlayDirs,
+ @Nullable String[] legacyOverlayDirs,
+ @Nullable String[] overlayPaths,
@Nullable String[] libDirs,
@Nullable Integer overrideDisplayId,
@Nullable Configuration overrideConfig,
@@ -1075,7 +1082,7 @@ public class ResourcesManager {
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
- overlayDirs,
+ combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
libDirs,
overrideDisplayId != null ? overrideDisplayId : INVALID_DISPLAY,
overrideConfig,
@@ -1250,7 +1257,7 @@ public class ResourcesManager {
// Create the new ResourcesKey with the rebased override config.
final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
- oldKey.mSplitResDirs, oldKey.mOverlayDirs, oldKey.mLibDirs,
+ oldKey.mSplitResDirs, oldKey.mOverlayPaths, oldKey.mLibDirs,
displayId, rebasedOverrideConfig, oldKey.mCompatInfo, oldKey.mLoaders);
if (DEBUG) {
@@ -1393,7 +1400,7 @@ public class ResourcesManager {
updatedResourceKeys.put(impl, new ResourcesKey(
key.mResDir,
key.mSplitResDirs,
- key.mOverlayDirs,
+ key.mOverlayPaths,
newLibAssets,
key.mDisplayId,
key.mOverrideConfiguration,
@@ -1423,7 +1430,8 @@ public class ResourcesManager {
// ApplicationInfo is mutable, so clone the arrays to prevent outside modification
String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
- String[] copiedResourceDirs = ArrayUtils.cloneOrNull(appInfo.resourceDirs);
+ String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs,
+ appInfo.overlayPaths);
final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
final int implCount = mResourceImpls.size();
@@ -1458,6 +1466,39 @@ public class ResourcesManager {
}
}
+ /**
+ * Creates an array with the contents of {@param overlayPaths} and the unique elements of
+ * {@param resourceDirs}.
+ *
+ * {@link ApplicationInfo#resourceDirs} only contains paths of overlays APKs.
+ * {@link ApplicationInfo#overlayPaths} was created to contain paths of overlay of varying file
+ * formats. It also contains the contents of {@code resourceDirs} because the order of loaded
+ * overlays matter. In case {@code resourceDirs} contains overlay APK paths that are not present
+ * in overlayPaths (perhaps an app inserted an additional overlay path into a
+ * {@code resourceDirs}), this method is used to combine the contents of {@code resourceDirs}
+ * that do not exist in {@code overlayPaths}} and {@code overlayPaths}}.
+ */
+ @Nullable
+ private static String[] combinedOverlayPaths(@Nullable String[] resourceDirs,
+ @Nullable String[] overlayPaths) {
+ if (resourceDirs == null) {
+ return ArrayUtils.cloneOrNull(overlayPaths);
+ } else if(overlayPaths == null) {
+ return ArrayUtils.cloneOrNull(resourceDirs);
+ } else {
+ final ArrayList<String> paths = new ArrayList<>();
+ for (final String path : overlayPaths) {
+ paths.add(path);
+ }
+ for (final String path : resourceDirs) {
+ if (!paths.contains(path)) {
+ paths.add(path);
+ }
+ }
+ return paths.toArray(new String[0]);
+ }
+ }
+
private void redirectResourcesToNewImplLocked(
@NonNull final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys) {
// Bail early if there is no work to do.
@@ -1559,7 +1600,7 @@ public class ResourcesManager {
final ResourcesKey newKey = new ResourcesKey(
oldKey.mResDir,
oldKey.mSplitResDirs,
- oldKey.mOverlayDirs,
+ oldKey.mOverlayPaths,
oldKey.mLibDirs,
oldKey.mDisplayId,
oldKey.mOverrideConfiguration,
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 2c1e951b6128..30ea5c476191 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -30,7 +30,7 @@ import java.util.Map;
interface IUsageStatsManager {
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
ParceledListSlice queryUsageStats(int bucketType, long beginTime, long endTime,
- String callingPackage);
+ String callingPackage, int userId);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
String callingPackage);
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index f74d16ee9238..31781ec79203 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -23,6 +23,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
import android.app.Activity;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
@@ -437,11 +438,12 @@ public final class UsageStatsManager {
* @see #INTERVAL_YEARLY
* @see #INTERVAL_BEST
*/
+ @UserHandleAware
public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
try {
@SuppressWarnings("unchecked")
ParceledListSlice<UsageStats> slice = mService.queryUsageStats(intervalType, beginTime,
- endTime, mContext.getOpPackageName());
+ endTime, mContext.getOpPackageName(), mContext.getUserId());
if (slice != null) {
return slice.getList();
}
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java
index 960a08755cb8..9007d9d8bbcc 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/Association.java
@@ -23,6 +23,7 @@ import android.os.Parcelable;
import com.android.internal.util.DataClass;
+import java.util.Date;
import java.util.Objects;
/**
@@ -39,12 +40,19 @@ public final class Association implements Parcelable {
private final @NonNull String mPackageName;
private final @Nullable String mDeviceProfile;
private final boolean mNotifyOnDeviceNearby;
+ private final long mTimeApprovedMs;
/** @hide */
public int getUserId() {
return mUserId;
}
+ private String timeApprovedMsToString() {
+ return new Date(mTimeApprovedMs).toString();
+ }
+
+
+
// Code below generated by codegen v1.0.22.
@@ -71,7 +79,8 @@ public final class Association implements Parcelable {
@NonNull String deviceMacAddress,
@NonNull String packageName,
@Nullable String deviceProfile,
- boolean notifyOnDeviceNearby) {
+ boolean notifyOnDeviceNearby,
+ long timeApprovedMs) {
this.mUserId = userId;
com.android.internal.util.AnnotationValidations.validate(
UserIdInt.class, null, mUserId);
@@ -83,6 +92,7 @@ public final class Association implements Parcelable {
NonNull.class, null, mPackageName);
this.mDeviceProfile = deviceProfile;
this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+ this.mTimeApprovedMs = timeApprovedMs;
// onConstructed(); // You can define this method to get a callback
}
@@ -107,6 +117,11 @@ public final class Association implements Parcelable {
return mNotifyOnDeviceNearby;
}
+ @DataClass.Generated.Member
+ public long getTimeApprovedMs() {
+ return mTimeApprovedMs;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -118,7 +133,8 @@ public final class Association implements Parcelable {
"deviceMacAddress = " + mDeviceMacAddress + ", " +
"packageName = " + mPackageName + ", " +
"deviceProfile = " + mDeviceProfile + ", " +
- "notifyOnDeviceNearby = " + mNotifyOnDeviceNearby +
+ "notifyOnDeviceNearby = " + mNotifyOnDeviceNearby + ", " +
+ "timeApprovedMs = " + timeApprovedMsToString() +
" }";
}
@@ -139,7 +155,8 @@ public final class Association implements Parcelable {
&& Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
&& Objects.equals(mPackageName, that.mPackageName)
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
- && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby;
+ && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
+ && mTimeApprovedMs == that.mTimeApprovedMs;
}
@Override
@@ -154,6 +171,7 @@ public final class Association implements Parcelable {
_hash = 31 * _hash + Objects.hashCode(mPackageName);
_hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
_hash = 31 * _hash + Boolean.hashCode(mNotifyOnDeviceNearby);
+ _hash = 31 * _hash + Long.hashCode(mTimeApprovedMs);
return _hash;
}
@@ -171,6 +189,7 @@ public final class Association implements Parcelable {
dest.writeString(mDeviceMacAddress);
dest.writeString(mPackageName);
if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
+ dest.writeLong(mTimeApprovedMs);
}
@Override
@@ -190,6 +209,7 @@ public final class Association implements Parcelable {
String deviceMacAddress = in.readString();
String packageName = in.readString();
String deviceProfile = (flg & 0x8) == 0 ? null : in.readString();
+ long timeApprovedMs = in.readLong();
this.mUserId = userId;
com.android.internal.util.AnnotationValidations.validate(
@@ -202,6 +222,7 @@ public final class Association implements Parcelable {
NonNull.class, null, mPackageName);
this.mDeviceProfile = deviceProfile;
this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+ this.mTimeApprovedMs = timeApprovedMs;
// onConstructed(); // You can define this method to get a callback
}
@@ -221,10 +242,10 @@ public final class Association implements Parcelable {
};
@DataClass.Generated(
- time = 1610482674799L,
+ time = 1612832377589L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/companion/Association.java",
- inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mNotifyOnDeviceNearby\npublic int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mNotifyOnDeviceNearby\nprivate final long mTimeApprovedMs\npublic int getUserId()\nprivate java.lang.String timeApprovedMsToString()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 527d8df94ea0..95d3515abb80 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -49,4 +49,6 @@ interface ICompanionDeviceManager {
void registerDevicePresenceListenerService(in String packageName, in String deviceAddress);
void unregisterDevicePresenceListenerService(in String packageName, in String deviceAddress);
+
+ boolean canPairWithoutPrompt(in String packageName, in String deviceMacAddress, int userId);
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6ec11693d69b..01ff4326a800 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -923,6 +923,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public String[] resourceDirs;
/**
+ * Contains the contents of {@link #resourceDirs} and along with paths for overlays that may or
+ * may not be APK packages.
+ *
+ * {@hide}
+ */
+ public String[] overlayPaths;
+
+ /**
* String retrieved from the seinfo tag found in selinux policy. This value can be set through
* the mac_permissions.xml policy construct. This value is used for setting an SELinux security
* context on the process as well as its data directory.
@@ -1472,6 +1480,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
if (resourceDirs != null) {
pw.println(prefix + "resourceDirs=" + Arrays.toString(resourceDirs));
}
+ if (overlayPaths != null) {
+ pw.println(prefix + "overlayPaths=" + Arrays.toString(overlayPaths));
+ }
if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && seInfo != null) {
pw.println(prefix + "seinfo=" + seInfo);
pw.println(prefix + "seinfoUser=" + seInfoUser);
@@ -1568,6 +1579,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
proto.write(ApplicationInfoProto.RESOURCE_DIRS, dir);
}
}
+ if (overlayPaths != null) {
+ for (String dir : overlayPaths) {
+ proto.write(ApplicationInfoProto.OVERLAY_PATHS, dir);
+ }
+ }
proto.write(ApplicationInfoProto.DATA_DIR, dataDir);
proto.write(ApplicationInfoProto.CLASS_LOADER_NAME, classLoaderName);
if (!ArrayUtils.isEmpty(splitClassLoaderNames)) {
@@ -1717,6 +1733,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
primaryCpuAbi = orig.primaryCpuAbi;
secondaryCpuAbi = orig.secondaryCpuAbi;
resourceDirs = orig.resourceDirs;
+ overlayPaths = orig.overlayPaths;
seInfo = orig.seInfo;
seInfoUser = orig.seInfoUser;
sharedLibraryFiles = orig.sharedLibraryFiles;
@@ -1803,6 +1820,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeString8(primaryCpuAbi);
dest.writeString8(secondaryCpuAbi);
dest.writeString8Array(resourceDirs);
+ dest.writeString8Array(overlayPaths);
dest.writeString8(seInfo);
dest.writeString8(seInfoUser);
dest.writeString8Array(sharedLibraryFiles);
@@ -1886,6 +1904,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
primaryCpuAbi = source.readString8();
secondaryCpuAbi = source.readString8();
resourceDirs = source.createString8Array();
+ overlayPaths = source.createString8Array();
seInfo = source.readString8();
seInfoUser = source.readString8();
sharedLibraryFiles = source.createString8Array();
@@ -2282,7 +2301,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* @hide
*/
public String[] getAllApkPaths() {
- final String[][] inputLists = { splitSourceDirs, sharedLibraryFiles, resourceDirs };
+ final String[][] inputLists = {
+ splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths
+ };
final List<String> output = new ArrayList<>(10);
if (sourceDir != null) {
output.add(sourceDir);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e6c0f6a4c2fa..0819d1743ad6 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -54,6 +54,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.split.SplitAssetLoader;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
@@ -7969,7 +7970,11 @@ public class PackageParser {
ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
}
ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
- ai.resourceDirs = state.getAllOverlayPaths();
+ final OverlayPaths overlayPaths = state.getAllOverlayPaths();
+ if (overlayPaths != null) {
+ ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
+ ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
+ }
ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes;
}
@@ -8600,6 +8605,7 @@ public class PackageParser {
null,
null,
androidAppInfo.resourceDirs,
+ androidAppInfo.overlayPaths,
androidAppInfo.sharedLibraryFiles,
null,
null,
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 5cc74c0a1c8e..e115597865b3 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -31,6 +31,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.BaseBundle;
import android.os.Debug;
@@ -53,7 +54,6 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.Arrays;
-import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
@@ -85,9 +85,10 @@ public class PackageUserState {
public ArraySet<String> disabledComponents;
public ArraySet<String> enabledComponents;
- private String[] overlayPaths;
- private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths
- private String[] cachedOverlayPaths;
+ private OverlayPaths overlayPaths;
+ // Maps library name to overlay paths.
+ private ArrayMap<String, OverlayPaths> sharedLibraryOverlayPaths;
+ private OverlayPaths cachedOverlayPaths;
@Nullable
private ArrayMap<ComponentName, Pair<String, Integer>> componentLabelIconOverrideMap;
@@ -121,8 +122,7 @@ public class PackageUserState {
uninstallReason = o.uninstallReason;
disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
- overlayPaths =
- o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length);
+ overlayPaths = o.overlayPaths;
if (o.sharedLibraryOverlayPaths != null) {
sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths);
}
@@ -132,25 +132,55 @@ public class PackageUserState {
}
}
- public String[] getOverlayPaths() {
+ @Nullable
+ public OverlayPaths getOverlayPaths() {
return overlayPaths;
}
- public void setOverlayPaths(String[] paths) {
- overlayPaths = paths;
- cachedOverlayPaths = null;
+ @Nullable
+ public Map<String, OverlayPaths> getSharedLibraryOverlayPaths() {
+ return sharedLibraryOverlayPaths;
}
- public Map<String, String[]> getSharedLibraryOverlayPaths() {
- return sharedLibraryOverlayPaths;
+ /**
+ * Sets the path of overlays currently enabled for this package and user combination.
+ * @return true if the path contents differ than what they were previously
+ */
+ @Nullable
+ public boolean setOverlayPaths(@Nullable OverlayPaths paths) {
+ if (Objects.equals(paths, overlayPaths)) {
+ return false;
+ }
+ if ((overlayPaths == null && paths.isEmpty())
+ || (paths == null && overlayPaths.isEmpty())) {
+ return false;
+ }
+ overlayPaths = paths;
+ cachedOverlayPaths = null;
+ return true;
}
- public void setSharedLibraryOverlayPaths(String library, String[] paths) {
+ /**
+ * Sets the path of overlays currently enabled for a library that this package uses.
+ *
+ * @return true if the path contents for the library differ than what they were previously
+ */
+ public boolean setSharedLibraryOverlayPaths(@NonNull String library,
+ @Nullable OverlayPaths paths) {
if (sharedLibraryOverlayPaths == null) {
sharedLibraryOverlayPaths = new ArrayMap<>();
}
- sharedLibraryOverlayPaths.put(library, paths);
+ final OverlayPaths currentPaths = sharedLibraryOverlayPaths.get(library);
+ if (Objects.equals(paths, currentPaths)) {
+ return false;
+ }
cachedOverlayPaths = null;
+ if (paths == null || paths.isEmpty()) {
+ return sharedLibraryOverlayPaths.remove(library) != null;
+ } else {
+ sharedLibraryOverlayPaths.put(library, paths);
+ return true;
+ }
}
/**
@@ -332,35 +362,21 @@ public class PackageUserState {
return isComponentEnabled;
}
- public String[] getAllOverlayPaths() {
+ public OverlayPaths getAllOverlayPaths() {
if (overlayPaths == null && sharedLibraryOverlayPaths == null) {
return null;
}
-
if (cachedOverlayPaths != null) {
return cachedOverlayPaths;
}
-
- final LinkedHashSet<String> paths = new LinkedHashSet<>();
- if (overlayPaths != null) {
- final int N = overlayPaths.length;
- for (int i = 0; i < N; i++) {
- paths.add(overlayPaths[i]);
- }
- }
-
+ final OverlayPaths.Builder newPaths = new OverlayPaths.Builder();
+ newPaths.addAll(overlayPaths);
if (sharedLibraryOverlayPaths != null) {
- for (String[] libOverlayPaths : sharedLibraryOverlayPaths.values()) {
- if (libOverlayPaths != null) {
- final int N = libOverlayPaths.length;
- for (int i = 0; i < N; i++) {
- paths.add(libOverlayPaths[i]);
- }
- }
+ for (final OverlayPaths libOverlayPaths : sharedLibraryOverlayPaths.values()) {
+ newPaths.addAll(libOverlayPaths);
}
}
-
- cachedOverlayPaths = paths.toArray(new String[0]);
+ cachedOverlayPaths = newPaths.build();
return cachedOverlayPaths;
}
diff --git a/core/java/android/content/pm/overlay/OverlayPaths.java b/core/java/android/content/pm/overlay/OverlayPaths.java
new file mode 100644
index 000000000000..a4db733af013
--- /dev/null
+++ b/core/java/android/content/pm/overlay/OverlayPaths.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.overlay;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/** @hide */
+@DataClass(genConstructor = false, genBuilder = false, genHiddenBuilder = false,
+ genEqualsHashCode = true, genToString = true)
+public class OverlayPaths {
+ /**
+ * Represents {@link android.content.pm.ApplicationInfo#resourceDirs}.
+ * Only contains paths to APKs of overlays that can have their idmap resolved from their base
+ * APK path. Currently all overlay APKs can have their idmap path resolved from their idmap
+ * path.
+ */
+ @NonNull
+ private final List<String> mResourceDirs = new ArrayList<>();
+
+ /**
+ * Represents {@link android.content.pm.ApplicationInfo#overlayPaths}.
+ * Contains the contents of {@link #getResourceDirs()} and along with paths for overlays
+ * that are not APKs.
+ */
+ @NonNull
+ private final List<String> mOverlayPaths = new ArrayList<>();
+
+ public static class Builder {
+ final OverlayPaths mPaths = new OverlayPaths();
+
+ /**
+ * Adds a non-APK path to the contents of {@link OverlayPaths#getOverlayPaths()}.
+ */
+ public Builder addNonApkPath(@NonNull String idmapPath) {
+ mPaths.mOverlayPaths.add(idmapPath);
+ return this;
+ }
+
+ /**
+ * Adds a overlay APK path to the contents of {@link OverlayPaths#getResourceDirs()} and
+ * {@link OverlayPaths#getOverlayPaths()}.
+ */
+ public Builder addApkPath(@NonNull String overlayPath) {
+ addUniquePath(mPaths.mResourceDirs, overlayPath);
+ addUniquePath(mPaths.mOverlayPaths, overlayPath);
+ return this;
+ }
+
+ public Builder addAll(@Nullable OverlayPaths other) {
+ if (other != null) {
+ for (final String path : other.getResourceDirs()) {
+ addUniquePath(mPaths.mResourceDirs, path);
+ }
+ for (final String path : other.getOverlayPaths()) {
+ addUniquePath(mPaths.mOverlayPaths, path);
+ }
+ }
+ return this;
+ }
+
+ public OverlayPaths build() {
+ return mPaths;
+ }
+
+ private static void addUniquePath(@NonNull List<String> paths, @NonNull String path) {
+ if (!paths.contains(path)) {
+ paths.add(path);
+ }
+ }
+ }
+
+ /**
+ * Returns whether {@link #getOverlayPaths()} and {@link #getOverlayPaths} are empty.
+ */
+ public boolean isEmpty() {
+ return mResourceDirs.isEmpty() && mOverlayPaths.isEmpty();
+ }
+
+ private OverlayPaths() {
+ }
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/overlay/OverlayPaths.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Represents {@link android.content.pm.ApplicationInfo#resourceDirs}.
+ * Only contains paths to APKs of overlays that can have their idmap resolved from their base
+ * APK path. Currently all overlay APKs can have their idmap path resolved from their idmap
+ * path.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<String> getResourceDirs() {
+ return mResourceDirs;
+ }
+
+ /**
+ * Represents {@link android.content.pm.ApplicationInfo#overlayPaths}.
+ * Contains the contents of {@link #getResourceDirs()} and along with paths for overlays
+ * that are not APKs.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<String> getOverlayPaths() {
+ return mOverlayPaths;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "OverlayPaths { " +
+ "resourceDirs = " + mResourceDirs + ", " +
+ "overlayPaths = " + mOverlayPaths +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(OverlayPaths other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ OverlayPaths that = (OverlayPaths) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && Objects.equals(mResourceDirs, that.mResourceDirs)
+ && Objects.equals(mOverlayPaths, that.mOverlayPaths);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Objects.hashCode(mResourceDirs);
+ _hash = 31 * _hash + Objects.hashCode(mOverlayPaths);
+ return _hash;
+ }
+
+ @DataClass.Generated(
+ time = 1612307813586L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/content/pm/overlay/OverlayPaths.java",
+ inputSignatures = "private final @android.annotation.NonNull java.util.List<java.lang.String> mResourceDirs\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mOverlayPaths\npublic boolean isEmpty()\nclass OverlayPaths extends java.lang.Object implements []\nfinal android.content.pm.overlay.OverlayPaths mPaths\npublic android.content.pm.overlay.OverlayPaths.Builder addNonApkPath(java.lang.String)\npublic android.content.pm.overlay.OverlayPaths.Builder addApkPath(java.lang.String)\npublic android.content.pm.overlay.OverlayPaths.Builder addAll(android.content.pm.overlay.OverlayPaths)\npublic android.content.pm.overlay.OverlayPaths build()\nprivate static void addUniquePath(java.util.List<java.lang.String>,java.lang.String)\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genHiddenBuilder=false, genEqualsHashCode=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index b7365b3eaf61..fb0d90490567 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -41,6 +41,7 @@ import android.content.pm.SELinuxUtil;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ComponentParseUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
@@ -412,7 +413,11 @@ public class PackageInfoWithoutStateUtils {
ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
}
ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
- ai.resourceDirs = state.getAllOverlayPaths();
+ final OverlayPaths overlayPaths = state.getAllOverlayPaths();
+ if (overlayPaths != null) {
+ ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
+ ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
+ }
return ai;
}
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 05769ddc5397..99b56a82173e 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -38,7 +38,7 @@ public final class ResourcesKey {
public final String[] mSplitResDirs;
@Nullable
- public final String[] mOverlayDirs;
+ public final String[] mOverlayPaths;
@Nullable
public final String[] mLibDirs;
@@ -67,7 +67,7 @@ public final class ResourcesKey {
public ResourcesKey(@Nullable String resDir,
@Nullable String[] splitResDirs,
- @Nullable String[] overlayDirs,
+ @Nullable String[] overlayPaths,
@Nullable String[] libDirs,
int overrideDisplayId,
@Nullable Configuration overrideConfig,
@@ -75,7 +75,7 @@ public final class ResourcesKey {
@Nullable ResourcesLoader[] loader) {
mResDir = resDir;
mSplitResDirs = splitResDirs;
- mOverlayDirs = overlayDirs;
+ mOverlayPaths = overlayPaths;
mLibDirs = libDirs;
mLoaders = (loader != null && loader.length == 0) ? null : loader;
mDisplayId = overrideDisplayId;
@@ -86,7 +86,7 @@ public final class ResourcesKey {
int hash = 17;
hash = 31 * hash + Objects.hashCode(mResDir);
hash = 31 * hash + Arrays.hashCode(mSplitResDirs);
- hash = 31 * hash + Arrays.hashCode(mOverlayDirs);
+ hash = 31 * hash + Arrays.hashCode(mOverlayPaths);
hash = 31 * hash + Arrays.hashCode(mLibDirs);
hash = 31 * hash + Objects.hashCode(mDisplayId);
hash = 31 * hash + Objects.hashCode(mOverrideConfiguration);
@@ -98,12 +98,12 @@ public final class ResourcesKey {
@UnsupportedAppUsage
public ResourcesKey(@Nullable String resDir,
@Nullable String[] splitResDirs,
- @Nullable String[] overlayDirs,
+ @Nullable String[] overlayPaths,
@Nullable String[] libDirs,
int displayId,
@Nullable Configuration overrideConfig,
@Nullable CompatibilityInfo compatInfo) {
- this(resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfig, compatInfo,
+ this(resDir, splitResDirs, overlayPaths, libDirs, displayId, overrideConfig, compatInfo,
null);
}
@@ -115,7 +115,7 @@ public final class ResourcesKey {
if (mResDir != null && mResDir.startsWith(path)) {
return true;
} else {
- return anyStartsWith(mSplitResDirs, path) || anyStartsWith(mOverlayDirs, path)
+ return anyStartsWith(mSplitResDirs, path) || anyStartsWith(mOverlayPaths, path)
|| anyStartsWith(mLibDirs, path);
}
}
@@ -154,7 +154,7 @@ public final class ResourcesKey {
if (!Arrays.equals(mSplitResDirs, peer.mSplitResDirs)) {
return false;
}
- if (!Arrays.equals(mOverlayDirs, peer.mOverlayDirs)) {
+ if (!Arrays.equals(mOverlayPaths, peer.mOverlayPaths)) {
return false;
}
if (!Arrays.equals(mLibDirs, peer.mLibDirs)) {
@@ -186,8 +186,8 @@ public final class ResourcesKey {
}
builder.append("]");
builder.append(" mOverlayDirs=[");
- if (mOverlayDirs != null) {
- builder.append(TextUtils.join(",", mOverlayDirs));
+ if (mOverlayPaths != null) {
+ builder.append(TextUtils.join(",", mOverlayPaths));
}
builder.append("]");
builder.append(" mLibDirs=[");
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 39e3e146f45b..8068c872c4bb 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -1442,7 +1442,7 @@ public class Process {
*
* @hide
*/
- public static boolean hasFileLocks(int pid) throws IOException {
+ public static boolean hasFileLocks(int pid) throws Exception {
BufferedReader br = null;
try {
@@ -1454,8 +1454,13 @@ public class Process {
for (int i = 0; i < 5 && st.hasMoreTokens(); i++) {
String str = st.nextToken();
- if (i == 4 && Integer.parseInt(str) == pid) {
- return true;
+ try {
+ if (i == 4 && Integer.parseInt(str) == pid) {
+ return true;
+ }
+ } catch (NumberFormatException nfe) {
+ throw new Exception("Exception parsing /proc/locks at \" "
+ + line + " \", token #" + i);
}
}
}
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index faa90d93719f..86b20c424869 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -350,6 +350,9 @@ public class FontsContract {
return cachedTypeface;
}
+ Log.w(TAG, "Platform version of downloadable fonts is deprecated. Please use"
+ + " androidx version instead.");
+
synchronized (sLock) {
// It is possible that Font is loaded during the thread sleep time
// re-check the cache to avoid re-loading the font
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 990b7bdfa987..dad932c2ee19 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -123,14 +123,6 @@ interface IWindowSession {
boolean outOfMemory(IWindow window);
/**
- * Give the window manager a hint of the part of the window that is
- * completely transparent, allowing it to work with the surface flinger
- * to optimize compositing of this part of the window.
- */
- @UnsupportedAppUsage
- oneway void setTransparentRegion(IWindow window, in Region region);
-
- /**
* Tell the window manager about the content and visible insets of the
* given window, which can be used to adjust the <var>outContentInsets</var>
* and <var>outVisibleInsets</var> values returned by
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e1ccc51c71e1..1273b491d0e1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -740,6 +740,7 @@ import java.util.function.Predicate;
* @attr ref android.R.styleable#View_alpha
* @attr ref android.R.styleable#View_background
* @attr ref android.R.styleable#View_clickable
+ * @attr ref android.R.styleable#View_clipToOutline
* @attr ref android.R.styleable#View_contentDescription
* @attr ref android.R.styleable#View_drawingCacheQuality
* @attr ref android.R.styleable#View_duplicateParentState
@@ -5968,6 +5969,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
case R.styleable.View_scrollCaptureHint:
setScrollCaptureHint((a.getInt(attr, SCROLL_CAPTURE_HINT_AUTO)));
break;
+ case R.styleable.View_clipToOutline:
+ setClipToOutline(a.getBoolean(attr, false));
+ break;
}
}
@@ -17921,6 +17925,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @see #setOutlineProvider(ViewOutlineProvider)
* @see #getClipToOutline()
+ *
+ * @attr ref android.R.styleable#View_clipToOutline
*/
@RemotableViewMethod
public void setClipToOutline(boolean clipToOutline) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 56d98e78303e..4716141ee8d3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1675,7 +1675,8 @@ public final class ViewRootImpl implements ViewParent,
requestLayout();
// See comment for View.sForceLayoutWhenInsetsChanged
- if (View.sForceLayoutWhenInsetsChanged && mView != null) {
+ if (View.sForceLayoutWhenInsetsChanged && mView != null
+ && mWindowAttributes.softInputMode == SOFT_INPUT_ADJUST_RESIZE) {
forceLayout(mView);
}
@@ -3063,11 +3064,14 @@ public final class ViewRootImpl implements ViewParent,
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
- // reconfigure window manager
- try {
- mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
- } catch (RemoteException e) {
- }
+ // TODO: Ideally we would do this in prepareSurfaces,
+ // but prepareSurfaces is currently working under
+ // the assumption that we paused the render thread
+ // via the WM relayout code path. We probably eventually
+ // want to synchronize transparent region hint changes
+ // with draws.
+ mTransaction.setTransparentRegionHint(getSurfaceControl(),
+ mTransparentRegion).apply();
}
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 3aedda1a6bd3..39d3c01dd409 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -314,10 +314,6 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
- public void setTransparentRegion(android.view.IWindow window, android.graphics.Region region) {
- }
-
- @Override
public void setInsets(android.view.IWindow window, int touchableInsets,
android.graphics.Rect contentInsets, android.graphics.Rect visibleInsets,
android.graphics.Region touchableRegion) {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 794181e388cf..decbf8c0c59e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -200,6 +200,34 @@ public final class AutofillManager {
"android.view.autofill.extra.AUTHENTICATION_RESULT";
/**
+ * Intent extra: The optional boolean extra field provided by the
+ * {@link android.service.autofill.AutofillService} accompanying the {@link
+ * android.service.autofill.Dataset} result of an authentication operation.
+ *
+ * <p> Before {@link android.os.Build.VERSION_CODES#R}, if the authentication result is a
+ * {@link android.service.autofill.Dataset}, it'll be used to autofill the fields, and also
+ * replace the existing dataset in the cached {@link android.service.autofill.FillResponse}.
+ * That means if the user clears the field values, the autofill suggestion will show up again
+ * with the new authenticated Dataset.
+ *
+ * <p> In {@link android.os.Build.VERSION_CODES#R}, we added an exception to this behavior
+ * that if the Dataset being authenticated is a pinned dataset (see
+ * {@link android.service.autofill.InlinePresentation#isPinned()}), the old Dataset will not be
+ * replaced.
+ *
+ * <p> In {@link android.os.Build.VERSION_CODES#S}, we added this boolean extra field to
+ * allow the {@link android.service.autofill.AutofillService} to explicitly specify whether
+ * the returned authenticated Dataset is ephemeral. An ephemeral Dataset will be used to
+ * autofill once and then thrown away. Therefore, when the boolean extra is set to true, the
+ * returned Dataset will not replace the old dataset from the existing
+ * {@link android.service.autofill.FillResponse}. When it's set to false, it will. When it's not
+ * set, the old dataset will be replaced, unless it is a pinned inline suggestion, which is
+ * consistent with the behavior in {@link android.os.Build.VERSION_CODES#R}.
+ */
+ public static final String EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET =
+ "android.view.autofill.extra.AUTHENTICATION_RESULT_EPHEMERAL_DATASET";
+
+ /**
* Intent extra: The optional extras provided by the
* {@link android.service.autofill.AutofillService}.
*
@@ -1755,6 +1783,11 @@ public final class AutofillManager {
if (newClientState != null) {
responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
}
+ if (data.getExtras().containsKey(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
+ responseData.putBoolean(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET,
+ data.getBooleanExtra(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET,
+ false));
+ }
try {
mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
mContext.getUserId());
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index bb39ea810add..5c6116a09b77 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -114,4 +114,5 @@ message ApplicationInfoProto {
optional bool native_heap_zero_init = 21;
}
optional Detail detail = 17;
+ repeated string overlay_paths = 18;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 827bf7b70cbc..856657ad8b56 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2698,11 +2698,11 @@
The app can check whether it has this authorization by calling
{@link android.provider.Settings#canDrawOverlays
Settings.canDrawOverlays()}.
- <p>Protection level: signature|appop|installer|recents|appPredictor|pre23|development -->
+ <p>Protection level: signature|appop|installer|appPredictor|pre23|development -->
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
android:label="@string/permlab_systemAlertWindow"
android:description="@string/permdesc_systemAlertWindow"
- android:protectionLevel="signature|appop|installer|recents|appPredictor|pre23|development" />
+ android:protectionLevel="signature|appop|installer|appPredictor|pre23|development" />
<!-- @SystemApi @hide Allows an application to create windows using the type
{@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY},
@@ -5447,6 +5447,11 @@
<permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING"
android:protectionLevel="signature" />
+ <!-- Allows managing the Game Mode
+ @hide Used internally. -->
+ <permission android:name="android.permission.MANAGE_GAME_MODE"
+ android:protectionLevel="signature" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e567c3d7d486..586c99d76335 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3251,6 +3251,16 @@
a value of 'true' will not override any 'false' value in its parent chain nor will
it prevent any 'false' in any of its children. -->
<attr name="forceDarkAllowed" format="boolean" />
+
+ <!-- <p>Whether the View's Outline should be used to clip the contents of the View.
+ <p>Only a single non-rectangular clip can be applied on a View at any time. Circular
+ clips from a
+ {@link android.view.ViewAnimationUtils#createCircularReveal(View, int, int, float,
+ float)} circular reveal animation take priority over Outline clipping, and child
+ Outline clipping takes priority over Outline clipping done by a parent.
+ <p>Note that this flag will only be respected if the View's Outline returns true from
+ {@link android.graphics.Outline#canClip()}. -->
+ <attr name="clipToOutline" format="boolean" />
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index c8cccc45f634..f7234269f227 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -48,6 +48,8 @@
<color name="text_color_secondary_device_default_dark">@color/system_main_200</color>
<color name="text_color_tertiary_device_default_light">@color/system_main_500</color>
<color name="text_color_tertiary_device_default_dark">@color/system_main_400</color>
+ <color name="foreground_device_default_light">@color/text_color_primary_device_default_light</color>
+ <color name="foreground_device_default_dark">@color/text_color_primary_device_default_dark</color>
<!-- Error color -->
<color name="error_color_device_default_dark">@color/error_color_material_dark</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8c5f454d204d..beae9353a10f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4712,4 +4712,10 @@
<!-- Whether to select voice/data/sms preference without user confirmation -->
<bool name="config_voice_data_sms_auto_fallback">false</bool>
+
+ <!-- Whether to enable the one-handed keyguard on the lock screen for wide-screen devices. -->
+ <bool name="config_enableOneHandedKeyguard">false</bool>
+
+ <!-- Whether to allow the caching of the SIM PIN for verification after unattended reboot -->
+ <bool name="config_allow_pin_storage_for_unattended_reboot">true</bool>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 97ec0f4fa71e..d3f3ebd7c3d9 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3062,6 +3062,7 @@
<!-- @hide @SystemApi -->
<public name="hotwordDetectionService" />
<public name="previewLayout" />
+ <public name="clipToOutline" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c41b78e4a680..41375a7dd419 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2647,6 +2647,7 @@
<java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" />
<java-symbol type="bool" name="config_overrideRemoteViewsActivityTransition" />
<java-symbol type="attr" name="colorProgressBackgroundNormal" />
+ <java-symbol type="bool" name="config_allow_pin_storage_for_unattended_reboot" />
<java-symbol type="layout" name="simple_account_item" />
<java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index ee17f6f7ce21..ce4ee87e52a0 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -222,6 +222,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
</style>
<style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -236,6 +238,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -267,6 +271,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -300,6 +306,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -332,6 +340,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -381,6 +391,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -405,6 +417,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -435,6 +449,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -466,6 +482,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -513,6 +531,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -545,6 +565,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -575,6 +597,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -607,6 +631,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -638,6 +664,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -669,6 +697,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -700,6 +730,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -731,6 +763,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -766,6 +800,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Text styles -->
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
@@ -798,6 +834,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -827,6 +865,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1012,6 +1052,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<item name="colorPopupBackground">?attr/colorBackgroundFloating</item>
<item name="panelColorBackground">?attr/colorBackgroundFloating</item>
</style>
@@ -1027,6 +1069,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1057,6 +1101,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1088,6 +1134,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1121,6 +1169,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1153,6 +1203,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1204,6 +1256,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Progress bar attributes -->
<item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
@@ -1227,6 +1281,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1260,6 +1316,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1294,6 +1352,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1329,6 +1389,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -1346,6 +1408,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
</style>
<!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -1362,6 +1426,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1397,6 +1463,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1430,6 +1498,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1462,6 +1532,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1493,6 +1565,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1524,6 +1598,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1553,6 +1629,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1672,6 +1750,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1702,6 +1782,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
@@ -1742,6 +1824,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1775,6 +1859,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
index cfcfcc8cf044..ece37f8054cf 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
@@ -201,6 +201,18 @@ public class BundleUtilTest {
assertThat(BundleUtil.deepHashCode(b1)).isNotEqualTo(BundleUtil.deepHashCode(b2));
}
+ @Test
+ public void testDeepHashCode_differentKeys() {
+ Bundle[] inputs = new Bundle[2];
+ for (int i = 0; i < 2; i++) {
+ Bundle b = new Bundle();
+ b.putString("key" + i, "value");
+ inputs[i] = b;
+ }
+ assertThat(BundleUtil.deepHashCode(inputs[0]))
+ .isNotEqualTo(BundleUtil.deepHashCode(inputs[1]));
+ }
+
private static Bundle createThoroughBundle() {
Bundle toy1 = new Bundle();
toy1.putString("a", "a");
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 45adf833de97..46dbe0fd658a 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -90,12 +90,12 @@ public class ResourcesManagerTest extends TestCase {
@SmallTest
public void testMultipleCallsWithIdenticalParametersCacheReference() {
Resources resources = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, null, null,
+ null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources);
Resources newResources = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, null, null,
+ null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(newResources);
assertSame(resources, newResources);
@@ -104,14 +104,14 @@ public class ResourcesManagerTest extends TestCase {
@SmallTest
public void testMultipleCallsWithDifferentParametersReturnDifferentReferences() {
Resources resources = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, null, null,
+ null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources);
Configuration overrideConfig = new Configuration();
overrideConfig.smallestScreenWidthDp = 200;
Resources newResources = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, null, overrideConfig,
+ null, APP_ONE_RES_DIR, null, null, null, null, null, overrideConfig,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(newResources);
assertNotSame(resources, newResources);
@@ -120,12 +120,12 @@ public class ResourcesManagerTest extends TestCase {
@SmallTest
public void testAddingASplitCreatesANewImpl() {
Resources resources1 = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, null, null,
+ null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
Resources resources2 = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, new String[] { APP_ONE_RES_SPLIT_DIR }, null, null,
+ null, APP_ONE_RES_DIR, new String[] { APP_ONE_RES_SPLIT_DIR }, null, null, null,
null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null,
null);
assertNotNull(resources2);
@@ -137,12 +137,12 @@ public class ResourcesManagerTest extends TestCase {
@SmallTest
public void testUpdateConfigurationUpdatesAllAssetManagers() {
Resources resources1 = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, null, null,
+ null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
Resources resources2 = mResourcesManager.getResources(
- null, APP_TWO_RES_DIR, null, null, null, null, null,
+ null, APP_TWO_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources2);
@@ -150,7 +150,7 @@ public class ResourcesManagerTest extends TestCase {
final Configuration overrideConfig = new Configuration();
overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
Resources resources3 = mResourcesManager.getResources(
- activity, APP_ONE_RES_DIR, null, null, null, null,
+ activity, APP_ONE_RES_DIR, null, null, null, null, null,
overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources3);
@@ -183,13 +183,13 @@ public class ResourcesManagerTest extends TestCase {
public void testTwoActivitiesWithIdenticalParametersShareImpl() {
Binder activity1 = new Binder();
Resources resources1 = mResourcesManager.getResources(
- activity1, APP_ONE_RES_DIR, null, null, null, null, null,
+ activity1, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
Binder activity2 = new Binder();
Resources resources2 = mResourcesManager.getResources(
- activity2, APP_ONE_RES_DIR, null, null, null, null, null,
+ activity2, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
@@ -204,7 +204,7 @@ public class ResourcesManagerTest extends TestCase {
public void testThemesGetUpdatedWithNewImpl() {
Binder activity1 = new Binder();
Resources resources1 = mResourcesManager.createBaseTokenResources(
- activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ activity1, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
@@ -237,15 +237,15 @@ public class ResourcesManagerTest extends TestCase {
Configuration config1 = new Configuration();
config1.densityDpi = 280;
Resources resources1 = mResourcesManager.createBaseTokenResources(
- activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config1,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
+ activity1, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY,
+ config1, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
// Create a Resources based on the Activity.
Configuration config2 = new Configuration();
config2.screenLayout |= Configuration.SCREENLAYOUT_ROUND_YES;
Resources resources2 = mResourcesManager.getResources(
- activity1, APP_ONE_RES_DIR, null, null, null, null, config2,
+ activity1, APP_ONE_RES_DIR, null, null, null, null, null, config2,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources2);
@@ -286,8 +286,8 @@ public class ResourcesManagerTest extends TestCase {
final Configuration overrideConfig = new Configuration();
overrideConfig.densityDpi = originalOverrideDensity;
final Resources resources = mResourcesManager.createBaseTokenResources(
- token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* overlayDirs */,
- null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig,
+ token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* legacyOverlayDirs */,
+ null /* overlayDirs */,null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* classLoader */,
null /* loaders */);
@@ -315,12 +315,12 @@ public class ResourcesManagerTest extends TestCase {
// Create a base token resources that are based on the default display.
Resources activityResources = mResourcesManager.createBaseTokenResources(
- activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ activity, APP_ONE_RES_DIR, null, null, null,null, Display.DEFAULT_DISPLAY, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
// Create another resources that explicitly override the display of the base token above
// and set it to DEFAULT_DISPLAY.
Resources defaultDisplayResources = mResourcesManager.getResources(
- activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ activity, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index 64906bb27ff0..e16d44854516 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -100,7 +100,7 @@ public class ResolverListControllerTest {
final List<UsageStats> slices = new ArrayList<>();
slices.add(packageStats);
ParceledListSlice<UsageStats> stats = new ParceledListSlice<>(slices);
- when(mMockService.queryUsageStats(anyInt(), anyLong(), anyLong(), anyString()))
+ when(mMockService.queryUsageStats(anyInt(), anyLong(), anyLong(), anyString(), anyInt()))
.thenReturn(stats);
Answer<Void> answer = new Answer<Void>() {
@Override
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index cb4dd9e8cacd..b70fa0e693c2 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -46,7 +46,6 @@ import java.io.File;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.stream.Stream;
@@ -1148,24 +1147,14 @@ public class HardwareRenderer {
// Default to SRGB if the display doesn't support wide color
.orElse(Dataspace.SRGB);
- float maxRefreshRate =
- (float) Arrays.stream(display.getSupportedModes())
- .mapToDouble(Mode::getRefreshRate)
- .max()
- .orElseGet(() -> {
- Log.i(LOG_TAG, "Failed to find the maximum display refresh rate");
- // Assume that the max refresh rate is 60hz if we can't find one.
- return 60.0;
- });
// Grab the physical screen dimensions from the active display mode
// Strictly speaking the screen resolution may not always be constant - it is for
// sizing the font cache for the underlying rendering thread. Since it's a
// heuristic we don't need to be always 100% correct.
Mode activeMode = display.getMode();
nInitDisplayInfo(activeMode.getPhysicalWidth(), activeMode.getPhysicalHeight(),
- display.getRefreshRate(), maxRefreshRate,
- wideColorDataspace.mNativeDataspace, display.getAppVsyncOffsetNanos(),
- display.getPresentationDeadlineNanos());
+ display.getRefreshRate(), wideColorDataspace.mNativeDataspace,
+ display.getAppVsyncOffsetNanos(), display.getPresentationDeadlineNanos());
// Defensively clear out the context
mContext = null;
@@ -1324,6 +1313,5 @@ public class HardwareRenderer {
private static native void nSetDisplayDensityDpi(int densityDpi);
private static native void nInitDisplayInfo(int width, int height, float refreshRate,
- float maxRefreshRate, int wideColorDataspace, long appVsyncOffsetNanos,
- long presentationDeadlineNanos);
+ int wideColorDataspace, long appVsyncOffsetNanos, long presentationDeadlineNanos);
}
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 372add9b7ecb..d188b6525579 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -190,7 +190,7 @@ public class KeyStoreSecurityLevel {
keyDescriptor.blob = wrappedKey;
keyDescriptor.domain = wrappedKeyDescriptor.domain;
- return handleExceptions(() -> mSecurityLevel.importWrappedKey(wrappedKeyDescriptor,
+ return handleExceptions(() -> mSecurityLevel.importWrappedKey(keyDescriptor,
wrappingKeyDescriptor, maskingKey,
args.toArray(new KeyParameter[args.size()]), authenticatorSpecs));
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
index 8475ad9fd57b..0f777495a3fe 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -164,6 +164,9 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
List<KeyParameter> parameters = new ArrayList<>();
parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_SIGN
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC
));
parameters.add(KeyStore2ParameterUtils.makeEnum(
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
index 32650aeda1b1..5619585d9c3c 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
@@ -21,7 +21,6 @@ import android.security.KeyStoreSecurityLevel;
import android.system.keystore2.Authorization;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
-import android.util.Log;
import java.security.Key;
@@ -127,15 +126,6 @@ public class AndroidKeyStoreKey implements Key {
return false;
}
- // If the key ids are equal and the class matches all the other fields cannot differ
- // unless we have a bug.
- if (!mAlgorithm.equals(other.mAlgorithm)
- || !mAuthorizations.equals(other.mAuthorizations)
- || !mDescriptor.equals(other.mDescriptor)) {
- Log.e("AndroidKeyStoreKey", "Bug: key ids are identical, but key metadata"
- + "differs.");
- return false;
- }
return true;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 8c8acc418a0e..39607aeb3852 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -866,7 +866,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
try {
response = mKeyStore.getKeyEntry(wrappingkey);
} catch (android.security.KeyStoreException e) {
- throw new KeyStoreException("Failed to load wrapping key.", e);
+ throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
+ + e.getErrorCode(), e);
}
KeyDescriptor wrappedKey = makeKeyDescriptor(alias);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 177646b22ea3..7ca569349633 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -106,6 +106,8 @@ public interface SplitScreen extends DragAndDropPolicy.Starter {
/** Removes the split-screen stages. */
void exitSplitScreen();
+ /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */
+ void exitSplitScreenOnHide(boolean exitSplitScreenOnHide);
/** Gets the stage bounds. */
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index bbad36dcc046..b0167afa2e4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -126,6 +126,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter {
mStageCoordinator.exitSplitScreen();
}
+ public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
+ }
+
public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
}
@@ -292,6 +296,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter {
}
@Override
+ public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.exitSplitScreenOnHide(exitSplitScreenOnHide);
+ });
+ }
+
+ @Override
public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
try {
mMainExecutor.executeBlocking(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 176852b148fa..2d4b77e0c630 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -79,6 +79,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
private DisplayAreaInfo mDisplayAreaInfo;
private final Context mContext;
private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
+ private boolean mExitSplitScreenOnHide = true;
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer) {
@@ -113,7 +114,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitScreen.StagePosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
- mSideStagePosition = sideStagePosition;
+ setSideStagePosition(sideStagePosition);
mMainStage.activate(getMainStageBounds(), wct);
mSideStage.addTask(task, getSideStageBounds(), wct);
mTaskOrganizer.applyTransaction(wct);
@@ -144,10 +145,14 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
}
void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
+ if (mSideStagePosition == sideStagePosition) return;
+
mSideStagePosition = sideStagePosition;
if (mSideStageListener.mVisible) {
onStageVisibilityChanged(mSideStageListener);
}
+
+ sendOnStagePositionChanged();
}
void setSideStageVisibility(boolean visible) {
@@ -162,6 +167,10 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
exitSplitScreen(null /* childrenToTop */);
}
+ void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ mExitSplitScreenOnHide = exitSplitScreenOnHide;
+ }
+
private void exitSplitScreen(StageTaskListener childrenToTop) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
@@ -202,6 +211,14 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
mListeners.remove(listener);
}
+ private void sendOnStagePositionChanged() {
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ final SplitScreen.SplitScreenListener l = mListeners.get(i);
+ l.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
+ l.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
+ }
+ }
+
private void onStageChildTaskStatusChanged(
StageListenerImpl stageListener, int taskId, boolean present) {
@@ -251,7 +268,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
}
}
- if (!mainStageVisible && !sideStageVisible) {
+ if (mExitSplitScreenOnHide && !mainStageVisible && !sideStageVisible) {
// Exit split-screen if both stage are not visible.
// TODO: This is only a temporary request from UX and is likely to be removed soon...
exitSplitScreen();
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt
new file mode 100644
index 000000000000..1869d833fbc4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("Utils")
+package com.android.wm.shell.flicker
+
+import android.app.ActivityTaskManager
+import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT
+import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+
+fun removeAllTasksButHome() {
+ val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf(
+ ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS,
+ ACTIVITY_TYPE_UNDEFINED)
+ val atm = ActivityTaskManager.getService()
+ atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME)
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index 7ec22bb9db1c..cac46fe676b3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -32,13 +32,12 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
/**
* Opens the IME and wait for it to be displayed
*
- * @param device UIDevice instance to interact with the device
* @param wmHelper Helper used to wait for WindowManager states
*/
@JvmOverloads
- open fun openIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) {
+ open fun openIME(wmHelper: WindowManagerStateHelper? = null) {
if (!isTelevision) {
- val editText = device.wait(
+ val editText = uiDevice.wait(
Until.findObject(By.res(getPackage(), "plain_text_input")),
FIND_TIMEOUT)
@@ -47,7 +46,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
"was left in an unknown state (e.g. in split screen)"
}
editText.click()
- waitAndAssertIMEShown(device, wmHelper)
+ waitAndAssertIMEShown(uiDevice, wmHelper)
} else {
// If we do the same thing as above - editText.click() - on TV, that's going to force TV
// into the touch mode. We really don't want that.
@@ -69,16 +68,15 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
/**
* Opens the IME and wait for it to be gone
*
- * @param device UIDevice instance to interact with the device
* @param wmHelper Helper used to wait for WindowManager states
*/
@JvmOverloads
- open fun closeIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) {
+ open fun closeIME(wmHelper: WindowManagerStateHelper? = null) {
if (!isTelevision) {
- device.pressBack()
+ uiDevice.pressBack()
// Using only the AccessibilityInfo it is not possible to identify if the IME is active
if (wmHelper == null) {
- device.waitForIdle()
+ uiDevice.waitForIdle()
} else {
require(wmHelper.waitImeWindowGone()) { "IME did did not close" }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index b90e865de691..111362a93495 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -17,17 +17,20 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
+import android.graphics.Point
import android.media.session.MediaController
import android.media.session.MediaSessionManager
import android.os.SystemClock
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
+import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow
import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild
+import com.android.wm.shell.flicker.pip.waitPipWindowGone
+import com.android.wm.shell.flicker.pip.waitPipWindowShown
import com.android.wm.shell.flicker.testapp.Components
-import org.junit.Assert.fail
class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
@@ -55,6 +58,17 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
}
}
+ /** {@inheritDoc} */
+ override fun launchViaIntent(
+ wmHelper: WindowManagerStateHelper,
+ expectedWindowName: String,
+ action: String?,
+ stringExtras: Map<String, String>
+ ) {
+ super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
+ wmHelper.waitPipWindowShown()
+ }
+
private fun focusOnObject(selector: BySelector): Boolean {
// We expect all the focusable UI elements to be arranged in a way so that it is possible
// to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top"
@@ -69,16 +83,12 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
return false
}
- fun clickEnterPipButton() {
+ @JvmOverloads
+ fun clickEnterPipButton(wmHelper: WindowManagerStateHelper? = null) {
clickObject(ENTER_PIP_BUTTON_ID)
- // TODO(b/172321238): remove this check once hasPipWindow is fixed on TVs
- if (!isTelevision) {
- uiDevice.hasPipWindow()
- } else {
- // Simply wait for 3 seconds
- SystemClock.sleep(3_000)
- }
+ // Wait on WMHelper or simply wait for 3 seconds
+ wmHelper?.waitPipWindowShown() ?: SystemClock.sleep(3_000)
}
fun clickStartMediaSessionButton() {
@@ -97,16 +107,75 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
fun stopMedia() = mediaController?.transportControls?.stop()
?: error("No active media session found")
+ @Deprecated("Use PipAppHelper.closePipWindow(wmHelper) instead",
+ ReplaceWith("closePipWindow(wmHelper)"))
fun closePipWindow() {
if (isTelevision) {
uiDevice.closeTvPipWindow()
} else {
uiDevice.closePipWindow()
}
+ }
+
+ /**
+ * Expands the pip window and dismisses it by clicking on the X button.
+ *
+ * Note, currently the View coordinates reported by the accessibility are relative to
+ * the window, so the correct coordinates need to be calculated
+ *
+ * For example, in a PIP window located at Rect(508, 1444 - 1036, 1741), the
+ * dismiss button coordinates are shown as Rect(650, 0 - 782, 132), with center in
+ * Point(716, 66), instead of Point(970, 1403)
+ *
+ * See b/179337864
+ */
+ fun closePipWindow(wmHelper: WindowManagerStateHelper) {
+ if (isTelevision) {
+ uiDevice.closeTvPipWindow()
+ } else {
+ expandPipWindow(wmHelper)
+ val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss"))
+ requireNotNull(exitPipObject) { "PIP window dismiss button not found" }
+ val coordinatesInWindow = exitPipObject.visibleBounds
+ val windowOffset = wmHelper.getWindowRegion(component).bounds
+ val newCoordinates = Point(windowOffset.left + coordinatesInWindow.centerX(),
+ windowOffset.top + coordinatesInWindow.centerY())
+ uiDevice.click(newCoordinates.x, newCoordinates.y)
+ }
+
+ // Wait for animation to complete.
+ wmHelper.waitPipWindowGone()
+ wmHelper.waitForHomeActivityVisible()
+ }
+
+ /**
+ * Click once on the PIP window to expand it
+ */
+ fun expandPipWindow(wmHelper: WindowManagerStateHelper) {
+ val windowRegion = wmHelper.getWindowRegion(component)
+ require(!windowRegion.isEmpty) {
+ "Unable to find a PIP window in the current state"
+ }
+ val windowRect = windowRegion.bounds
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
+ // Ensure WindowManagerService wait until all animations have completed
+ wmHelper.waitForAppTransitionIdle()
+ mInstrumentation.uiAutomation.syncInputTransactions()
+ }
- if (!waitUntilClosed()) {
- fail("Couldn't close Pip")
+ /**
+ * Double click on the PIP window to reopen to app
+ */
+ fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) {
+ val windowRegion = wmHelper.getWindowRegion(component)
+ require(!windowRegion.isEmpty) {
+ "Unable to find a PIP window in the current state"
}
+ val windowRect = windowRegion.bounds
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
+ wmHelper.waitPipWindowGone()
+ wmHelper.waitForAppTransitionIdle()
}
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt
index 2015f4941cea..bc42d5ed04ce 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt
@@ -16,11 +16,6 @@
package com.android.wm.shell.flicker.pip
-import android.app.ActivityTaskManager
-import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT
-import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS
-import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
-import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
import android.os.SystemClock
import com.android.wm.shell.flicker.NonRotationTestBase
@@ -29,14 +24,6 @@ abstract class AppTestBase(
rotation: Int
) : NonRotationTestBase(rotationName, rotation) {
companion object {
- fun removeAllTasksButHome() {
- val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf(
- ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS,
- ACTIVITY_TYPE_UNDEFINED)
- val atm = ActivityTaskManager.getService()
- atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME)
- }
-
fun waitForAnimationComplete() {
// TODO: UiDevice doesn't have reliable way to wait for the completion of animation.
// Consider to introduce WindowManagerStateHelper to access Activity state.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 5a3d18d9feef..a14b46ef7a3d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -16,21 +16,21 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Presubmit
+import android.os.Bundle
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.runFlicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.wm.shell.flicker.helpers.PipAppHelper
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -39,82 +39,67 @@ import org.junit.runners.Parameterized
* Test Pip launch and exit.
* To run this test: `atest WMShellFlickerTests:EnterExitPipTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class EnterExitPipTest(
- rotationName: String,
- rotation: Int
-) : AppTestBase(rotationName, rotation) {
- private val pipApp = PipAppHelper(instrumentation)
- private val testApp = FixedAppHelper(instrumentation)
-
- @Test
- fun testDisplayMetricsPinUnpin() {
- runFlicker(instrumentation) {
- withTestName { "testDisplayMetricsPinUnpin" }
- setup {
- test {
- removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true"))
- testApp.launchViaIntent()
- waitForAnimationComplete()
- }
- }
- transitions {
- // This will bring PipApp to fullscreen
- pipApp.launchViaIntent()
- waitForAnimationComplete()
- }
- teardown {
- test {
- removeAllTasksButHome()
- }
- }
- assertions {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- windowManagerTrace {
- all("pipApp must remain inside visible bounds") {
- coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
- }
- all("Initially shows both app windows then pipApp hides testApp") {
- showsAppWindow(testApp.defaultWindowName)
- .showsAppWindowOnTop(pipApp.defaultWindowName)
- .then()
- .hidesAppWindow(testApp.defaultWindowName)
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<Array<Any>> {
+ val testApp = FixedAppHelper(instrumentation)
+ val baseConfig = getTransitionLaunch(eachRun = true)
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ setup {
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
}
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
}
- layersTrace {
- all("Initially shows both app layers then pipApp hides testApp") {
- showsLayer(testApp.defaultWindowName)
- .showsLayer(pipApp.defaultWindowName)
- .then()
- .hidesLayer(testApp.defaultWindowName)
- }
- start("testApp covers the fullscreen, pipApp remains inside display") {
- hasVisibleRegion(testApp.defaultWindowName, displayBounds)
- coversAtMostRegion(displayBounds, pipApp.defaultWindowName)
- }
- end("pipApp covers the fullscreen") {
- hasVisibleRegion(pipApp.defaultWindowName, displayBounds)
+ transitions {
+ // This will bring PipApp to fullscreen
+ pipApp.launchViaIntent(wmHelper)
+ }
+ assertions {
+ val displayBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
+ presubmit {
+ windowManagerTrace {
+ all("pipApp must remain inside visible bounds") {
+ coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
+ }
+ all("Initially shows both app windows then pipApp hides testApp") {
+ showsAppWindow(testApp.defaultWindowName)
+ .showsAppWindowOnTop(pipApp.defaultWindowName)
+ .then()
+ .hidesAppWindow(testApp.defaultWindowName)
+ }
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+ layersTrace {
+ all("Initially shows both app layers then pipApp hides testApp") {
+ showsLayer(testApp.defaultWindowName)
+ .showsLayer(pipApp.defaultWindowName)
+ .then()
+ .hidesLayer(testApp.defaultWindowName)
+ }
+ start("testApp covers the fullscreen, pipApp remains inside display") {
+ hasVisibleRegion(testApp.defaultWindowName, displayBounds)
+ coversAtMostRegion(displayBounds, pipApp.defaultWindowName)
+ }
+ end("pipApp covers the fullscreen") {
+ hasVisibleRegion(pipApp.defaultWindowName, displayBounds)
+ }
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ }
}
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
}
}
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig,
+ testSpec, supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
}
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index af62eb9ae40d..99a40daa027f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,18 +16,13 @@
package com.android.wm.shell.flicker.pip
+import android.os.Bundle
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.expandPipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
@@ -35,9 +30,7 @@ import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
-import com.android.wm.shell.flicker.helpers.PipAppHelper
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -50,80 +43,58 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 152738416)
class EnterPipTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
- companion object {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = PipAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
- withTestName { buildTestTag("enterPip", testApp, configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- device.pressHome()
- testApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.startRotation)
- }
- }
- teardown {
- eachRun {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- }
- }
- transitions {
- testApp.clickEnterPipButton()
- device.expandPipWindow()
- }
- assertions {
+ val baseConfig = getTransitionLaunch(
+ eachRun = true, stringExtras = emptyMap())
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ transitions {
+ pipApp.clickEnterPipButton()
+ pipApp.expandPipWindow(wmHelper)
+ }
+ assertions {
+ presubmit {
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
all("pipWindowBecomesVisible") {
- this.showsAppWindow(testApp.`package`)
- .then()
- .showsAppWindow(PIP_WINDOW_TITLE)
+ this.showsAppWindow(pipApp.defaultWindowName)
}
}
layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
- enabled = false)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0, bugId = 140855415)
statusBarLayerRotatesScales(configuration.startRotation,
Surface.ROTATION_0)
}
layersTrace {
all("pipLayerBecomesVisible") {
- this.showsLayer(testApp.launcherName)
- .then()
- .showsLayer(PIP_WINDOW_TITLE)
+ this.showsLayer(pipApp.launcherName)
}
}
}
+
+ flaky {
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0, bugId = 140855415)
+ }
+ }
}
+ }
+
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig,
+ testSpec, supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
}
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
new file mode 100644
index 000000000000..eaaa2f6390be
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.wm.shell.flicker.pip
+
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
+import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
+import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
+import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip with orientation changes.
+ * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class EnterPipToOtherOrientationTest(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+ private val testApp = FixedAppHelper(instrumentation)
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5) { configuration ->
+ setupAndTeardown(this, configuration)
+
+ setup {
+ eachRun {
+ // Launch a portrait only app on the fullscreen stack
+ testApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()))
+ // Launch the PiP activity fixed as landscape
+ pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
+ }
+ }
+ teardown {
+ eachRun {
+ pipApp.exit()
+ testApp.exit()
+ }
+ }
+ transitions {
+ // Enter PiP, and assert that the PiP is within bounds now that the device is back
+ // in portrait
+ broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
+ wmHelper.waitPipWindowShown()
+ wmHelper.waitForAppTransitionIdle()
+ }
+ assertions {
+ val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
+ val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
+
+ presubmit {
+ windowManagerTrace {
+ all("pipApp window is always on top") {
+ showsAppWindowOnTop(pipApp.defaultWindowName)
+ }
+ start("pipApp window hides testApp") {
+ isInvisible(testApp.defaultWindowName)
+ }
+ end("testApp windows is shown") {
+ isVisible(testApp.defaultWindowName)
+ }
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+
+ layersTrace {
+ start("pipApp layer hides testApp") {
+ hasVisibleRegion(pipApp.defaultWindowName, startingBounds)
+ isInvisible(testApp.defaultWindowName)
+ }
+ }
+ }
+
+ flaky {
+ layersTrace {
+ end("testApp layer covers fullscreen") {
+ hasVisibleRegion(testApp.defaultWindowName, endingBounds)
+ }
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
new file mode 100644
index 000000000000..707d28d9c4c0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.wm.shell.flicker.pip
+
+import android.app.WindowConfiguration
+import android.content.ComponentName
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerStateSubject
+import com.android.server.wm.traces.common.windowmanager.WindowManagerState
+import com.android.server.wm.traces.parser.toWindowName
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.google.common.truth.Truth
+
+inline val WindowManagerState.pinnedWindows
+ get() = visibleWindows
+ .filter { it.windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED }
+
+/**
+ * Checks if the state has any window in PIP mode
+ */
+fun WindowManagerState.hasPipWindow(): Boolean = pinnedWindows.isNotEmpty()
+
+/**
+ * Checks that an activity [activity] is in PIP mode
+ */
+fun WindowManagerState.isInPipMode(activity: ComponentName): Boolean {
+ val windowName = activity.toWindowName()
+ return pinnedWindows.any { it.title == windowName }
+}
+
+/**
+ * Asserts that an activity [activity] exists and is in PIP mode
+ */
+fun WindowManagerStateSubject.isInPipMode(
+ activity: ComponentName
+): WindowManagerStateSubject = apply {
+ val windowName = activity.toWindowName()
+ hasWindow(windowName)
+ val pinnedWindows = wmState.pinnedWindows
+ .map { it.title }
+ Truth.assertWithMessage("Window not in PIP mode")
+ .that(pinnedWindows)
+ .contains(windowName)
+}
+
+/**
+ * Waits until the state has a window in PIP mode, i.e., with
+ * windowingMode = WindowConfiguration.WINDOWING_MODE_PINNED
+ */
+fun WindowManagerStateHelper.waitPipWindowShown(): Boolean =
+ waitFor("PIP window shown") {
+ val result = it.wmState.hasPipWindow()
+ result
+ }
+
+/**
+ * Waits until the state doesn't have a window in PIP mode, i.e., with
+ * windowingMode = WindowConfiguration.WINDOWING_MODE_PINNED
+ */
+fun WindowManagerStateHelper.waitPipWindowGone(): Boolean =
+ waitFor("PIP window gone") {
+ val result = !it.wmState.hasPipWindow()
+ result
+ }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index c21b594246b9..7576e24ace19 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -16,21 +16,19 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Presubmit
+import android.os.Bundle
import android.view.Surface
import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.dsl.runWithFlicker
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.startRotation
import com.android.wm.shell.flicker.IME_WINDOW_NAME
import com.android.wm.shell.flicker.helpers.ImeAppHelper
-import com.android.wm.shell.flicker.testapp.Components
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -39,114 +37,61 @@ import org.junit.runners.Parameterized
* Test Pip launch.
* To run this test: `atest WMShellFlickerTests:PipKeyboardTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipKeyboardTest(
- rotationName: String,
- rotation: Int
-) : PipTestBase(rotationName, rotation) {
- private val keyboardApp = ImeAppHelper(instrumentation)
- private val keyboardComponent = Components.ImeActivity.COMPONENT
- private val helper = WindowManagerStateHelper()
+class PipKeyboardTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTestRunner(testSpec) {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+ private const val TAG_IME_VISIBLE = "imeIsVisible"
- private val keyboardScenario: FlickerBuilder
- get() = FlickerBuilder(instrumentation).apply {
- repeat { TEST_REPETITIONS }
- // disable layer tracing
- withLayerTracing { null }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- device.pressHome()
- // launch our target pip app
- testApp.launchViaIntent(wmHelper)
- this.setRotation(rotation)
- testApp.clickEnterPipButton()
- // open an app with an input field and a keyboard
- // UiAutomator doesn't support to launch the multiple Activities in a task.
- // So use launchActivity() for the Keyboard Activity.
- keyboardApp.launchViaIntent()
- helper.waitForAppTransitionIdle()
- helper.waitForFullScreenApp(keyboardComponent)
- }
- }
- teardown {
- test {
- keyboardApp.exit()
-
- if (device.hasPipWindow()) {
- device.closePipWindow()
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val imeApp = ImeAppHelper(instrumentation)
+ val baseConfig = getTransitionLaunch(eachRun = false)
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ setup {
+ test {
+ imeApp.launchViaIntent(wmHelper)
+ setRotation(configuration.startRotation)
}
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
}
- }
- }
-
- /** Ensure the pip window remains visible throughout any keyboard interactions. */
- @Test
- fun pipWindow_doesNotLeaveTheScreen_onKeyboardOpenClose() {
- val testTag = "pipWindow_doesNotLeaveTheScreen_onKeyboardOpenClose"
- runWithFlicker(keyboardScenario) {
- withTestName { testTag }
- transitions {
- // open the soft keyboard
- keyboardApp.openIME(device, wmHelper)
- helper.waitImeWindowShown()
-
- // then close it again
- keyboardApp.closeIME(device, wmHelper)
- helper.waitImeWindowGone()
- }
- assertions {
- windowManagerTrace {
- all("PiP window must remain inside visible bounds") {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- coversAtMostRegion(testApp.defaultWindowName, displayBounds)
+ teardown {
+ test {
+ imeApp.exit()
+ setRotation(Surface.ROTATION_0)
}
}
- }
- }
- }
+ transitions {
+ // open the soft keyboard
+ imeApp.openIME(wmHelper)
+ createTag(TAG_IME_VISIBLE)
- /** Ensure the pip window does not obscure the keyboard. */
- @Test
- fun pipWindow_doesNotObscure_keyboard() {
- val testTag = "pipWindow_doesNotObscure_keyboard"
- runWithFlicker(keyboardScenario) {
- withTestName { testTag }
- transitions {
- // open the soft keyboard
- keyboardApp.openIME(device, wmHelper)
- helper.waitImeWindowShown()
- }
- teardown {
- eachRun {
- // close the keyboard
- keyboardApp.closeIME(device, wmHelper)
- helper.waitImeWindowGone()
+ // then close it again
+ imeApp.closeIME(wmHelper)
}
- }
- assertions {
- windowManagerTrace {
- end("imeWindowAboveApp") {
- isAboveWindow(IME_WINDOW_NAME, testApp.defaultWindowName)
+ assertions {
+ presubmit {
+ windowManagerTrace {
+ // Ensure the pip window remains visible throughout
+ // any keyboard interactions
+ all("pipInVisibleBounds") {
+ val displayBounds = WindowUtils.getDisplayBounds(
+ configuration.startRotation)
+ coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
+ }
+ // Ensure that the pip window does not obscure the keyboard
+ tag(TAG_IME_VISIBLE) {
+ isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName)
+ }
+ }
}
}
}
- }
- }
-
- companion object {
- private const val TEST_REPETITIONS = 5
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ baseConfig, testSpec, supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index e5790962c025..f10bd7f1e45a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -32,6 +32,7 @@ import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.removeAllTasksButHome
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.FixMethodOrder
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
deleted file mode 100644
index 5e0760ceeda7..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.content.Intent
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.runFlicker
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.wm.shell.flicker.helpers.PipAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_PIP_ORIENTATION
-import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
-import org.junit.Assert.assertEquals
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test Pip with orientation changes.
- * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipOrientationTest(
- rotationName: String,
- rotation: Int
-) : AppTestBase(rotationName, rotation) {
- // Helper class to process test actions by broadcast.
- private inner class BroadcastActionTrigger {
- private fun createIntentWithAction(broadcastAction: String): Intent {
- return Intent(broadcastAction).setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- }
- fun doAction(broadcastAction: String) {
- instrumentation.getContext().sendBroadcast(createIntentWithAction(broadcastAction))
- }
- fun requestOrientationForPip(orientation: Int) {
- instrumentation.getContext()
- .sendBroadcast(createIntentWithAction(ACTION_SET_REQUESTED_ORIENTATION)
- .putExtra(EXTRA_PIP_ORIENTATION, orientation.toString()))
- }
- }
- private val broadcastActionTrigger = BroadcastActionTrigger()
-
- // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
- private val ORIENTATION_LANDSCAPE = 0
- // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
- private val ORIENTATION_PORTRAIT = 1
-
- private val testApp = FixedAppHelper(instrumentation)
- private val pipApp = PipAppHelper(instrumentation)
-
- @Test
- fun testEnterPipToOtherOrientation() {
- runFlicker(instrumentation) {
- withTestName { "testEnterPipToOtherOrientation" }
- setup {
- test {
- removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- // Launch a portrait only app on the fullscreen stack
- testApp.launchViaIntent(stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()))
- waitForAnimationComplete()
- // Launch the PiP activity fixed as landscape
- pipApp.launchViaIntent(stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
- waitForAnimationComplete()
- }
- }
- transitions {
- // Enter PiP, and assert that the PiP is within bounds now that the device is back
- // in portrait
- broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
- waitForAnimationComplete()
- }
- teardown {
- test {
- removeAllTasksButHome()
- }
- }
- assertions {
- windowManagerTrace {
- all("pipApp window is always on top") {
- showsAppWindowOnTop(pipApp.defaultWindowName)
- }
- start("pipApp window hides testApp") {
- isInvisible(testApp.defaultWindowName)
- }
- end("testApp windows is shown") {
- isVisible(testApp.defaultWindowName)
- }
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- layersTrace {
- val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
- val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
- start("pipApp layer hides testApp") {
- hasVisibleRegion(pipApp.defaultWindowName, startingBounds)
- isInvisible(testApp.defaultWindowName)
- }
- end("testApp layer covers fullscreen", enabled = false) {
- hasVisibleRegion(testApp.defaultWindowName, endingBounds)
- }
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- }
- }
- }
- }
-
- @Test
- fun testSetRequestedOrientationWhilePinned() {
- runFlicker(instrumentation) {
- withTestName { "testSetRequestedOrientationWhilePinned" }
- setup {
- test {
- removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- // Launch the PiP activity fixed as landscape
- pipApp.launchViaIntent(stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(),
- EXTRA_ENTER_PIP to "true"))
- waitForAnimationComplete()
- assertEquals(Surface.ROTATION_0, device.displayRotation)
- }
- }
- transitions {
- // Request that the orientation is set to landscape
- broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE)
-
- // Launch the activity back into fullscreen and ensure that it is now in landscape
- pipApp.launchViaIntent()
- waitForAnimationComplete()
- assertEquals(Surface.ROTATION_90, device.displayRotation)
- }
- teardown {
- test {
- removeAllTasksButHome()
- }
- }
- assertions {
- val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
- val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
- windowManagerTrace {
- start("PIP window must remain inside display") {
- coversAtMostRegion(pipApp.defaultWindowName, startingBounds)
- }
- end("pipApp shows on top") {
- showsAppWindowOnTop(pipApp.defaultWindowName)
- }
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- layersTrace {
- start("PIP layer must remain inside display") {
- coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
- }
- end("pipApp layer covers fullscreen") {
- hasVisibleRegion(pipApp.defaultWindowName, endingBounds)
- }
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- }
- }
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index a00c5f463a50..adab5e81b32d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -16,21 +16,18 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Presubmit
+import android.os.Bundle
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.wm.shell.flicker.helpers.PipAppHelper
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -38,7 +35,6 @@ import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -48,80 +44,77 @@ import org.junit.runners.Parameterized
* Test Pip Stack in bounds after rotations.
* To run this test: `atest WMShellFlickerTests:PipRotationTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class PipRotationTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
- companion object {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = FixedAppHelper(instrumentation)
- val pipApp = PipAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)) {
- configuration ->
- withTestName { buildTestTag("PipRotationTest", testApp, configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- AppTestBase.removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- pipApp.launchViaIntent(stringExtras = mapOf(
- EXTRA_ENTER_PIP to "true"))
- testApp.launchViaIntent()
- AppTestBase.waitForAnimationComplete()
- }
- eachRun {
- setRotation(configuration.startRotation)
- }
- }
- transitions {
- setRotation(configuration.endRotation)
+ val fixedApp = FixedAppHelper(instrumentation)
+ val baseConfig = getTransitionLaunch(eachRun = false)
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ setup {
+ test {
+ fixedApp.launchViaIntent(wmHelper)
+ }
+ eachRun {
+ setRotation(configuration.startRotation)
+ }
+ }
+ transitions {
+ setRotation(configuration.endRotation)
+ }
+ teardown {
+ eachRun {
+ setRotation(Surface.ROTATION_0)
+ }
+ }
+ assertions {
+ val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
+ val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation)
+
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
}
- teardown {
- eachRun {
- setRotation(Surface.ROTATION_0)
- }
- test {
- AppTestBase.removeAllTasksButHome()
- }
+
+ layersTrace {
+ noUncoveredRegions(configuration.startRotation,
+ configuration.endRotation, allStates = false)
}
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- noUncoveredRegions(configuration.startRotation,
- configuration.endRotation, allStates = false)
- navBarLayerRotatesAndScales(configuration.startRotation,
- configuration.endRotation, bugId = 140855415)
- statusBarLayerRotatesScales(configuration.startRotation,
- configuration.endRotation, bugId = 140855415)
+ }
+
+ flaky {
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ configuration.endRotation, bugId = 140855415)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ configuration.endRotation, bugId = 140855415)
+
+ start("appLayerRotates_StartingBounds", bugId = 140855415) {
+ hasVisibleRegion(fixedApp.defaultWindowName, startingBounds)
+ coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
}
- layersTrace {
- val startingBounds = WindowUtils.getDisplayBounds(
- configuration.startRotation)
- val endingBounds = WindowUtils.getDisplayBounds(
- configuration.endRotation)
- start("appLayerRotates_StartingBounds", bugId = 140855415) {
- hasVisibleRegion(testApp.defaultWindowName, startingBounds)
- coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
- }
- end("appLayerRotates_EndingBounds", bugId = 140855415) {
- hasVisibleRegion(testApp.defaultWindowName, endingBounds)
- coversAtMostRegion(endingBounds, pipApp.defaultWindowName)
- }
+ end("appLayerRotates_EndingBounds", bugId = 140855415) {
+ hasVisibleRegion(fixedApp.defaultWindowName, endingBounds)
+ coversAtMostRegion(endingBounds, pipApp.defaultWindowName)
}
}
}
+ }
+ }
+
+ return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation,
+ baseConfig, testSpec,
+ supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
+ repetitions = 5)
}
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 3e7eb134e627..4b826ffd646d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -16,29 +16,23 @@
package com.android.wm.shell.flicker.pip
+import android.os.Bundle
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.focusChanges
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.expandPipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.helpers.PipAppHelper
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -51,48 +45,30 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 152738416)
class PipToAppTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
- companion object {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = PipAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
- withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- device.pressHome()
- testApp.launchViaIntent(wmHelper)
- }
- eachRun {
- this.setRotation(configuration.startRotation)
- testApp.clickEnterPipButton()
- device.hasPipWindow()
- }
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- testApp.exit()
- }
+ val baseConfig = getTransitionLaunch(eachRun = true)
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ setup {
+ eachRun {
+ this.setRotation(configuration.startRotation)
}
- transitions {
- device.expandPipWindow()
- device.waitForIdle()
+ }
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
}
- assertions {
+ }
+ transitions {
+ pipApp.expandPipWindowToApp(wmHelper)
+ }
+ assertions {
+ presubmit {
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
@@ -100,34 +76,42 @@ class PipToAppTest(
all("appReplacesPipWindow") {
this.showsAppWindow(PIP_WINDOW_TITLE)
.then()
- .showsAppWindowOnTop(testApp.launcherName)
+ .showsAppWindowOnTop(pipApp.launcherName)
}
}
layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
- enabled = false)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0, bugId = 140855415)
statusBarLayerRotatesScales(configuration.startRotation,
Surface.ROTATION_0)
all("appReplacesPipLayer") {
this.showsLayer(PIP_WINDOW_TITLE)
.then()
- .showsLayer(testApp.launcherName)
+ .showsLayer(pipApp.launcherName)
}
}
+ }
+
+ flaky {
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0, bugId = 140855415)
+ }
eventLog {
focusChanges(
- "NexusLauncherActivity", testApp.launcherName,
+ "NexusLauncherActivity", pipApp.launcherName,
"NexusLauncherActivity", bugId = 151179149)
}
}
}
+ }
+
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig,
+ testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
index 5d3bc1388686..62e82212b1d1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
@@ -16,28 +16,23 @@
package com.android.wm.shell.flicker.pip
+import android.os.Bundle
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.focusChanges
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.helpers.PipAppHelper
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -50,50 +45,30 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 152738416)
class PipToHomeTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
- companion object {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = PipAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
- withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- device.pressHome()
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.startRotation)
- testApp.clickEnterPipButton()
- device.hasPipWindow()
- }
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- }
- test {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- testApp.exit()
- }
+ val baseConfig = getTransitionLaunch(eachRun = true)
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ setup {
+ eachRun {
+ this.setRotation(configuration.startRotation)
}
- transitions {
- testApp.closePipWindow()
+ }
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
}
- assertions {
+ }
+ transitions {
+ pipApp.closePipWindow(wmHelper)
+ }
+ assertions {
+ presubmit {
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
@@ -106,12 +81,7 @@ class PipToHomeTest(
}
layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
- enabled = false)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0, bugId = 140855415)
statusBarLayerRotatesScales(configuration.startRotation,
Surface.ROTATION_0)
@@ -121,13 +91,28 @@ class PipToHomeTest(
.hidesLayer(PIP_WINDOW_TITLE)
}
}
+ }
+ postsubmit {
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
+
+ flaky {
eventLog {
- focusChanges(testApp.launcherName, "NexusLauncherActivity",
+ focusChanges(pipApp.launcherName, "NexusLauncherActivity",
bugId = 151179149)
}
}
}
+ }
+
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig,
+ testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
}
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt
new file mode 100644
index 000000000000..eb7bae160577
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.wm.shell.flicker.pip
+
+import android.app.Instrumentation
+import android.content.Intent
+import android.os.Bundle
+import android.view.Surface
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.closePipWindow
+import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.repetitions
+import com.android.wm.shell.flicker.helpers.PipAppHelper
+import com.android.wm.shell.flicker.removeAllTasksButHome
+import com.android.wm.shell.flicker.testapp.Components
+
+abstract class PipTransitionBase(protected val instrumentation: Instrumentation) {
+ // Helper class to process test actions by broadcast.
+ protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
+ private fun createIntentWithAction(broadcastAction: String): Intent {
+ return Intent(broadcastAction).setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ }
+
+ fun doAction(broadcastAction: String) {
+ instrumentation.context
+ .sendBroadcast(createIntentWithAction(broadcastAction))
+ }
+
+ fun requestOrientationForPip(orientation: Int) {
+ instrumentation.context.sendBroadcast(
+ createIntentWithAction(Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION)
+ .putExtra(Components.PipActivity.EXTRA_PIP_ORIENTATION, orientation.toString())
+ )
+ }
+
+ companion object {
+ // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ @JvmStatic
+ val ORIENTATION_LANDSCAPE = 0
+
+ // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+ @JvmStatic
+ val ORIENTATION_PORTRAIT = 1
+ }
+ }
+
+ protected val pipApp = PipAppHelper(instrumentation)
+ protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
+
+ /**
+ * Gets a configuration that handles basic setup and teardown of pip tests
+ */
+ protected val setupAndTeardown: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ withTestName { buildTestTag(configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ removeAllTasksButHome()
+ device.wakeUpAndGoToHomeScreen()
+ }
+ }
+ teardown {
+ eachRun {
+ setRotation(Surface.ROTATION_0)
+ }
+ test {
+ removeAllTasksButHome()
+
+ if (device.hasPipWindow()) {
+ device.closePipWindow()
+ }
+ pipApp.exit()
+ }
+ }
+ }
+
+ /**
+ * Gets a configuration that handles basic setup and teardown of pip tests and that
+ * launches the Pip app for test
+ *
+ * @param eachRun If the pip app should be launched in each run (otherwise only 1x per test)
+ * @param stringExtras Arguments to pass to the PIP launch intent
+ */
+ @JvmOverloads
+ fun getTransitionLaunch(
+ eachRun: Boolean,
+ stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true")
+ ): FlickerBuilder.(Bundle) -> Unit {
+ return { configuration ->
+ setupAndTeardown(this, configuration)
+
+ setup {
+ test {
+ removeAllTasksButHome()
+ if (!eachRun) {
+ pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+ wmHelper.waitPipWindowShown()
+ }
+ }
+ eachRun {
+ if (eachRun) {
+ pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+ wmHelper.waitPipWindowShown()
+ }
+ }
+ }
+ teardown {
+ eachRun {
+ if (eachRun) {
+ pipApp.exit()
+ }
+ }
+ test {
+ if (!eachRun) {
+ pipApp.exit()
+ }
+ removeAllTasksButHome()
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
new file mode 100644
index 000000000000..c01bc94151e9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip
+
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
+import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
+import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
+import org.junit.Assert.assertEquals
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip with orientation changes.
+ * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SetRequestedOrientationWhilePinnedTest(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 1) { configuration ->
+ setupAndTeardown(this, configuration)
+
+ setup {
+ eachRun {
+ // Launch the PiP activity fixed as landscape
+ pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(),
+ EXTRA_ENTER_PIP to "true"))
+ }
+ }
+ teardown {
+ eachRun {
+ pipApp.exit()
+ }
+ }
+ transitions {
+ // Request that the orientation is set to landscape
+ broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE)
+
+ // Launch the activity back into fullscreen and
+ // ensure that it is now in landscape
+ pipApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(pipApp.component)
+ wmHelper.waitForRotation(Surface.ROTATION_90)
+ assertEquals(Surface.ROTATION_90, device.displayRotation)
+ }
+ assertions {
+ val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
+ val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
+ presubmit {
+ windowManagerTrace {
+ start("PIP window must remain inside display") {
+ coversAtMostRegion(pipApp.defaultWindowName, startingBounds)
+ }
+ end("pipApp shows on top") {
+ showsAppWindowOnTop(pipApp.defaultWindowName)
+ }
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+ layersTrace {
+ start("PIP layer must remain inside display") {
+ coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
+ }
+ end("pipApp layer covers fullscreen") {
+ hasVisibleRegion(pipApp.defaultWindowName, endingBounds)
+ }
+ }
+ }
+
+ flaky {
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 27be62269959..d5fee3f667a9 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -35,7 +35,6 @@ class DeviceInfo {
public:
static DeviceInfo* get();
- static float getMaxRefreshRate() { return get()->mMaxRefreshRate; }
static int32_t getWidth() { return get()->mWidth; }
static int32_t getHeight() { return get()->mHeight; }
// Gets the density in density-independent pixels
@@ -45,7 +44,6 @@ public:
static int64_t getAppOffset() { return get()->mAppVsyncOffsetNanos; }
// Sets the density in density-independent pixels
static void setDensity(float density) { sDensity.store(density); }
- static void setMaxRefreshRate(float refreshRate) { get()->mMaxRefreshRate = refreshRate; }
static void setWidth(int32_t width) { get()->mWidth = width; }
static void setHeight(int32_t height) { get()->mHeight = height; }
static void setRefreshRate(float refreshRate) {
@@ -91,7 +89,6 @@ private:
SkColorType mWideColorType = SkColorType::kN32_SkColorType;
int mDisplaysSize = 0;
int mPhysicalDisplayIndex = -1;
- float mMaxRefreshRate = 60.0;
int32_t mWidth = 1080;
int32_t mHeight = 1920;
int64_t mVsyncPeriod = 16666666;
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index e798f2a2bc69..971a53a8b2dc 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -79,7 +79,6 @@ bool Properties::debuggingEnabled = false;
bool Properties::isolatedProcess = false;
int Properties::contextPriority = 0;
-int Properties::defaultRenderAhead = -1;
float Properties::defaultSdrWhitePoint = 200.f;
bool Properties::load() {
@@ -129,10 +128,6 @@ bool Properties::load() {
runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false);
- defaultRenderAhead = std::max(
- -1,
- std::min(2, base::GetIntProperty(PROPERTY_RENDERAHEAD, render_ahead().value_or(-1))));
-
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 1639143ef87c..dcb79babad24 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -162,8 +162,6 @@ enum DebugLevel {
*/
#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu"
-#define PROPERTY_RENDERAHEAD "debug.hwui.render_ahead"
-
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -247,8 +245,6 @@ public:
static int contextPriority;
- static int defaultRenderAhead;
-
static float defaultSdrWhitePoint;
private:
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index a146b64e29cc..4966bfa1c1e9 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -603,14 +603,12 @@ static void android_view_ThreadedRenderer_setDisplayDensityDpi(JNIEnv*, jclass,
static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint physicalWidth,
jint physicalHeight, jfloat refreshRate,
- jfloat maxRefreshRate,
jint wideColorDataspace,
jlong appVsyncOffsetNanos,
jlong presentationDeadlineNanos) {
DeviceInfo::setWidth(physicalWidth);
DeviceInfo::setHeight(physicalHeight);
DeviceInfo::setRefreshRate(refreshRate);
- DeviceInfo::setMaxRefreshRate(maxRefreshRate);
DeviceInfo::setWideColorDataspace(static_cast<ADataSpace>(wideColorDataspace));
DeviceInfo::setAppVsyncOffsetNanos(appVsyncOffsetNanos);
DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos);
@@ -735,7 +733,7 @@ static const JNINativeMethod gMethods[] = {
{"nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark},
{"nSetDisplayDensityDpi", "(I)V",
(void*)android_view_ThreadedRenderer_setDisplayDensityDpi},
- {"nInitDisplayInfo", "(IIFFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
+ {"nInitDisplayInfo", "(IIFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
{"preload", "()V", (void*)android_view_ThreadedRenderer_preload},
};
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 3392dac02493..c8471a9b8feb 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -219,7 +219,7 @@ static jlong Font_getBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
}
// Critical Native
-static jlong Font_getReleaseNativeFontFunc() {
+static jlong Font_getReleaseNativeFontFunc(CRITICAL_JNI_PARAMS) {
return reinterpret_cast<jlong>(releaseFont);
}
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index 80964794efb2..b68213549938 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -101,13 +101,13 @@ static jint FontFamily_getVariant(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) {
}
// CriticalNative
-static jint FontFamily_getFontSize(jlong familyPtr) {
+static jint FontFamily_getFontSize(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) {
FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
return family->family->getNumFonts();
}
// CriticalNative
-static jlong FontFamily_getFont(jlong familyPtr, jint index) {
+static jlong FontFamily_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr, jint index) {
FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
std::shared_ptr<minikin::Font> font = family->family->getFontRef(index);
return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 37a6ee71c4a6..65afcc3a2558 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -108,7 +108,6 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode*
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
mProfiler.setDensity(DeviceInfo::getDensity());
- setRenderAheadDepth(Properties::defaultRenderAhead);
}
CanvasContext::~CanvasContext() {
@@ -157,24 +156,17 @@ static void setBufferCount(ANativeWindow* window) {
void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
ATRACE_CALL();
- if (mFixedRenderAhead) {
- mRenderAheadCapacity = mRenderAheadDepth;
- } else {
- if (DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
- mRenderAheadCapacity = 1;
- } else {
- mRenderAheadCapacity = 0;
- }
- }
-
if (window) {
+ int extraBuffers = 0;
+ native_window_get_extra_buffer_count(window, &extraBuffers);
+
mNativeSurface = std::make_unique<ReliableSurface>(window);
mNativeSurface->init();
if (enableTimeout) {
// TODO: Fix error handling & re-shorten timeout
ANativeWindow_setDequeueTimeout(window, 4000_ms);
}
- mNativeSurface->setExtraBufferCount(mRenderAheadCapacity);
+ mNativeSurface->setExtraBufferCount(extraBuffers);
} else {
mNativeSurface = nullptr;
}
@@ -441,24 +433,6 @@ void CanvasContext::notifyFramePending() {
mRenderThread.pushBackFrameCallback(this);
}
-void CanvasContext::setPresentTime() {
- int64_t presentTime = NATIVE_WINDOW_TIMESTAMP_AUTO;
- int renderAhead = 0;
- const auto frameIntervalNanos = mRenderThread.timeLord().frameIntervalNanos();
- if (mFixedRenderAhead) {
- renderAhead = std::min(mRenderAheadDepth, mRenderAheadCapacity);
- } else if (frameIntervalNanos < 15_ms) {
- renderAhead = std::min(1, static_cast<int>(mRenderAheadCapacity));
- }
-
- if (renderAhead) {
- presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
- (frameIntervalNanos * (renderAhead + 1)) - DeviceInfo::get()->getAppOffset() +
- (frameIntervalNanos / 2);
- }
- native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime);
-}
-
void CanvasContext::draw() {
SkRect dirty;
mDamageAccumulator.finish(&dirty);
@@ -478,8 +452,6 @@ void CanvasContext::draw() {
mCurrentFrameInfo->markIssueDrawCommandsStart();
Frame frame = mRenderPipeline->getFrame();
- setPresentTime();
-
SkRect windowDirty = computeDirtyRect(frame, &dirty);
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
@@ -765,19 +737,6 @@ bool CanvasContext::surfaceRequiresRedraw() {
return width != mLastFrameWidth || height != mLastFrameHeight;
}
-void CanvasContext::setRenderAheadDepth(int renderAhead) {
- if (renderAhead > 2 || renderAhead < -1 || mNativeSurface) {
- return;
- }
- if (renderAhead == -1) {
- mFixedRenderAhead = false;
- mRenderAheadDepth = 0;
- } else {
- mFixedRenderAhead = true;
- mRenderAheadDepth = static_cast<uint32_t>(renderAhead);
- }
-}
-
SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) {
// can't rely on prior content of window if viewport size changes
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index cc4eb3285bbe..b31883b9ae94 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -193,9 +193,6 @@ public:
return mUseForceDark;
}
- // Must be called before setSurface
- void setRenderAheadDepth(int renderAhead);
-
SkISize getNextFrameSize() const;
private:
@@ -211,7 +208,6 @@ private:
bool isSwapChainStuffed();
bool surfaceRequiresRedraw();
- void setPresentTime();
void setupPipelineSurface();
SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
@@ -232,9 +228,6 @@ private:
// painted onto its surface.
bool mIsDirty = false;
SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default;
- bool mFixedRenderAhead = false;
- uint32_t mRenderAheadDepth = 0;
- uint32_t mRenderAheadCapacity = 0;
struct SwapHistory {
SkRect damage;
nsecs_t vsyncTime;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b51f6dcfc66f..0ade8dde12eb 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -295,11 +295,6 @@ void RenderProxy::setForceDark(bool enable) {
mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
}
-void RenderProxy::setRenderAheadDepth(int renderAhead) {
- mRenderThread.queue().post(
- [context = mContext, renderAhead] { context->setRenderAheadDepth(renderAhead); });
-}
-
int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom,
SkBitmap* bitmap) {
auto& thread = RenderThread::getInstance();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 33dabc9895b1..a4adb16a930e 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -123,23 +123,6 @@ public:
void removeFrameMetricsObserver(FrameMetricsObserver* observer);
void setForceDark(bool enable);
- /**
- * Sets a render-ahead depth on the backing renderer. This will increase latency by
- * <swapInterval> * renderAhead and increase memory usage by (3 + renderAhead) * <resolution>.
- * In return the renderer will be less susceptible to jitter, resulting in a smoother animation.
- *
- * Not recommended to use in response to anything touch driven, but for canned animations
- * where latency is not a concern careful use may be beneficial.
- *
- * Note that when increasing this there will be a frame gap of N frames where N is
- * renderAhead - <current renderAhead>. When decreasing this if there are any pending
- * frames they will retain their prior renderAhead value, so it will take a few frames
- * for the decrease to flush through.
- *
- * @param renderAhead How far to render ahead, must be in the range [0..2]
- */
- void setRenderAheadDepth(int renderAhead);
-
static int copySurfaceInto(ANativeWindow* window, int left, int top, int right,
int bottom, SkBitmap* bitmap);
static void prepareToDraw(Bitmap& bitmap);
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index 74a039b3d090..91022cfe734b 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -38,7 +38,6 @@ public:
int count = 0;
int reportFrametimeWeight = 0;
bool renderOffscreen = true;
- int renderAhead = 0;
};
template <class T>
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index eda5d2266dcf..8c7d2612f39b 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -153,11 +153,6 @@ void run(const TestScene::Info& info, const TestScene::Options& opts,
proxy->resetProfileInfo();
proxy->fence();
- if (opts.renderAhead) {
- usleep(33000);
- }
- proxy->setRenderAheadDepth(opts.renderAhead);
-
ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight);
nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC);
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 88d33c315a09..174a14080eff 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -69,7 +69,6 @@ OPTIONS:
are offscreen rendered
--benchmark_format Set output format. Possible values are tabular, json, csv
--renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk
- --render-ahead=NUM Sets how far to render-ahead. Must be 0 (default), 1, or 2.
)");
}
@@ -171,7 +170,6 @@ enum {
Onscreen,
Offscreen,
Renderer,
- RenderAhead,
};
}
@@ -187,7 +185,6 @@ static const struct option LONG_OPTIONS[] = {
{"onscreen", no_argument, nullptr, LongOpts::Onscreen},
{"offscreen", no_argument, nullptr, LongOpts::Offscreen},
{"renderer", required_argument, nullptr, LongOpts::Renderer},
- {"render-ahead", required_argument, nullptr, LongOpts::RenderAhead},
{0, 0, 0, 0}};
static const char* SHORT_OPTIONS = "c:r:h";
@@ -286,16 +283,6 @@ void parseOptions(int argc, char* argv[]) {
gOpts.renderOffscreen = true;
break;
- case LongOpts::RenderAhead:
- if (!optarg) {
- error = true;
- }
- gOpts.renderAhead = atoi(optarg);
- if (gOpts.renderAhead < 0 || gOpts.renderAhead > 2) {
- error = true;
- }
- break;
-
case 'h':
printHelp();
exit(EXIT_SUCCESS);
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index d40de76431e4..205c1f4b4057 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -354,7 +354,7 @@ public final class AudioDeviceInfo {
/**
* @hide
- * @return the internal device tyoe
+ * @return the internal device type
*/
public int getInternalType() {
return mPort.type();
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index fd71670ee404..606cd5718d98 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -322,7 +322,8 @@ public class DeviceDiscoveryService extends Service {
void onDeviceSelected(String callingPackage, String deviceAddress) {
mServiceCallback.complete(new Association(
- getUserId(), deviceAddress, callingPackage, mRequest.getDeviceProfile(), false));
+ getUserId(), deviceAddress, callingPackage, mRequest.getDeviceProfile(), false,
+ System.currentTimeMillis()));
}
void onCancel() {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index e38cf237d36b..5c943f63a9a1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -245,9 +245,11 @@ interface ISystemUiProxy {
void setSideStageVisibility(in boolean visible) = 36;
/** Removes the split-screen stages. */
void exitSplitScreen() = 37;
- void startTask(in int taskId, in int stage, in int position, in Bundle options) = 38;
+ /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */
+ void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 38;
+ void startTask(in int taskId, in int stage, in int position, in Bundle options) = 39;
void startShortcut(in String packageName, in String shortcutId, in int stage, in int position,
- in Bundle options, in UserHandle user) = 39;
+ in Bundle options, in UserHandle user) = 40;
void startIntent(
- in PendingIntent intent, in int stage, in int position, in Bundle options) = 40;
+ in PendingIntent intent, in int stage, in int position, in Bundle options) = 41;
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
index 429f67fdc706..d06568a7caf9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -24,6 +24,9 @@ import android.service.controls.Control
*/
interface ControlActionCoordinator {
+ // Handle actions launched from GlobalActionsDialog or ControlDialog
+ var startedFromGlobalActions: Boolean
+
/**
* Close any dialogs which may have been open
*/
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 7cd7e18965c2..247f25e1ccea 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -30,6 +30,7 @@ import android.service.controls.actions.CommandAction
import android.service.controls.actions.FloatAction
import android.util.Log
import android.view.HapticFeedbackConstants
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsComponent
@@ -37,6 +38,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.wm.shell.TaskViewFactory
+import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
@@ -48,13 +50,17 @@ class ControlActionCoordinatorImpl @Inject constructor(
private val activityStarter: ActivityStarter,
private val keyguardStateController: KeyguardStateController,
private val globalActionsComponent: GlobalActionsComponent,
- private val taskViewFactory: Optional<TaskViewFactory>
+ private val taskViewFactory: Optional<TaskViewFactory>,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val lazyUiController: Lazy<ControlsUiController>
) : ControlActionCoordinator {
private var dialog: Dialog? = null
private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
private var pendingAction: Action? = null
private var actionsInProgress = mutableSetOf<String>()
+ override var startedFromGlobalActions: Boolean = true
+
companion object {
private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L
}
@@ -131,8 +137,8 @@ class ControlActionCoordinatorImpl @Inject constructor(
private fun bouncerOrRun(action: Action) {
if (keyguardStateController.isShowing()) {
- var closeGlobalActions = !keyguardStateController.isUnlocked()
- if (closeGlobalActions) {
+ var closeDialog = !keyguardStateController.isUnlocked()
+ if (closeDialog) {
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
// pending actions will only run after the control state has been refreshed
@@ -141,8 +147,12 @@ class ControlActionCoordinatorImpl @Inject constructor(
activityStarter.dismissKeyguardThenExecute({
Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
- if (closeGlobalActions) {
- globalActionsComponent.handleShowGlobalActionsMenu()
+ if (closeDialog) {
+ if (startedFromGlobalActions) {
+ globalActionsComponent.handleShowGlobalActionsMenu()
+ } else {
+ ControlsDialog(context, broadcastDispatcher).show(lazyUiController.get())
+ }
} else {
action.invoke()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
index 8e878cf76ad9..f533cfb47076 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
@@ -66,7 +66,7 @@ class ControlsDialog(
val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls)
vg.alpha = 0f
- controller.show(vg, { /* do nothing */ })
+ controller.show(vg, { /* do nothing */ }, false /* startedFromGlobalActions */)
vg.animate()
.alpha(1f)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 4e4c82cabaa0..944887741721 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -29,7 +29,7 @@ interface ControlsUiController {
public const val EXTRA_ANIMATE = "extra_animate"
}
- fun show(parent: ViewGroup, dismissGlobalActions: Runnable)
+ fun show(parent: ViewGroup, onDismiss: Runnable, startedFromGlobalActions: Boolean)
fun hide()
/**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 2b529f9a6cde..762362cde095 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -102,7 +102,7 @@ class ControlsUiControllerImpl @Inject constructor (
private lateinit var lastItems: List<SelectionItem>
private var popup: ListPopupWindow? = null
private var hidden = true
- private lateinit var dismissGlobalActions: Runnable
+ private lateinit var onDismiss: Runnable
private val popupThemedContext = ContextThemeWrapper(context, R.style.Control_ListPopupWindow)
private var retainCache = false
@@ -145,13 +145,19 @@ class ControlsUiControllerImpl @Inject constructor (
}
}
- override fun show(parent: ViewGroup, dismissGlobalActions: Runnable) {
+ override fun show(
+ parent: ViewGroup,
+ onDismiss: Runnable,
+ startedFromGlobalActions: Boolean
+ ) {
Log.d(ControlsUiController.TAG, "show()")
this.parent = parent
- this.dismissGlobalActions = dismissGlobalActions
+ this.onDismiss = onDismiss
hidden = false
retainCache = false
+ controlActionCoordinator.startedFromGlobalActions = startedFromGlobalActions
+
allStructures = controlsController.get().getFavorites()
selectedStructure = loadPreference(allStructures)
@@ -187,7 +193,7 @@ class ControlsUiControllerImpl @Inject constructor (
controlViewsById.clear()
controlsById.clear()
- show(parent, dismissGlobalActions)
+ show(parent, onDismiss, controlActionCoordinator.startedFromGlobalActions)
val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
showAnim.setInterpolator(DecelerateInterpolator(1.0f))
showAnim.setDuration(FADE_IN_MILLIS)
@@ -260,7 +266,7 @@ class ControlsUiControllerImpl @Inject constructor (
private fun startActivity(context: Context, intent: Intent) {
// Force animations when transitioning from a dialog to an activity
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
- dismissGlobalActions.run()
+ onDismiss.run()
activityStarter.dismissKeyguardThenExecute({
shadeController.collapsePanel(false)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index ec4a91c24464..2b362b94d1f5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import com.android.systemui.ForegroundServicesDialog;
import com.android.systemui.keyguard.WorkLockActivity;
+import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.screenrecord.ScreenRecordDialog;
import com.android.systemui.settings.brightness.BrightnessDialog;
import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
@@ -92,4 +93,10 @@ public abstract class DefaultActivityBinder {
@IntoMap
@ClassKey(TvNotificationPanelActivity.class)
public abstract Activity bindTvNotificationPanelActivity(TvNotificationPanelActivity activity);
+
+ /** Inject into PeopleSpaceActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(PeopleSpaceActivity.class)
+ public abstract Activity bindPeopleSpaceActivity(PeopleSpaceActivity activity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index ad4c44767995..8af45a5c0ef1 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -2229,7 +2229,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private void showControls(ControlsUiController controller) {
mControlsUiController = controller;
- mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
+ mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
+ true /* startedFromGlobalActions */);
}
private boolean isWalletViewAvailable() {
@@ -2457,7 +2458,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return WindowInsets.CONSUMED;
});
if (mControlsUiController != null) {
- mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
+ mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
+ true /* startedFromGlobalActions */);
}
mBackgroundDrawable.setAlpha(0);
@@ -2632,7 +2634,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
initializeLayout();
mGlobalActionsLayout.updateList();
if (mControlsUiController != null) {
- mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
+ mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
+ true /* startedFromGlobalActions */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 5a918d4808d6..c55fdf4783e3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -67,6 +67,7 @@ import android.telephony.TelephonyManager;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.RemoteAnimationTarget;
@@ -306,6 +307,13 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
*/
private final SparseIntArray mLastSimStates = new SparseIntArray();
+ /**
+ * Indicates if a SIM card had the SIM PIN enabled during the initialization, before
+ * reaching the SIM_STATE_READY state. The flag is reset to false at SIM_STATE_READY.
+ * Index is the slotId - in case of multiple SIM cards.
+ */
+ private final SparseBooleanArray mSimWasLocked = new SparseBooleanArray();
+
private boolean mDeviceInteractive;
private boolean mGoingToSleep;
@@ -477,10 +485,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
}
}
- boolean simWasLocked;
+ boolean lastSimStateWasLocked;
synchronized (KeyguardViewMediator.this) {
int lastState = mLastSimStates.get(slotId);
- simWasLocked = (lastState == TelephonyManager.SIM_STATE_PIN_REQUIRED
+ lastSimStateWasLocked = (lastState == TelephonyManager.SIM_STATE_PIN_REQUIRED
|| lastState == TelephonyManager.SIM_STATE_PUK_REQUIRED);
mLastSimStates.append(slotId, simState);
}
@@ -504,17 +512,19 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
if (simState == TelephonyManager.SIM_STATE_ABSENT) {
// MVNO SIMs can become transiently NOT_READY when switching networks,
// so we should only lock when they are ABSENT.
- if (simWasLocked) {
+ if (lastSimStateWasLocked) {
if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the "
+ "previous state was locked. Reset the state.");
resetStateLocked();
}
+ mSimWasLocked.append(slotId, false);
}
}
break;
case TelephonyManager.SIM_STATE_PIN_REQUIRED:
case TelephonyManager.SIM_STATE_PUK_REQUIRED:
synchronized (KeyguardViewMediator.this) {
+ mSimWasLocked.append(slotId, true);
if (!mShowing) {
if (DEBUG_SIM_STATES) Log.d(TAG,
"INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
@@ -541,9 +551,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
case TelephonyManager.SIM_STATE_READY:
synchronized (KeyguardViewMediator.this) {
if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
- if (mShowing && simWasLocked) {
+ if (mShowing && mSimWasLocked.get(slotId, false)) {
if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to READY when the "
- + "previous state was locked. Reset the state.");
+ + "previously was locked. Reset the state.");
+ mSimWasLocked.append(slotId, false);
resetStateLocked();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 580cbcf8ce47..c67aef618652 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -37,9 +37,12 @@ import android.view.ViewGroup;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.util.List;
+import javax.inject.Inject;
+
/**
* Shows the user their tiles for their priority People (go/live-status).
*/
@@ -54,10 +57,17 @@ public class PeopleSpaceActivity extends Activity {
private LauncherApps mLauncherApps;
private Context mContext;
private AppWidgetManager mAppWidgetManager;
+ private NotificationEntryManager mNotificationEntryManager;
private int mAppWidgetId;
private boolean mShowSingleConversation;
private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+ @Inject
+ public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager) {
+ super();
+ mNotificationEntryManager = notificationEntryManager;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -91,8 +101,8 @@ public class PeopleSpaceActivity extends Activity {
*/
private void setTileViewsWithPriorityConversations() {
try {
- List<PeopleSpaceTile> tiles = PeopleSpaceUtils.getTiles(
- mContext, mNotificationManager, mPeopleManager, mLauncherApps);
+ List<PeopleSpaceTile> tiles = PeopleSpaceUtils.getTiles(mContext, mNotificationManager,
+ mPeopleManager, mLauncherApps, mNotificationEntryManager);
for (PeopleSpaceTile tile : tiles) {
PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext, mPeopleSpaceLayout,
tile.getId());
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 994dc6d78507..dd054848aed2 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -60,9 +60,12 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ArrayUtils;
import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.text.SimpleDateFormat;
import java.time.Duration;
@@ -137,7 +140,7 @@ public class PeopleSpaceUtils {
/** Returns a list of map entries corresponding to user's conversations. */
public static List<PeopleSpaceTile> getTiles(
Context context, INotificationManager notificationManager, IPeopleManager peopleManager,
- LauncherApps launcherApps)
+ LauncherApps launcherApps, NotificationEntryManager notificationEntryManager)
throws Exception {
boolean showOnlyPriority = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 1;
@@ -173,6 +176,8 @@ public class PeopleSpaceUtils {
getSortedTiles(peopleManager, launcherApps, mergedStream);
tiles.addAll(recentTiles);
}
+
+ tiles = augmentTilesFromVisibleNotifications(tiles, notificationEntryManager);
return tiles;
}
@@ -258,7 +263,8 @@ public class PeopleSpaceUtils {
ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE)),
- context.getSystemService(LauncherApps.class));
+ context.getSystemService(LauncherApps.class),
+ Dependency.get(NotificationEntryManager.class));
Optional<PeopleSpaceTile> entry = tiles.stream().filter(
e -> e.getId().equals(shortcutId)).findFirst();
if (entry.isPresent()) {
@@ -339,6 +345,41 @@ public class PeopleSpaceUtils {
&& storedUserId == userId;
}
+ static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(List<PeopleSpaceTile> tiles,
+ NotificationEntryManager notificationEntryManager) {
+ if (notificationEntryManager == null) {
+ Log.w(TAG, "NotificationEntryManager is null");
+ return tiles;
+ }
+ Map<String, NotificationEntry> visibleNotifications = notificationEntryManager
+ .getVisibleNotifications()
+ .stream()
+ .filter(entry -> entry.getRanking() != null
+ && entry.getRanking().getConversationShortcutInfo() != null)
+ .collect(Collectors.toMap(PeopleSpaceUtils::getKey, e -> e));
+ if (DEBUG) {
+ Log.d(TAG, "Number of visible notifications:" + visibleNotifications.size());
+ }
+ return tiles
+ .stream()
+ .map(entry -> augmentTileFromVisibleNotifications(entry, visibleNotifications))
+ .collect(Collectors.toList());
+ }
+
+ static PeopleSpaceTile augmentTileFromVisibleNotifications(PeopleSpaceTile tile,
+ Map<String, NotificationEntry> visibleNotifications) {
+ String shortcutId = tile.getId();
+ String packageName = tile.getPackageName();
+ int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+ String key = getKey(shortcutId, packageName, userId);
+ if (!visibleNotifications.containsKey(key)) {
+ if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key);
+ return tile;
+ }
+ if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key);
+ return augmentTileFromNotification(tile, visibleNotifications.get(key).getSbn());
+ }
+
/**
* If incoming notification changed tile, store the changes in the tile options.
*/
@@ -355,17 +396,7 @@ public class PeopleSpaceUtils {
}
if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
- Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn);
- if (message == null) {
- if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping.");
- return;
- }
- storedTile = storedTile
- .toBuilder()
- .setNotificationKey(sbn.getKey())
- .setNotificationContent(message.getText())
- .setNotificationDataUri(message.getDataUri())
- .build();
+ storedTile = augmentTileFromNotification(storedTile, sbn);
} else {
if (DEBUG) {
Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
@@ -380,6 +411,21 @@ public class PeopleSpaceUtils {
updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
}
+ static PeopleSpaceTile augmentTileFromNotification(PeopleSpaceTile tile,
+ StatusBarNotification sbn) {
+ Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn);
+ if (message == null) {
+ if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping.");
+ return tile;
+ }
+ return tile
+ .toBuilder()
+ .setNotificationKey(sbn.getKey())
+ .setNotificationContent(message.getText())
+ .setNotificationDataUri(message.getDataUri())
+ .build();
+ }
+
private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
PeopleSpaceTile tile) {
if (tile == null) {
@@ -792,6 +838,16 @@ public class PeopleSpaceUtils {
return lookupKeysWithBirthdaysToday;
}
+ static String getKey(NotificationEntry entry) {
+ if (entry.getRanking() == null || entry.getRanking().getConversationShortcutInfo() == null
+ || entry.getSbn() == null || entry.getSbn().getUser() == null) {
+ return null;
+ }
+ return getKey(entry.getRanking().getConversationShortcutInfo().getId(),
+ entry.getSbn().getPackageName(),
+ entry.getSbn().getUser().getIdentifier());
+ }
+
/**
* Returns the uniquely identifying key for the conversation.
*
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index bee54e4dca0a..bee9889eaa4e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -135,12 +135,14 @@ public class PeopleSpaceWidgetManager {
try {
String sbnShortcutId = sbn.getShortcutId();
if (sbnShortcutId == null) {
+ if (DEBUG) Log.d(TAG, "Sbn shortcut id is null");
return;
}
int[] widgetIds = mAppWidgetService.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
);
if (widgetIds.length == 0) {
+ Log.d(TAG, "No app widget ids returned");
return;
}
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
@@ -148,6 +150,7 @@ public class PeopleSpaceWidgetManager {
String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId);
Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
if (storedWidgetIds.isEmpty()) {
+ Log.d(TAG, "No stored widget ids");
return;
}
for (String widgetIdString : storedWidgetIds) {
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
index fb33affcbac5..80794cb64883 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -28,9 +28,11 @@ import android.util.Log;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.people.PeopleSpaceTileView;
import com.android.systemui.people.PeopleSpaceUtils;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.util.ArrayList;
import java.util.List;
@@ -42,6 +44,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R
private IPeopleManager mPeopleManager;
private INotificationManager mNotificationManager;
+ private NotificationEntryManager mNotificationEntryManager;
private PackageManager mPackageManager;
private LauncherApps mLauncherApps;
private List<PeopleSpaceTile> mTiles = new ArrayList<>();
@@ -56,6 +59,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R
if (DEBUG) Log.d(TAG, "onCreate called");
mNotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
mPackageManager = mContext.getPackageManager();
mPeopleManager = IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE));
@@ -70,7 +74,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R
private void setTileViewsWithPriorityConversations() {
try {
mTiles = PeopleSpaceUtils.getTiles(mContext, mNotificationManager,
- mPeopleManager, mLauncherApps);
+ mPeopleManager, mLauncherApps, mNotificationEntryManager);
} catch (Exception e) {
Log.e(TAG, "Couldn't retrieve conversations", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index d248ab544656..c8edaec98ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -362,7 +362,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
if(view == parent || view == null) return;
// Ignore tile pages as they can have some offset we don't want to take into account in
// RTL.
- if (!(view instanceof PagedTileLayout.TilePage)) {
+ if (!(view instanceof PagedTileLayout.TilePage || view instanceof SideLabelTileLayout)) {
loc1[0] += view.getLeft();
loc1[1] += view.getTop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 01a8c1c89f84..5b2a7e7ff617 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -620,6 +620,19 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
@Override
+ public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ if (!verifyCaller("exitSplitScreenOnHide")) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSplitScreenOptional.ifPresent(s -> s.exitSplitScreenOnHide(exitSplitScreenOnHide));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void startTask(int taskId, int stage, int position, Bundle options) {
if (!verifyCaller("startTask")) {
return;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index 6db21f9159ec..4ee2759028a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.people;
import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
+import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
import static com.google.common.truth.Truth.assertThat;
@@ -52,6 +53,7 @@ import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
@@ -64,6 +66,9 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import org.junit.Before;
import org.junit.Test;
@@ -82,10 +87,17 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
private static final int WIDGET_ID_WITH_SHORTCUT = 1;
private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
- private static final String SHORTCUT_ID = "101";
+ private static final String SHORTCUT_ID_1 = "101";
+ private static final String SHORTCUT_ID_2 = "202";
+ private static final String SHORTCUT_ID_3 = "303";
+ private static final String SHORTCUT_ID_4 = "404";
private static final String NOTIFICATION_KEY = "notification_key";
private static final String NOTIFICATION_CONTENT = "notification_content";
private static final String TEST_LOOKUP_KEY = "lookup_key";
+ private static final String NOTIFICATION_TEXT_1 = "notification_text_1";
+ private static final String NOTIFICATION_TEXT_2 = "notification_text_2";
+ private static final String NOTIFICATION_TEXT_3 = "notification_text_3";
+ private static final String NOTIFICATION_TEXT_4 = "notification_text_4";
private static final int TEST_COLUMN_INDEX = 1;
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
@@ -97,20 +109,66 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
.build();
private static final PeopleSpaceTile PERSON_TILE =
new PeopleSpaceTile
- .Builder(SHORTCUT_ID, "username", ICON, new Intent())
+ .Builder(SHORTCUT_ID_1, "username", ICON, new Intent())
.setNotificationKey(NOTIFICATION_KEY)
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(URI)
.build();
private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
- SHORTCUT_ID).setLongLabel(
+ SHORTCUT_ID_1).setLongLabel(
"name").setPerson(PERSON)
.build();
private final ShortcutInfo mShortcutInfoWithoutPerson = new ShortcutInfo.Builder(mContext,
- SHORTCUT_ID).setLongLabel(
+ SHORTCUT_ID_1).setLongLabel(
"name")
.build();
+ private final Notification mNotification1 = new Notification.Builder(mContext, "test")
+ .setContentTitle("TEST_TITLE")
+ .setContentText("TEST_TEXT")
+ .setShortcutId(SHORTCUT_ID_1)
+ .setStyle(new Notification.MessagingStyle(PERSON)
+ .addMessage(new Notification.MessagingStyle.Message(
+ NOTIFICATION_TEXT_1, 0, PERSON))
+ .addMessage(new Notification.MessagingStyle.Message(
+ NOTIFICATION_TEXT_2, 20, PERSON))
+ .addMessage(new Notification.MessagingStyle.Message(
+ NOTIFICATION_TEXT_3, 10, PERSON))
+ )
+ .build();
+ private final Notification mNotification2 = new Notification.Builder(mContext, "test2")
+ .setContentTitle("TEST_TITLE")
+ .setContentText("OTHER_TEXT")
+ .setShortcutId(SHORTCUT_ID_2)
+ .setStyle(new Notification.MessagingStyle(PERSON)
+ .addMessage(new Notification.MessagingStyle.Message(
+ NOTIFICATION_TEXT_4, 0, PERSON))
+ )
+ .build();
+ private final Notification mNotification3 = new Notification.Builder(mContext, "test2")
+ .setContentTitle("TEST_TITLE")
+ .setContentText("OTHER_TEXT")
+ .setShortcutId(SHORTCUT_ID_3)
+ .setStyle(new Notification.MessagingStyle(PERSON))
+ .build();
+ private final NotificationEntry mNotificationEntry1 = new NotificationEntryBuilder()
+ .setNotification(mNotification1)
+ .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_1).build())
+ .setUser(UserHandle.of(0))
+ .setPkg(PACKAGE_NAME)
+ .build();
+ private final NotificationEntry mNotificationEntry2 = new NotificationEntryBuilder()
+ .setNotification(mNotification2)
+ .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_2).build())
+ .setUser(UserHandle.of(0))
+ .setPkg(PACKAGE_NAME)
+ .build();
+ private final NotificationEntry mNotificationEntry3 = new NotificationEntryBuilder()
+ .setNotification(mNotification3)
+ .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_3).build())
+ .setUser(UserHandle.of(0))
+ .setPkg(PACKAGE_NAME)
+ .build();
@Mock
private NotificationListener mListenerService;
@@ -130,6 +188,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
private ContentResolver mMockContentResolver;
@Mock
private Context mMockContext;
+ @Mock
+ private NotificationEntryManager mNotificationEntryManager;
@Before
public void setUp() throws RemoteException {
@@ -152,15 +212,17 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
isNull())).thenReturn(mMockCursor);
when(mMockContext.getString(R.string.birthday_status)).thenReturn(
mContext.getString(R.string.birthday_status));
+ when(mNotificationEntryManager.getVisibleNotifications())
+ .thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3));
}
@Test
public void testGetTilesReturnsSortedListWithMultipleRecentConversations() throws Exception {
// Ensure the less-recent Important conversation is before more recent conversations.
ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID, false, 3);
+ SHORTCUT_ID_1, false, 3);
ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID + 1,
+ SHORTCUT_ID_1 + 1,
true, 1);
when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
new ParceledListSlice(Arrays.asList(
@@ -169,9 +231,9 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
// Ensure the non-Important conversation is sorted between these recent conversations.
ConversationChannel recentConversationBeforeNonImportantConversation =
getConversationChannel(
- SHORTCUT_ID + 2, 4);
+ SHORTCUT_ID_1 + 2, 4);
ConversationChannel recentConversationAfterNonImportantConversation =
- getConversationChannel(SHORTCUT_ID + 3,
+ getConversationChannel(SHORTCUT_ID_1 + 3,
2);
when(mPeopleManager.getRecentConversations()).thenReturn(
new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
@@ -179,7 +241,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
List<String> orderedShortcutIds = PeopleSpaceUtils.getTiles(
mContext, mNotificationManager, mPeopleManager,
- mLauncherApps).stream().map(tile -> tile.getId()).collect(Collectors.toList());
+ mLauncherApps, mNotificationEntryManager)
+ .stream().map(tile -> tile.getId()).collect(Collectors.toList());
assertThat(orderedShortcutIds).containsExactly(
// Even though the oldest conversation, should be first since "important"
@@ -196,11 +259,11 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
throws Exception {
// Ensure the less-recent Important conversation is before more recent conversations.
ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID, false, 3);
+ SHORTCUT_ID_1, false, 3);
ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID + 1, true, 3);
+ SHORTCUT_ID_1 + 1, true, 3);
ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID + 2,
+ SHORTCUT_ID_1 + 2,
true, 1);
when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
new ParceledListSlice(Arrays.asList(
@@ -210,9 +273,9 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
// Ensure the non-Important conversation is sorted between these recent conversations.
ConversationChannel recentConversationBeforeNonImportantConversation =
getConversationChannel(
- SHORTCUT_ID + 3, 4);
+ SHORTCUT_ID_1 + 3, 4);
ConversationChannel recentConversationAfterNonImportantConversation =
- getConversationChannel(SHORTCUT_ID + 4,
+ getConversationChannel(SHORTCUT_ID_1 + 4,
2);
when(mPeopleManager.getRecentConversations()).thenReturn(
new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
@@ -220,7 +283,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
List<String> orderedShortcutIds = PeopleSpaceUtils.getTiles(
mContext, mNotificationManager, mPeopleManager,
- mLauncherApps).stream().map(tile -> tile.getId()).collect(Collectors.toList());
+ mLauncherApps, mNotificationEntryManager)
+ .stream().map(tile -> tile.getId()).collect(Collectors.toList());
assertThat(orderedShortcutIds).containsExactly(
// Important conversations should be sorted at the beginning.
@@ -238,7 +302,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
Notification notification = new Notification.Builder(mContext, "test")
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
- .setShortcutId(SHORTCUT_ID)
+ .setShortcutId(SHORTCUT_ID_1)
.build();
StatusBarNotification sbn = new SbnBuilder()
.setNotification(notification)
@@ -341,24 +405,126 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
@Test
public void testGetLastMessagingStyleMessage() {
- Notification notification = new Notification.Builder(mContext, "test")
- .setContentTitle("TEST_TITLE")
- .setContentText("TEST_TEXT")
- .setShortcutId(SHORTCUT_ID)
- .setStyle(new Notification.MessagingStyle(PERSON)
- .addMessage(new Notification.MessagingStyle.Message("text1", 0, PERSON))
- .addMessage(new Notification.MessagingStyle.Message("text2", 20, PERSON))
- .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
- )
- .build();
StatusBarNotification sbn = new SbnBuilder()
- .setNotification(notification)
+ .setNotification(mNotification1)
.build();
Notification.MessagingStyle.Message lastMessage =
PeopleSpaceUtils.getLastMessagingStyleMessage(sbn);
- assertThat(lastMessage.getText()).isEqualTo("text2");
+ assertThat(lastMessage.getText().toString()).isEqualTo(NOTIFICATION_TEXT_2);
+ }
+
+ @Test
+ public void testAugmentTileFromNotification() {
+ StatusBarNotification sbn = new SbnBuilder()
+ .setNotification(mNotification1)
+ .build();
+
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ PeopleSpaceTile actual = PeopleSpaceUtils
+ .augmentTileFromNotification(tile, sbn);
+
+ assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
+ }
+
+ @Test
+ public void testAugmentTileFromNotificationNoContent() {
+ StatusBarNotification sbn = new SbnBuilder()
+ .setNotification(mNotification3)
+ .build();
+
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_3, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ PeopleSpaceTile actual = PeopleSpaceUtils
+ .augmentTileFromNotification(tile, sbn);
+
+ assertThat(actual.getNotificationKey()).isEqualTo(null);
+ assertThat(actual.getNotificationContent()).isEqualTo(null);
+ }
+
+ @Test
+ public void testAugmentTileFromVisibleNotifications() {
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ PeopleSpaceTile actual = PeopleSpaceUtils
+ .augmentTileFromVisibleNotifications(tile,
+ Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
+
+ assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
+ }
+
+ @Test
+ public void testAugmentTileFromVisibleNotificationsDifferentShortcutId() {
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_4, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ PeopleSpaceTile actual = PeopleSpaceUtils
+ .augmentTileFromVisibleNotifications(tile,
+ Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
+
+ assertThat(actual.getNotificationContent()).isEqualTo(null);
+ }
+
+ @Test
+ public void testAugmentTilesFromVisibleNotificationsSingleTile() {
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ List<PeopleSpaceTile> actualList = PeopleSpaceUtils
+ .augmentTilesFromVisibleNotifications(List.of(tile), mNotificationEntryManager);
+
+ assertThat(actualList.size()).isEqualTo(1);
+ assertThat(actualList.get(0).getNotificationContent().toString())
+ .isEqualTo(NOTIFICATION_TEXT_2);
+
+ verify(mNotificationEntryManager, times(1)).getVisibleNotifications();
+ }
+
+ @Test
+ public void testAugmentTilesFromVisibleNotificationsMultipleTiles() {
+ PeopleSpaceTile tile1 =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(1)
+ .build();
+ PeopleSpaceTile tile2 =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_2, "userName2", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ List<PeopleSpaceTile> actualList = PeopleSpaceUtils
+ .augmentTilesFromVisibleNotifications(List.of(tile1, tile2),
+ mNotificationEntryManager);
+
+ assertThat(actualList.size()).isEqualTo(2);
+ assertThat(actualList.get(0).getNotificationContent().toString())
+ .isEqualTo(NOTIFICATION_TEXT_2);
+ assertThat(actualList.get(1).getNotificationContent().toString())
+ .isEqualTo(NOTIFICATION_TEXT_4);
+
+ verify(mNotificationEntryManager, times(1)).getVisibleNotifications();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index ef314ad16556..9470141178dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -421,6 +421,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
@@ -429,15 +430,21 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
.build();
StatusBarNotification sbn = new SbnBuilder()
.setNotification(notificationWithoutMessagingStyle)
+ .setPkg(TEST_PACKAGE_A)
+ .setUid(0)
.build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mAppWidgetManager, never())
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
- verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle options = requireNonNull(mBundleArgumentCaptor.getValue());
+ assertThat((PeopleSpaceTile) options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE))
+ .isEqualTo(PERSON_TILE);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index 25f4abe2cb14..903a07140eae 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -19,12 +19,12 @@ package com.android.server.accessibility.gestures;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
-import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
@@ -147,15 +147,15 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this));
mMultiFingerGestures.add(
- new MultiFingerMultiTapAndHold(
- mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD, this));
- mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
new MultiFingerMultiTapAndHold(
mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, this));
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP, this));
+ mMultiFingerGestures.add(
+ new MultiFingerMultiTapAndHold(
+ mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD, this));
// Three-finger taps.
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 3, 1, GESTURE_3_FINGER_SINGLE_TAP, this));
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 67f654e6360f..6d72ca7bae25 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1492,7 +1492,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
final Dataset dataset = (Dataset) result;
final Dataset oldDataset = authenticatedResponse.getDatasets().get(datasetIdx);
- if (!isPinnedDataset(oldDataset)) {
+ if (!isAuthResultDatasetEphemeral(oldDataset, data)) {
authenticatedResponse.getDatasets().set(datasetIdx, dataset);
}
autoFill(requestId, datasetIdx, dataset, false);
@@ -1513,6 +1513,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
+ * Returns whether the dataset returned from the authentication result is ephemeral or not.
+ * See {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more
+ * information.
+ */
+ private static boolean isAuthResultDatasetEphemeral(@Nullable Dataset oldDataset,
+ @NonNull Bundle authResultData) {
+ if (authResultData.containsKey(
+ AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
+ return authResultData.getBoolean(
+ AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET);
+ }
+ return isPinnedDataset(oldDataset);
+ }
+
+ /**
* A dataset can potentially have multiple fields, and it's possible that some of the fields'
* has inline presentation and some don't. It's also possible that some of the fields'
* inline presentation is pinned and some isn't. So the concept of whether a dataset is
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 96b69dcc45d6..10b00d38fb42 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -161,6 +161,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private static final boolean DEBUG = false;
private static final String LOG_TAG = "CompanionDeviceManagerService";
+ private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min
+
private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
@@ -170,6 +172,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private static final String XML_ATTR_DEVICE = "device";
private static final String XML_ATTR_PROFILE = "profile";
private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
+ private static final String XML_ATTR_TIME_APPROVED = "time_approved";
private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
private final CompanionDeviceManagerImpl mImpl;
@@ -606,7 +609,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
association.getDeviceMacAddress(),
association.getPackageName(),
association.getDeviceProfile(),
- active /* notifyOnDeviceNearby */);
+ active, /* notifyOnDeviceNearby */
+ association.getTimeApprovedMs());
} else {
return association;
}
@@ -639,6 +643,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
@Override
+ public boolean canPairWithoutPrompt(
+ String packageName, String deviceMacAddress, int userId) {
+ return CollectionUtils.any(
+ getAllAssociations(userId, packageName, deviceMacAddress),
+ a -> System.currentTimeMillis() - a.getTimeApprovedMs()
+ < PAIR_WITHOUT_PROMPT_WINDOW_MS);
+ }
+
+ @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver)
throws RemoteException {
@@ -877,6 +890,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
Boolean.toString(
association.isNotifyOnDeviceNearby()));
}
+ tag.attribute(null, XML_ATTR_TIME_APPROVED,
+ Long.toString(association.getTimeApprovedMs()));
tag.endTag(null, XML_TAG_ASSOCIATION);
});
@@ -921,7 +936,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
}
- @Nullable
private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
return CollectionUtils.filter(
getAllAssociations(userId),
@@ -941,6 +955,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
}
+ private Set<Association> getAllAssociations(
+ int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
+ return CollectionUtils.filter(
+ getAllAssociations(userId),
+ a -> Objects.equals(packageFilter, a.getPackageName())
+ && Objects.equals(addressFilter, a.getDeviceMacAddress()));
+ }
+
private Set<Association> readAllAssociations(int userId) {
final AtomicFile file = getStorageFileForUser(userId);
@@ -962,12 +984,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE);
final boolean persistentGrants = Boolean.valueOf(
parser.getAttributeValue(null, XML_ATTR_NOTIFY_DEVICE_NEARBY));
+ final long timeApproved = parseLongOrDefault(
+ parser.getAttributeValue(null, XML_ATTR_TIME_APPROVED), 0L);
if (appPackage == null || deviceAddress == null) continue;
result = ArrayUtils.add(result,
new Association(userId, deviceAddress, appPackage,
- profile, persistentGrants));
+ profile, persistentGrants, timeApproved));
}
return result;
} catch (XmlPullParserException | IOException e) {
@@ -1293,6 +1317,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
return result;
}
+ private static long parseLongOrDefault(String str, long def) {
+ try {
+ return Long.parseLong(str);
+ } catch (NumberFormatException e) {
+ Log.w(LOG_TAG, "Failed to parse", e);
+ return def;
+ }
+ }
+
private class ShellCmd extends ShellCommand {
public static final String USAGE = "help\n"
+ "list USER_ID\n"
@@ -1321,7 +1354,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
int userId = getNextArgInt();
String pkg = getNextArgRequired();
String address = getNextArgRequired();
- addAssociation(new Association(userId, address, pkg, null, false));
+ addAssociation(new Association(userId, address, pkg, null, false,
+ System.currentTimeMillis()));
}
break;
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 6886cdefc28a..737a9e4ff46c 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -30,6 +30,7 @@ import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.pm.PackageManager.ComponentInfoFlags;
import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.Bundle;
import android.os.Handler;
@@ -49,8 +50,8 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Collection;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -535,17 +536,17 @@ public abstract class PackageManagerInternal {
* Set which overlay to use for a package.
* @param userId The user for which to update the overlays.
* @param targetPackageName The package name of the package for which to update the overlays.
- * @param overlayPackageNames The complete list of overlay packages that should be enabled for
- * the target. Previously enabled overlays not specified in the list
- * will be disabled. Pass in null or an empty list to disable
- * all overlays. The order of the items is significant if several
- * overlays modify the same resource.
+ * @param overlayPaths The complete list of overlay paths that should be enabled for
+ * the target. Previously enabled overlays not specified in the list
+ * will be disabled. Pass in null or empty paths to disable all overlays.
+ * The order of the items is significant if several overlays modify the
+ * same resource.
* @param outUpdatedPackageNames An output list that contains the package names of packages
* affected by the update of enabled overlays.
* @return true if all packages names were known by the package manager, false otherwise
*/
public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName,
- List<String> overlayPackageNames, Collection<String> outUpdatedPackageNames);
+ @Nullable OverlayPaths overlayPaths, Set<String> outUpdatedPackageNames);
/**
* Resolves an activity intent, allowing instant apps to be resolved.
diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index c56cef2d58dc..a83c981235df 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -16,12 +16,6 @@
package com.android.server;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -33,10 +27,15 @@ import android.os.Message;
import android.os.SystemProperties;
import android.util.Slog;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+
/**
* A service designed to load and periodically save &quot;randomness&quot;
- * for the Linux kernel RNG and to mix in data from Hardware RNG (if present)
- * into the Linux RNG.
+ * for the Linux kernel RNG.
*
* <p>When a Linux system starts up, the entropy pool associated with
* {@code /dev/random} may be in a fairly predictable state. Applications which
@@ -45,15 +44,8 @@ import android.util.Slog;
* this effect, it's helpful to carry the entropy pool information across
* shutdowns and startups.
*
- * <p>On systems with Hardware RNG (/dev/hw_random), a block of output from HW
- * RNG is mixed into the Linux RNG on EntropyMixer's startup and whenever
- * EntropyMixer periodically runs to save a block of output from Linux RNG on
- * disk. This mixing is done in a way that does not increase the Linux RNG's
- * entropy estimate is not increased. This is to avoid having to trust/verify
- * the quality and authenticity of the &quot;randomness&quot; of the HW RNG.
- *
* <p>This class was modeled after the script in the
- * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">
+ * <a href="https://man7.org/linux/man-pages/man4/random.4.html">
* random(4) manual page</a>.
*/
public class EntropyMixer extends Binder {
@@ -64,7 +56,6 @@ public class EntropyMixer extends Binder {
private static final long START_NANOTIME = System.nanoTime();
private final String randomDevice;
- private final String hwRandomDevice;
private final String entropyFile;
/**
@@ -80,7 +71,6 @@ public class EntropyMixer extends Binder {
Slog.e(TAG, "Will not process invalid message");
return;
}
- addHwRandomEntropy();
writeEntropy();
scheduleEntropyWriter();
}
@@ -94,25 +84,21 @@ public class EntropyMixer extends Binder {
};
public EntropyMixer(Context context) {
- this(context, getSystemDir() + "/entropy.dat", "/dev/urandom", "/dev/hw_random");
+ this(context, getSystemDir() + "/entropy.dat", "/dev/urandom");
}
/** Test only interface, not for public use */
public EntropyMixer(
Context context,
String entropyFile,
- String randomDevice,
- String hwRandomDevice) {
+ String randomDevice) {
if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
- if (hwRandomDevice == null) { throw new NullPointerException("hwRandomDevice"); }
if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
this.randomDevice = randomDevice;
- this.hwRandomDevice = hwRandomDevice;
this.entropyFile = entropyFile;
loadInitialEntropy();
addDeviceSpecificEntropy();
- addHwRandomEntropy();
writeEntropy();
scheduleEntropyWriter();
IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
@@ -192,23 +178,6 @@ public class EntropyMixer extends Binder {
}
}
- /**
- * Mixes in the output from HW RNG (if present) into the Linux RNG.
- */
- private void addHwRandomEntropy() {
- if (!new File(hwRandomDevice).exists()) {
- // HW RNG not present/exposed -- ignore
- return;
- }
-
- try {
- RandomBlock.fromFile(hwRandomDevice).toFile(randomDevice, false);
- Slog.i(TAG, "Added HW RNG output to entropy pool");
- } catch (IOException e) {
- Slog.w(TAG, "Failed to add HW RNG output to entropy pool", e);
- }
- }
-
private static String getSystemDir() {
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c18031fd6de6..0afdbdecd610 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1149,7 +1149,7 @@ public final class CachedAppOptimizer {
}
return;
}
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG_AM, "Not freezing. Unable to check file locks for " + name + "(" + pid
+ "): " + e);
return;
@@ -1244,7 +1244,7 @@ public final class CachedAppOptimizer {
}
}
}
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
synchronized (mAm) {
synchronized (mProcLock) {
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 3acad49496c5..76c34678d589 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -16,17 +16,23 @@
package com.android.server.app;
+import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
import android.app.GameManager;
import android.app.GameManager.GameMode;
import android.app.IGameManagerService;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -81,6 +87,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
switch (msg.what) {
case WRITE_SETTINGS: {
final int userId = (int) msg.obj;
+ if (userId < 0) {
+ Slog.wtf(TAG, "Attempt to write settings for invalid user: " + userId);
+ synchronized (mLock) {
+ removeMessages(WRITE_SETTINGS, msg.obj);
+ }
+ break;
+ }
+
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_SETTINGS, msg.obj);
@@ -94,6 +108,15 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
case REMOVE_SETTINGS: {
final int userId = (int) msg.obj;
+ if (userId < 0) {
+ Slog.wtf(TAG, "Attempt to write settings for invalid user: " + userId);
+ synchronized (mLock) {
+ removeMessages(WRITE_SETTINGS, msg.obj);
+ removeMessages(REMOVE_SETTINGS, msg.obj);
+ }
+ break;
+ }
+
synchronized (mLock) {
// Since the user was removed, ignore previous write message
// and do write here.
@@ -146,9 +169,23 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
- //TODO(b/178111358) Add proper permission check and multi-user handling
+ private boolean hasPermission(String permission) {
+ return mContext.checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
@Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public @GameMode int getGameMode(String packageName, int userId) {
+ if (!hasPermission(Manifest.permission.MANAGE_GAME_MODE)) {
+ Log.w(TAG, String.format("Caller or self does not have permission.MANAGE_GAME_MODE"));
+ return GameManager.GAME_MODE_UNSUPPORTED;
+ }
+
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "getGameMode",
+ "com.android.server.app.GameManagerService");
+
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
return GameManager.GAME_MODE_UNSUPPORTED;
@@ -158,9 +195,18 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
- //TODO(b/178111358) Add proper permission check and multi-user handling
@Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public void setGameMode(String packageName, @GameMode int gameMode, int userId) {
+ if (!hasPermission(Manifest.permission.MANAGE_GAME_MODE)) {
+ Log.w(TAG, String.format("Caller or self does not have permission.MANAGE_GAME_MODE"));
+ return;
+ }
+
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "setGameMode",
+ "com.android.server.app.GameManagerService");
+
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
return;
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index ce24e5efdc07..de571863dbd4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -69,6 +69,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates();
+ Slog.d(TAG, "Enumerate onClientFinished: " + clientMonitor + ", success: " + success);
+
if (!unknownHALTemplates.isEmpty()) {
Slog.w(TAG, "Adding " + unknownHALTemplates.size() + " templates for deletion");
}
@@ -90,6 +92,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
private final Callback mRemoveCallback = new Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
+ Slog.d(TAG, "Remove onClientFinished: " + clientMonitor + ", success: " + success);
mCallback.onClientFinished(InternalCleanupClient.this, success);
}
};
@@ -115,6 +118,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
}
private void startCleanupUnknownHalTemplates() {
+ Slog.d(TAG, "startCleanupUnknownHalTemplates, size: " + mUnknownHALTemplates.size());
+
UserTemplate template = mUnknownHALTemplates.get(0);
mUnknownHALTemplates.remove(template);
mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
@@ -138,6 +143,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
// Start enumeration. Removal will start if necessary, when enumeration is completed.
mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(),
getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId());
+
+ Slog.d(TAG, "Starting enumerate: " + mCurrentTask);
mCurrentTask.start(mEnumerateCallback);
}
@@ -165,6 +172,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
+ mCurrentTask.getClass().getSimpleName());
return;
}
+ Slog.d(TAG, "onEnumerated, remaining: " + remaining);
((EnumerateConsumer) mCurrentTask).onEnumerationResult(identifier, remaining);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 9d19fdf4868d..e3feb74248ec 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -82,6 +82,7 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
if (identifier == null) {
+ Slog.d(TAG, "Null identifier");
return;
}
Slog.v(TAG, "handleEnumeratedTemplate: " + identifier.getBiometricId());
@@ -103,6 +104,7 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
private void doTemplateCleanup() {
if (mEnrolledList == null) {
+ Slog.d(TAG, "Null enrolledList");
return;
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index ea759bf500dd..28dc5167487d 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -79,6 +79,7 @@ import android.os.UserHandle;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.stats.location.LocationStatsEnums;
+import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -87,6 +88,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.geofence.GeofenceManager;
import com.android.server.location.geofence.GeofenceProxy;
import com.android.server.location.gnss.GnssConfiguration;
@@ -98,7 +100,6 @@ import com.android.server.location.injector.AppOpsHelper;
import com.android.server.location.injector.EmergencyHelper;
import com.android.server.location.injector.Injector;
import com.android.server.location.injector.LocationAttributionHelper;
-import com.android.server.location.injector.LocationEventLog;
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationPowerSaveModeHelper;
import com.android.server.location.injector.LocationUsageLogger;
@@ -147,9 +148,10 @@ public class LocationManagerService extends ILocationManager.Stub {
public Lifecycle(Context context) {
super(context);
+ LocationEventLog eventLog = new LocationEventLog();
mUserInfoHelper = new LifecycleUserInfoHelper(context);
- mSystemInjector = new SystemInjector(context, mUserInfoHelper);
- mService = new LocationManagerService(context, mSystemInjector);
+ mSystemInjector = new SystemInjector(context, mUserInfoHelper, eventLog);
+ mService = new LocationManagerService(context, mSystemInjector, eventLog);
}
@Override
@@ -159,7 +161,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// client caching behavior is only enabled after seeing the first invalidate
LocationManager.invalidateLocalLocationEnabledCaches();
// disable caching for our own process
- Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class))
+ Objects.requireNonNull(getContext().getSystemService(LocationManager.class))
.disableLocalLocationEnabledCaches();
}
@@ -221,6 +223,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private final Context mContext;
private final Injector mInjector;
+ private final LocationEventLog mEventLog;
private final LocalService mLocalService;
private final GeofenceManager mGeofenceManager;
@@ -245,10 +248,10 @@ public class LocationManagerService extends ILocationManager.Stub {
private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
new CopyOnWriteArrayList<>();
- LocationManagerService(Context context, Injector injector) {
+ LocationManagerService(Context context, Injector injector, LocationEventLog eventLog) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mInjector = injector;
-
+ mEventLog = eventLog;
mLocalService = new LocalService();
LocalServices.addService(LocationManagerInternal.class, mLocalService);
@@ -256,7 +259,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// set up passive provider first since it will be required for all other location providers,
// which are loaded later once the system is ready.
- mPassiveManager = new PassiveLocationProviderManager(mContext, injector);
+ mPassiveManager = new PassiveLocationProviderManager(mContext, injector, mEventLog);
addLocationProviderManager(mPassiveManager, new PassiveLocationProvider(mContext));
// TODO: load the gps provider here as well, which will require refactoring
@@ -297,7 +300,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
LocationProviderManager manager = new LocationProviderManager(mContext, mInjector,
- providerName, mPassiveManager);
+ mEventLog, providerName, mPassiveManager);
addLocationProviderManager(manager, null);
return manager;
}
@@ -341,7 +344,7 @@ public class LocationManagerService extends ILocationManager.Stub {
com.android.internal.R.string.config_networkLocationProviderPackageName);
if (networkProvider != null) {
LocationProviderManager networkManager = new LocationProviderManager(mContext,
- mInjector, NETWORK_PROVIDER, mPassiveManager);
+ mInjector, mEventLog, NETWORK_PROVIDER, mPassiveManager);
addLocationProviderManager(networkManager, networkProvider);
} else {
Log.w(TAG, "no network location provider found");
@@ -360,7 +363,7 @@ public class LocationManagerService extends ILocationManager.Stub {
com.android.internal.R.string.config_fusedLocationProviderPackageName);
if (fusedProvider != null) {
LocationProviderManager fusedManager = new LocationProviderManager(mContext, mInjector,
- FUSED_PROVIDER, mPassiveManager);
+ mEventLog, FUSED_PROVIDER, mPassiveManager);
addLocationProviderManager(fusedManager, fusedProvider);
} else {
Log.wtf(TAG, "no fused location provider found");
@@ -375,7 +378,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mGnssManagerService.onSystemReady();
LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector,
- GPS_PROVIDER, mPassiveManager);
+ mEventLog, GPS_PROVIDER, mPassiveManager);
addLocationProviderManager(gnssManager, mGnssManagerService.getGnssLocationProvider());
}
@@ -431,7 +434,7 @@ public class LocationManagerService extends ILocationManager.Stub {
Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
}
- mInjector.getLocationEventLog().logLocationEnabled(userId, enabled);
+ mEventLog.logLocationEnabled(userId, enabled);
Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
@@ -1193,9 +1196,27 @@ public class LocationManagerService extends ILocationManager.Stub {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- if (mGnssManagerService != null && args.length > 0 && args[0].equals("--gnssmetrics")) {
- mGnssManagerService.dump(fd, ipw, args);
- return;
+ if (args.length > 0) {
+ LocationProviderManager manager = getLocationProviderManager(args[0]);
+ if (manager != null) {
+ ipw.println("Provider:");
+ ipw.increaseIndent();
+ manager.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+
+ ipw.println("Event Log:");
+ ipw.increaseIndent();
+ mEventLog.iterate(manager.getName(), ipw::println);
+ ipw.decreaseIndent();
+ return;
+ }
+
+ if ("--gnssmetrics".equals(args[0])) {
+ if (mGnssManagerService != null) {
+ mGnssManagerService.dump(fd, ipw, args);
+ }
+ return;
+ }
}
ipw.println("Location Manager State:");
@@ -1227,6 +1248,25 @@ public class LocationManagerService extends ILocationManager.Stub {
}
ipw.decreaseIndent();
+ ipw.println("Historical Aggregate Location Provider Data:");
+ ipw.increaseIndent();
+ ArrayMap<String, ArrayMap<String, LocationEventLog.AggregateStats>> aggregateStats =
+ mEventLog.copyAggregateStats();
+ for (int i = 0; i < aggregateStats.size(); i++) {
+ ipw.println(aggregateStats.keyAt(i));
+ ipw.increaseIndent();
+ ArrayMap<String, LocationEventLog.AggregateStats> providerStats =
+ aggregateStats.valueAt(i);
+ for (int j = 0; j < providerStats.size(); j++) {
+ ipw.print(providerStats.keyAt(j));
+ ipw.print(": ");
+ providerStats.valueAt(j).updateTotals();
+ ipw.println(providerStats.valueAt(j));
+ }
+ ipw.decreaseIndent();
+ }
+ ipw.decreaseIndent();
+
if (mGnssManagerService != null) {
ipw.println("GNSS Manager:");
ipw.increaseIndent();
@@ -1241,7 +1281,7 @@ public class LocationManagerService extends ILocationManager.Stub {
ipw.println("Event Log:");
ipw.increaseIndent();
- mInjector.getLocationEventLog().iterate(ipw::println);
+ mEventLog.iterate(ipw::println);
ipw.decreaseIndent();
}
@@ -1320,7 +1360,6 @@ public class LocationManagerService extends ILocationManager.Stub {
private final Context mContext;
private final UserInfoHelper mUserInfoHelper;
- private final LocationEventLog mLocationEventLog;
private final AlarmHelper mAlarmHelper;
private final SystemAppOpsHelper mAppOpsHelper;
private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
@@ -1339,19 +1378,17 @@ public class LocationManagerService extends ILocationManager.Stub {
@GuardedBy("this")
private boolean mSystemReady;
- SystemInjector(Context context, UserInfoHelper userInfoHelper) {
+ SystemInjector(Context context, UserInfoHelper userInfoHelper, LocationEventLog eventLog) {
mContext = context;
mUserInfoHelper = userInfoHelper;
- mLocationEventLog = new LocationEventLog();
mAlarmHelper = new SystemAlarmHelper(context);
mAppOpsHelper = new SystemAppOpsHelper(context);
mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
mAppOpsHelper);
mSettingsHelper = new SystemSettingsHelper(context);
mAppForegroundHelper = new SystemAppForegroundHelper(context);
- mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context,
- mLocationEventLog);
+ mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context, eventLog);
mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
mLocationUsageLogger = new LocationUsageLogger();
@@ -1430,11 +1467,6 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public LocationEventLog getLocationEventLog() {
- return mLocationEventLog;
- }
-
- @Override
public LocationUsageLogger getLocationUsageLogger() {
return mLocationUsageLogger;
}
diff --git a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index b5746bbf310a..12dd3e6a4a4c 100644
--- a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
@@ -16,12 +16,13 @@
package com.android.server.location.eventlog;
+import android.annotation.Nullable;
import android.os.SystemClock;
import android.util.TimeUtils;
import com.android.internal.util.Preconditions;
-import java.util.ListIterator;
+import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
@@ -35,6 +36,7 @@ public abstract class LocalEventLog {
boolean isFiller();
long getTimeDeltaMs();
String getLogString();
+ boolean filter(@Nullable String filter);
}
private static final class FillerEvent implements Log {
@@ -62,6 +64,11 @@ public abstract class LocalEventLog {
public String getLogString() {
throw new AssertionError();
}
+
+ @Override
+ public boolean filter(String filter) {
+ return false;
+ }
}
/**
@@ -87,6 +94,11 @@ public abstract class LocalEventLog {
public final long getTimeDeltaMs() {
return Integer.toUnsignedLong(mTimeDelta);
}
+
+ @Override
+ public boolean filter(String filter) {
+ return false;
+ }
}
// circular buffer of log entries
@@ -198,6 +210,17 @@ public abstract class LocalEventLog {
}
}
+ /**
+ * Iterates over the event log, passing each filter-matching log string to the given
+ * consumer.
+ */
+ public synchronized void iterate(String filter, Consumer<String> consumer) {
+ LogIterator it = new LogIterator(filter);
+ while (it.hasNext()) {
+ consumer.accept(it.next());
+ }
+ }
+
// returns the index of the first element
private int startIndex() {
return wrapIndex(mLogEndIndex - mLogSize);
@@ -205,12 +228,13 @@ public abstract class LocalEventLog {
// returns the index after this one
private int incrementIndex(int index) {
- return wrapIndex(index + 1);
- }
-
- // returns the index before this one
- private int decrementIndex(int index) {
- return wrapIndex(index - 1);
+ if (index == -1) {
+ return startIndex();
+ } else if (index >= 0) {
+ return wrapIndex(index + 1);
+ } else {
+ throw new IllegalArgumentException();
+ }
}
// rolls over the given index if necessary
@@ -219,7 +243,9 @@ public abstract class LocalEventLog {
return (index % mLog.length + mLog.length) % mLog.length;
}
- private class LogIterator implements ListIterator<String> {
+ private class LogIterator implements Iterator<String> {
+
+ private final @Nullable String mFilter;
private final long mSystemTimeDeltaMs;
@@ -228,10 +254,17 @@ public abstract class LocalEventLog {
private int mCount;
LogIterator() {
+ this(null);
+ }
+
+ LogIterator(@Nullable String filter) {
+ mFilter = filter;
mSystemTimeDeltaMs = System.currentTimeMillis() - SystemClock.elapsedRealtime();
mCurrentRealtimeMs = mStartRealtimeMs;
- mIndex = startIndex();
- mCount = 0;
+ mIndex = -1;
+ mCount = -1;
+
+ increment();
}
@Override
@@ -239,75 +272,17 @@ public abstract class LocalEventLog {
return mCount < mLogSize;
}
- @Override
- public boolean hasPrevious() {
- return mCount > 0;
- }
-
- @Override
- // return then increment
public String next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Log log = mLog[mIndex];
- long nextDeltaMs = log.getTimeDeltaMs();
- long realtimeMs = mCurrentRealtimeMs + nextDeltaMs;
-
- // calculate next index, skipping filler events
- do {
- mCurrentRealtimeMs += nextDeltaMs;
- mIndex = incrementIndex(mIndex);
- if (++mCount < mLogSize) {
- nextDeltaMs = mLog[mIndex].getTimeDeltaMs();
- }
- } while (mCount < mLogSize && mLog[mIndex].isFiller());
-
- return getTimePrefix(realtimeMs + mSystemTimeDeltaMs) + log.getLogString();
- }
-
- @Override
- // decrement then return
- public String previous() {
- Log log;
- long currentDeltaMs;
- long realtimeMs;
-
- // calculate previous index, skipping filler events with MAX_TIME_DELTA
- do {
- if (!hasPrevious()) {
- throw new NoSuchElementException();
- }
-
- mIndex = decrementIndex(mIndex);
- mCount--;
-
- log = mLog[mIndex];
- realtimeMs = mCurrentRealtimeMs;
-
- if (mCount > 0) {
- currentDeltaMs = log.getTimeDeltaMs();
- mCurrentRealtimeMs -= currentDeltaMs;
- }
- } while (mCount >= 0 && log.isFiller());
-
- return getTimePrefix(realtimeMs + mSystemTimeDeltaMs) + log.getLogString();
- }
-
- @Override
- public int nextIndex() {
- throw new UnsupportedOperationException();
- }
+ long timeMs = mCurrentRealtimeMs + log.getTimeDeltaMs() + mSystemTimeDeltaMs;
- @Override
- public int previousIndex() {
- throw new UnsupportedOperationException();
- }
+ increment();
- @Override
- public void add(String s) {
- throw new UnsupportedOperationException();
+ return getTimePrefix(timeMs) + log.getLogString();
}
@Override
@@ -315,9 +290,16 @@ public abstract class LocalEventLog {
throw new UnsupportedOperationException();
}
- @Override
- public void set(String s) {
- throw new UnsupportedOperationException();
+ private void increment() {
+ long nextDeltaMs = mIndex == -1 ? 0 : mLog[mIndex].getTimeDeltaMs();
+ do {
+ mCurrentRealtimeMs += nextDeltaMs;
+ mIndex = incrementIndex(mIndex);
+ if (++mCount < mLogSize) {
+ nextDeltaMs = mLog[mIndex].getTimeDeltaMs();
+ }
+ } while (mCount < mLogSize && (mLog[mIndex].isFiller() || (mFilter != null
+ && !mLog[mIndex].filter(mFilter))));
}
}
}
diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index 8d73518bced1..67060fc2c082 100644
--- a/services/core/java/com/android/server/location/injector/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,24 +14,32 @@
* limitations under the License.
*/
-package com.android.server.location.injector;
+package com.android.server.location.eventlog;
import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+import static android.util.TimeUtils.formatDuration;
import static com.android.server.location.LocationManagerService.D;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
import android.annotation.Nullable;
import android.location.LocationRequest;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Build;
import android.os.PowerManager.LocationPowerSaveMode;
+import android.os.SystemClock;
+import android.util.ArrayMap;
-import com.android.server.location.eventlog.LocalEventLog;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
/** In memory event log for location events. */
public class LocationEventLog extends LocalEventLog {
@@ -54,8 +62,39 @@ public class LocationEventLog extends LocalEventLog {
private static final int EVENT_PROVIDER_DELIVER_LOCATION = 8;
private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 9;
+ @GuardedBy("mAggregateStats")
+ private final ArrayMap<String, ArrayMap<String, AggregateStats>> mAggregateStats;
+
public LocationEventLog() {
super(getLogSize());
+ mAggregateStats = new ArrayMap<>(4);
+ }
+
+ public ArrayMap<String, ArrayMap<String, AggregateStats>> copyAggregateStats() {
+ synchronized (mAggregateStats) {
+ ArrayMap<String, ArrayMap<String, AggregateStats>> copy = new ArrayMap<>(
+ mAggregateStats);
+ for (int i = 0; i < copy.size(); i++) {
+ copy.setValueAt(i, new ArrayMap<>(copy.valueAt(i)));
+ }
+ return copy;
+ }
+ }
+
+ private AggregateStats getAggregateStats(String provider, String packageName) {
+ synchronized (mAggregateStats) {
+ ArrayMap<String, AggregateStats> packageMap = mAggregateStats.get(provider);
+ if (packageMap == null) {
+ packageMap = new ArrayMap<>(2);
+ mAggregateStats.put(provider, packageMap);
+ }
+ AggregateStats stats = packageMap.get(packageName);
+ if (stats == null) {
+ stats = new AggregateStats();
+ packageMap.put(packageName, stats);
+ }
+ return stats;
+ }
}
/** Logs a location enabled/disabled event. */
@@ -77,12 +116,34 @@ public class LocationEventLog extends LocalEventLog {
public void logProviderClientRegistered(String provider, CallerIdentity identity,
LocationRequest request) {
addLogEvent(EVENT_PROVIDER_REGISTER_CLIENT, provider, identity, request);
+ getAggregateStats(provider, identity.getPackageName())
+ .markRequestAdded(request.getIntervalMillis());
}
/** Logs a client unregistration for a location provider. */
- public void logProviderClientUnregistered(String provider,
- CallerIdentity identity) {
+ public void logProviderClientUnregistered(String provider, CallerIdentity identity) {
addLogEvent(EVENT_PROVIDER_UNREGISTER_CLIENT, provider, identity);
+ getAggregateStats(provider, identity.getPackageName()).markRequestRemoved();
+ }
+
+ /** Logs a client for a location provider entering the active state. */
+ public void logProviderClientActive(String provider, CallerIdentity identity) {
+ getAggregateStats(provider, identity.getPackageName()).markRequestActive();
+ }
+
+ /** Logs a client for a location provider leaving the active state. */
+ public void logProviderClientInactive(String provider, CallerIdentity identity) {
+ getAggregateStats(provider, identity.getPackageName()).markRequestInactive();
+ }
+
+ /** Logs a client for a location provider entering the foreground state. */
+ public void logProviderClientForeground(String provider, CallerIdentity identity) {
+ getAggregateStats(provider, identity.getPackageName()).markRequestForeground();
+ }
+
+ /** Logs a client for a location provider leaving the foreground state. */
+ public void logProviderClientBackground(String provider, CallerIdentity identity) {
+ getAggregateStats(provider, identity.getPackageName()).markRequestBackground();
}
/** Logs a change to the provider request for a location provider. */
@@ -143,16 +204,29 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderEnabledEvent extends LogEvent {
+ private abstract static class ProviderEvent extends LogEvent {
+
+ protected final String mProvider;
+
+ protected ProviderEvent(long timeDelta, String provider) {
+ super(timeDelta);
+ mProvider = provider;
+ }
+
+ @Override
+ public boolean filter(String filter) {
+ return mProvider.equals(filter);
+ }
+ }
+
+ private static final class ProviderEnabledEvent extends ProviderEvent {
- private final String mProvider;
private final int mUserId;
private final boolean mEnabled;
protected ProviderEnabledEvent(long timeDelta, String provider, int userId,
boolean enabled) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mUserId = userId;
mEnabled = enabled;
}
@@ -164,14 +238,12 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderMockedEvent extends LogEvent {
+ private static final class ProviderMockedEvent extends ProviderEvent {
- private final String mProvider;
private final boolean mMocked;
protected ProviderMockedEvent(long timeDelta, String provider, boolean mocked) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mMocked = mocked;
}
@@ -185,17 +257,15 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderRegisterEvent extends LogEvent {
+ private static final class ProviderRegisterEvent extends ProviderEvent {
- private final String mProvider;
private final boolean mRegistered;
private final CallerIdentity mIdentity;
@Nullable private final LocationRequest mLocationRequest;
private ProviderRegisterEvent(long timeDelta, String provider, boolean registered,
CallerIdentity identity, @Nullable LocationRequest locationRequest) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mRegistered = registered;
mIdentity = identity;
mLocationRequest = locationRequest;
@@ -212,14 +282,12 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderUpdateEvent extends LogEvent {
+ private static final class ProviderUpdateEvent extends ProviderEvent {
- private final String mProvider;
private final ProviderRequest mRequest;
private ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mRequest = request;
}
@@ -229,14 +297,12 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderReceiveLocationEvent extends LogEvent {
+ private static final class ProviderReceiveLocationEvent extends ProviderEvent {
- private final String mProvider;
private final int mNumLocations;
private ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mNumLocations = numLocations;
}
@@ -246,16 +312,14 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderDeliverLocationEvent extends LogEvent {
+ private static final class ProviderDeliverLocationEvent extends ProviderEvent {
- private final String mProvider;
private final int mNumLocations;
@Nullable private final CallerIdentity mIdentity;
private ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations,
@Nullable CallerIdentity identity) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mNumLocations = numLocations;
mIdentity = identity;
}
@@ -267,7 +331,7 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class LocationPowerSaveModeEvent extends LogEvent {
+ private static final class LocationPowerSaveModeEvent extends LogEvent {
@LocationPowerSaveMode
private final int mLocationPowerSaveMode;
@@ -305,7 +369,7 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class LocationEnabledEvent extends LogEvent {
+ private static final class LocationEnabledEvent extends LogEvent {
private final int mUserId;
private final boolean mEnabled;
@@ -321,4 +385,118 @@ public class LocationEventLog extends LocalEventLog {
return "[u" + mUserId + "] location setting " + (mEnabled ? "enabled" : "disabled");
}
}
+
+ /**
+ * Aggregate statistics for a single package under a single provider.
+ */
+ public static final class AggregateStats {
+
+ @GuardedBy("this")
+ private int mAddedRequestCount;
+ @GuardedBy("this")
+ private int mActiveRequestCount;
+ @GuardedBy("this")
+ private int mForegroundRequestCount;
+
+ @GuardedBy("this")
+ private long mFastestIntervalMs = Long.MAX_VALUE;
+ @GuardedBy("this")
+ private long mSlowestIntervalMs = 0;
+
+ @GuardedBy("this")
+ private long mAddedTimeTotalMs;
+ @GuardedBy("this")
+ private long mAddedTimeLastUpdateRealtimeMs;
+
+ @GuardedBy("this")
+ private long mActiveTimeTotalMs;
+ @GuardedBy("this")
+ private long mActiveTimeLastUpdateRealtimeMs;
+
+ @GuardedBy("this")
+ private long mForegroundTimeTotalMs;
+ @GuardedBy("this")
+ private long mForegroundTimeLastUpdateRealtimeMs;
+
+ AggregateStats() {}
+
+ synchronized void markRequestAdded(long intervalMillis) {
+ if (mAddedRequestCount++ == 0) {
+ mAddedTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
+ }
+
+ mFastestIntervalMs = min(intervalMillis, mFastestIntervalMs);
+ mSlowestIntervalMs = max(intervalMillis, mSlowestIntervalMs);
+ }
+
+ synchronized void markRequestRemoved() {
+ updateTotals();
+ --mAddedRequestCount;
+ Preconditions.checkState(mAddedRequestCount >= 0);
+
+ mActiveRequestCount = min(mAddedRequestCount, mActiveRequestCount);
+ mForegroundRequestCount = min(mAddedRequestCount, mForegroundRequestCount);
+ }
+
+ synchronized void markRequestActive() {
+ Preconditions.checkState(mAddedRequestCount > 0);
+ if (mActiveRequestCount++ == 0) {
+ mActiveTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
+ }
+ }
+
+ synchronized void markRequestInactive() {
+ updateTotals();
+ --mActiveRequestCount;
+ Preconditions.checkState(mActiveRequestCount >= 0);
+ }
+
+ synchronized void markRequestForeground() {
+ Preconditions.checkState(mAddedRequestCount > 0);
+ if (mForegroundRequestCount++ == 0) {
+ mForegroundTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
+ }
+ }
+
+ synchronized void markRequestBackground() {
+ updateTotals();
+ --mForegroundRequestCount;
+ Preconditions.checkState(mForegroundRequestCount >= 0);
+ }
+
+ public synchronized void updateTotals() {
+ if (mAddedRequestCount > 0) {
+ long realtimeMs = SystemClock.elapsedRealtime();
+ mAddedTimeTotalMs += realtimeMs - mAddedTimeLastUpdateRealtimeMs;
+ mAddedTimeLastUpdateRealtimeMs = realtimeMs;
+ }
+ if (mActiveRequestCount > 0) {
+ long realtimeMs = SystemClock.elapsedRealtime();
+ mActiveTimeTotalMs += realtimeMs - mActiveTimeLastUpdateRealtimeMs;
+ mActiveTimeLastUpdateRealtimeMs = realtimeMs;
+ }
+ if (mForegroundRequestCount > 0) {
+ long realtimeMs = SystemClock.elapsedRealtime();
+ mForegroundTimeTotalMs += realtimeMs - mForegroundTimeLastUpdateRealtimeMs;
+ mForegroundTimeLastUpdateRealtimeMs = realtimeMs;
+ }
+ }
+
+ @Override
+ public synchronized String toString() {
+ return "min/max interval = " + intervalToString(mFastestIntervalMs) + "/"
+ + intervalToString(mSlowestIntervalMs)
+ + ", total/active/foreground duration = " + formatDuration(mAddedTimeTotalMs)
+ + "/" + formatDuration(mActiveTimeTotalMs) + "/"
+ + formatDuration(mForegroundTimeTotalMs);
+ }
+
+ private static String intervalToString(long intervalMs) {
+ if (intervalMs == LocationRequest.PASSIVE_INTERVAL) {
+ return "passive";
+ } else {
+ return MILLISECONDS.toSeconds(intervalMs) + "s";
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index 03938b2b8ba2..0e157c22a32b 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -56,7 +56,4 @@ public interface Injector {
/** Returns a LocationUsageLogger. */
LocationUsageLogger getLocationUsageLogger();
-
- /** Returns a LocationEventLog. */
- LocationEventLog getLocationEventLog();
}
diff --git a/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
index 532826a02ab0..cc00d5684991 100644
--- a/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
@@ -24,6 +24,8 @@ import static com.android.server.location.LocationManagerService.TAG;
import android.os.PowerManager.LocationPowerSaveMode;
import android.util.Log;
+import com.android.server.location.eventlog.LocationEventLog;
+
import java.util.concurrent.CopyOnWriteArrayList;
/**
diff --git a/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
index 1b74865b268e..c47a64d6d9a7 100644
--- a/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
@@ -25,6 +25,7 @@ import android.os.PowerSaveState;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.location.eventlog.LocationEventLog;
import java.util.Objects;
import java.util.function.Consumer;
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 48a012e57a02..388b5a4dd54e 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -89,6 +89,7 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.location.LocationPermissions;
import com.android.server.location.LocationPermissions.PermissionLevel;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.fudger.LocationFudger;
import com.android.server.location.injector.AlarmHelper;
import com.android.server.location.injector.AppForegroundHelper;
@@ -96,7 +97,6 @@ import com.android.server.location.injector.AppForegroundHelper.AppForegroundLis
import com.android.server.location.injector.AppOpsHelper;
import com.android.server.location.injector.Injector;
import com.android.server.location.injector.LocationAttributionHelper;
-import com.android.server.location.injector.LocationEventLog;
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationPermissionsHelper.LocationPermissionsListener;
import com.android.server.location.injector.LocationPowerSaveModeHelper;
@@ -323,7 +323,7 @@ public class LocationProviderManager extends
+ getRequest());
}
- mLocationEventLog.logProviderClientRegistered(mName, getIdentity(), super.getRequest());
+ mEventLog.logProviderClientRegistered(mName, getIdentity(), super.getRequest());
// initialization order is important as there are ordering dependencies
mPermitted = mLocationPermissionsHelper.hasLocationPermissions(mPermissionLevel,
@@ -333,6 +333,10 @@ public class LocationProviderManager extends
mIsUsingHighPower = isUsingHighPower();
onProviderListenerRegister();
+
+ if (mForeground) {
+ mEventLog.logProviderClientForeground(mName, getIdentity());
+ }
}
@GuardedBy("mLock")
@@ -344,7 +348,7 @@ public class LocationProviderManager extends
onProviderListenerUnregister();
- mLocationEventLog.logProviderClientUnregistered(mName, getIdentity());
+ mEventLog.logProviderClientUnregistered(mName, getIdentity());
if (D) {
Log.d(TAG, mName + " provider removed registration from " + getIdentity());
@@ -369,6 +373,8 @@ public class LocationProviderManager extends
Preconditions.checkState(Thread.holdsLock(mLock));
}
+ mEventLog.logProviderClientActive(mName, getIdentity());
+
if (!getRequest().isHiddenFromAppOps()) {
mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
}
@@ -389,6 +395,8 @@ public class LocationProviderManager extends
}
onProviderListenerInactive();
+
+ mEventLog.logProviderClientInactive(mName, getIdentity());
}
/**
@@ -524,6 +532,12 @@ public class LocationProviderManager extends
mForeground = foreground;
+ if (mForeground) {
+ mEventLog.logProviderClientForeground(mName, getIdentity());
+ } else {
+ mEventLog.logProviderClientBackground(mName, getIdentity());
+ }
+
// note that onProviderLocationRequestChanged() is always called
return onProviderLocationRequestChanged()
|| mLocationPowerSaveModeHelper.getLocationPowerSaveMode()
@@ -855,7 +869,7 @@ public class LocationProviderManager extends
listener.deliverOnLocationChanged(deliverLocationResult,
mUseWakeLock ? mWakeLock::release : null);
- mLocationEventLog.logProviderDeliveredLocations(mName, locationResult.size(),
+ mEventLog.logProviderDeliveredLocations(mName, locationResult.size(),
getIdentity());
}
@@ -1154,7 +1168,7 @@ public class LocationProviderManager extends
// we currently don't hold a wakelock for getCurrentLocation deliveries
listener.deliverOnLocationChanged(deliverLocationResult, null);
- mLocationEventLog.logProviderDeliveredLocations(mName,
+ mEventLog.logProviderDeliveredLocations(mName,
locationResult != null ? locationResult.size() : 0, getIdentity());
}
@@ -1223,6 +1237,7 @@ public class LocationProviderManager extends
private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners;
+ protected final LocationEventLog mEventLog;
protected final LocationManagerInternal mLocationManagerInternal;
protected final SettingsHelper mSettingsHelper;
protected final UserInfoHelper mUserHelper;
@@ -1235,7 +1250,6 @@ public class LocationProviderManager extends
protected final LocationAttributionHelper mLocationAttributionHelper;
protected final LocationUsageLogger mLocationUsageLogger;
protected final LocationFudger mLocationFudger;
- protected final LocationEventLog mLocationEventLog;
private final UserListener mUserChangedListener = this::onUserChanged;
private final UserSettingChangedListener mLocationEnabledChangedListener =
@@ -1273,8 +1287,8 @@ public class LocationProviderManager extends
@GuardedBy("mLock")
private @Nullable OnAlarmListener mDelayedRegister;
- public LocationProviderManager(Context context, Injector injector, String name,
- @Nullable PassiveLocationProviderManager passiveManager) {
+ public LocationProviderManager(Context context, Injector injector, LocationEventLog eventLog,
+ String name, @Nullable PassiveLocationProviderManager passiveManager) {
mContext = context;
mName = Objects.requireNonNull(name);
mPassiveManager = passiveManager;
@@ -1285,6 +1299,7 @@ public class LocationProviderManager extends
mEnabledListeners = new ArrayList<>();
mProviderRequestListeners = new CopyOnWriteArrayList<>();
+ mEventLog = eventLog;
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
mSettingsHelper = injector.getSettingsHelper();
@@ -1297,7 +1312,6 @@ public class LocationProviderManager extends
mScreenInteractiveHelper = injector.getScreenInteractiveHelper();
mLocationAttributionHelper = injector.getLocationAttributionHelper();
mLocationUsageLogger = injector.getLocationUsageLogger();
- mLocationEventLog = injector.getLocationEventLog();
mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
mProvider = new MockableLocationProvider(mLock);
@@ -1437,7 +1451,7 @@ public class LocationProviderManager extends
synchronized (mLock) {
Preconditions.checkState(mState != STATE_STOPPED);
- mLocationEventLog.logProviderMocked(mName, provider != null);
+ mEventLog.logProviderMocked(mName, provider != null);
final long identity = Binder.clearCallingIdentity();
try {
@@ -1925,7 +1939,7 @@ public class LocationProviderManager extends
@GuardedBy("mLock")
private void setProviderRequest(ProviderRequest request) {
- mLocationEventLog.logProviderUpdateRequest(mName, request);
+ mEventLog.logProviderUpdateRequest(mName, request);
mProvider.getController().setRequest(request);
FgThread.getHandler().post(() -> {
@@ -2261,7 +2275,7 @@ public class LocationProviderManager extends
}
// don't log location received for passive provider because it's spammy
- mLocationEventLog.logProviderReceivedLocations(mName, filtered.size());
+ mEventLog.logProviderReceivedLocations(mName, filtered.size());
} else {
// passive provider should get already filtered results as input
filtered = locationResult;
@@ -2361,7 +2375,7 @@ public class LocationProviderManager extends
if (D) {
Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled);
}
- mLocationEventLog.logProviderEnabled(mName, userId, enabled);
+ mEventLog.logProviderEnabled(mName, userId, enabled);
}
// clear last locations if we become disabled
diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
index b35af4f6475c..027f4e94f55b 100644
--- a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
@@ -24,6 +24,7 @@ import android.location.provider.ProviderRequest;
import android.os.Binder;
import com.android.internal.util.Preconditions;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.Injector;
import java.util.Collection;
@@ -33,8 +34,9 @@ import java.util.Collection;
*/
public class PassiveLocationProviderManager extends LocationProviderManager {
- public PassiveLocationProviderManager(Context context, Injector injector) {
- super(context, injector, LocationManager.PASSIVE_PROVIDER, null);
+ public PassiveLocationProviderManager(Context context, Injector injector,
+ LocationEventLog eventLog) {
+ super(context, injector, eventLog, LocationManager.PASSIVE_PROVIDER, null);
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f7f1865f757e..4c3dfbff3f87 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -378,7 +378,9 @@ public class NotificationManagerService extends SystemService {
static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] {
Adjustment.KEY_CONTEXTUAL_ACTIONS,
Adjustment.KEY_TEXT_REPLIES,
- Adjustment.KEY_NOT_CONVERSATION};
+ Adjustment.KEY_NOT_CONVERSATION,
+ Adjustment.KEY_IMPORTANCE,
+ Adjustment.KEY_RANKING_SCORE};
static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] {
RoleManager.ROLE_DIALER,
@@ -9048,7 +9050,8 @@ public class NotificationManagerService extends SystemService {
public class NotificationAssistants extends ManagedServices {
static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
- private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "q_allowed_adjustments";
+ private static final String TAG_ALLOWED_ADJUSTMENT_TYPES_OLD = "q_allowed_adjustments";
+ private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "s_allowed_adjustments";
private static final String ATT_TYPES = "types";
private final Object mLock = new Object();
@@ -9150,13 +9153,19 @@ public class NotificationManagerService extends SystemService {
@Override
protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {
- if (TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) {
+ if (TAG_ALLOWED_ADJUSTMENT_TYPES_OLD.equals(tag)
+ || TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) {
final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES);
synchronized (mLock) {
mAllowedAdjustments.clear();
if (!TextUtils.isEmpty(types)) {
mAllowedAdjustments.addAll(Arrays.asList(types.split(",")));
}
+ if (TAG_ALLOWED_ADJUSTMENT_TYPES_OLD.equals(tag)) {
+ if (DEBUG) Slog.d(TAG, "Migrate allowed adjustments.");
+ mAllowedAdjustments.addAll(
+ Arrays.asList(DEFAULT_ALLOWED_ADJUSTMENTS));
+ }
}
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 50fb176e3a5a..fd2fb1fcab39 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -50,6 +50,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
+import android.content.pm.overlay.OverlayPaths;
import android.content.res.ApkAssets;
import android.net.Uri;
import android.os.Binder;
@@ -1376,18 +1377,18 @@ public final class OverlayManagerService extends SystemService {
targetPackageNames = pm.getTargetPackageNames(userId);
}
- final Map<String, List<String>> pendingChanges =
+ final Map<String, OverlayPaths> pendingChanges =
new ArrayMap<>(targetPackageNames.size());
synchronized (mLock) {
- final List<String> frameworkOverlays =
- mImpl.getEnabledOverlayPackageNames("android", userId);
+ final OverlayPaths frameworkOverlays =
+ mImpl.getEnabledOverlayPaths("android", userId);
for (final String targetPackageName : targetPackageNames) {
- List<String> list = new ArrayList<>();
+ final OverlayPaths.Builder list = new OverlayPaths.Builder();
if (!"android".equals(targetPackageName)) {
list.addAll(frameworkOverlays);
}
- list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
- pendingChanges.put(targetPackageName, list);
+ list.addAll(mImpl.getEnabledOverlayPaths(targetPackageName, userId));
+ pendingChanges.put(targetPackageName, list.build());
}
}
@@ -1395,7 +1396,7 @@ public final class OverlayManagerService extends SystemService {
for (final String targetPackageName : targetPackageNames) {
if (DEBUG) {
Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
- + TextUtils.join(",", pendingChanges.get(targetPackageName))
+ + pendingChanges.get(targetPackageName)
+ "] userId=" + userId);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index e60411bb78c5..c547c36a8033 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -31,6 +31,7 @@ import android.annotation.Nullable;
import android.content.om.OverlayInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.overlay.OverlayPaths;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -697,19 +698,20 @@ final class OverlayManagerServiceImpl {
removeIdmapIfPossible(oi);
}
- List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
+ OverlayPaths getEnabledOverlayPaths(@NonNull final String targetPackageName,
final int userId) {
final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
userId);
- final List<String> paths = new ArrayList<>(overlays.size());
+ final OverlayPaths.Builder paths = new OverlayPaths.Builder();
final int n = overlays.size();
for (int i = 0; i < n; i++) {
final OverlayInfo oi = overlays.get(i);
- if (oi.isEnabled()) {
- paths.add(oi.packageName);
+ if (!oi.isEnabled()) {
+ continue;
}
+ paths.addApkPath(oi.baseCodePath);
}
- return paths;
+ return paths.build();
}
/**
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 15e1d5281bfa..7bf704299373 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -59,6 +59,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.utils.Snappable;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
+import com.android.server.utils.Watched;
import com.android.server.utils.WatchedSparseArray;
import com.android.server.utils.WatchedSparseBooleanArray;
import com.android.server.utils.Watcher;
@@ -123,6 +124,7 @@ class InstantAppRegistry implements Watchable, Snappable {
private final CookiePersistence mCookiePersistence;
/** State for uninstalled instant apps */
+ @Watched
@GuardedBy("mService.mLock")
private final WatchedSparseArray<List<UninstalledInstantAppState>> mUninstalledInstantApps;
@@ -132,10 +134,12 @@ class InstantAppRegistry implements Watchable, Snappable {
* The value is a set of instant app UIDs.
* UserID -> TargetAppId -> InstantAppId
*/
+ @Watched
@GuardedBy("mService.mLock")
private final WatchedSparseArray<WatchedSparseArray<WatchedSparseBooleanArray>> mInstantGrants;
/** The set of all installed instant apps. UserID -> AppID */
+ @Watched
@GuardedBy("mService.mLock")
private final WatchedSparseArray<WatchedSparseBooleanArray> mInstalledInstantAppUids;
@@ -189,6 +193,7 @@ class InstantAppRegistry implements Watchable, Snappable {
mUninstalledInstantApps.registerObserver(mObserver);
mInstantGrants.registerObserver(mObserver);
mInstalledInstantAppUids.registerObserver(mObserver);
+ Watchable.verifyWatchedAttributes(this, mObserver);
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 965f68bdcaf3..0c143c97e1aa 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -193,6 +193,7 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
import android.content.pm.ModuleInfo;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.PackageChangeEvent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
@@ -234,6 +235,7 @@ import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.IArtManager;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.ParsingPackageUtils;
@@ -1747,6 +1749,11 @@ public class PackageManagerService extends IPackageManager.Stub
public AndroidPackage getPackage(@NonNull String packageName) {
return getPackageLocked(packageName);
}
+
+ @Override
+ public boolean filterAppAccess(String packageName, int callingUid, int userId) {
+ return mPmInternal.filterAppAccess(packageName, callingUid, userId);
+ }
}
/**
@@ -5938,6 +5945,21 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ // Link watchables to the class
+ private void registerObserver() {
+ mPackages.registerObserver(mWatcher);
+ mSharedLibraries.registerObserver(mWatcher);
+ mStaticLibsByDeclaringPackage.registerObserver(mWatcher);
+ mInstrumentation.registerObserver(mWatcher);
+ mWebInstantAppsDisabled.registerObserver(mWatcher);
+ mAppsFilter.registerObserver(mWatcher);
+ mInstantAppRegistry.registerObserver(mWatcher);
+ mSettings.registerObserver(mWatcher);
+ // If neither "build" attribute is true then this may be a mockito test, and verification
+ // can fail as a false positive.
+ Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
+ }
+
/**
* A extremely minimal constructor designed to start up a PackageManagerService instance for
* testing.
@@ -6021,15 +6043,7 @@ public class PackageManagerService extends IPackageManager.Stub
sSnapshotCorked = true;
mLiveComputer = createLiveComputer();
mSnapshotComputer = mLiveComputer;
-
- // Link up the watchers
- mPackages.registerObserver(mWatcher);
- mSharedLibraries.registerObserver(mWatcher);
- mStaticLibsByDeclaringPackage.registerObserver(mWatcher);
- mInstrumentation.registerObserver(mWatcher);
- mWebInstantAppsDisabled.registerObserver(mWatcher);
- mAppsFilter.registerObserver(mWatcher);
- Watchable.verifyWatchedAttributes(this, mWatcher);
+ registerObserver();
mPackages.putAll(testParams.packages);
mEnableFreeCacheV2 = testParams.enableFreeCacheV2;
@@ -6183,15 +6197,6 @@ public class PackageManagerService extends IPackageManager.Stub
mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
mDomainVerificationManager.setConnection(mDomainVerificationConnection);
- // Link up the watchers
- mPackages.registerObserver(mWatcher);
- mSharedLibraries.registerObserver(mWatcher);
- mStaticLibsByDeclaringPackage.registerObserver(mWatcher);
- mInstrumentation.registerObserver(mWatcher);
- mWebInstantAppsDisabled.registerObserver(mWatcher);
- mAppsFilter.registerObserver(mWatcher);
- Watchable.verifyWatchedAttributes(this, mWatcher);
-
// Create the computer as soon as the state objects have been installed. The
// cached computer is the same as the live computer until the end of the
// constructor, at which time the invalidation method updates it. The cache is
@@ -6200,6 +6205,7 @@ public class PackageManagerService extends IPackageManager.Stub
sSnapshotCorked = true;
mLiveComputer = createLiveComputer();
mSnapshotComputer = mLiveComputer;
+ registerObserver();
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
@@ -16156,8 +16162,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Deprecated
@Override
public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
- mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
- return true;
+ return mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
}
@Deprecated
@@ -18119,11 +18124,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (libPs == null) {
continue;
}
- final String[] overlayPaths = libPs.getOverlayPaths(currentUserId);
- if (overlayPaths != null) {
- ps.setOverlayPathsForLibrary(sharedLib.getName(),
- Arrays.asList(overlayPaths), currentUserId);
- }
+ ps.setOverlayPathsForLibrary(sharedLib.getName(),
+ libPs.getOverlayPaths(currentUserId), currentUserId);
}
}
}
@@ -26614,34 +26616,19 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
- @Nullable List<String> overlayPackageNames,
- @NonNull Collection<String> outUpdatedPackageNames) {
+ @Nullable OverlayPaths overlayPaths,
+ @NonNull Set<String> outUpdatedPackageNames) {
+ boolean modified = false;
synchronized (mLock) {
final AndroidPackage targetPkg = mPackages.get(targetPackageName);
if (targetPackageName == null || targetPkg == null) {
Slog.e(TAG, "failed to find package " + targetPackageName);
return false;
}
- ArrayList<String> overlayPaths = null;
- if (overlayPackageNames != null && overlayPackageNames.size() > 0) {
- final int N = overlayPackageNames.size();
- overlayPaths = new ArrayList<>(N);
- for (int i = 0; i < N; i++) {
- final String packageName = overlayPackageNames.get(i);
- final AndroidPackage pkg = mPackages.get(packageName);
- if (pkg == null) {
- Slog.e(TAG, "failed to find package " + packageName);
- return false;
- }
- overlayPaths.add(pkg.getBaseApkPath());
- }
- }
- ArraySet<String> updatedPackageNames = null;
if (targetPkg.getLibraryNames() != null) {
// Set the overlay paths for dependencies of the shared library.
- updatedPackageNames = new ArraySet<>();
- for (String libName : targetPkg.getLibraryNames()) {
+ for (final String libName : targetPkg.getLibraryNames()) {
final SharedLibraryInfo info = getSharedLibraryInfoLPr(libName,
SharedLibraryInfo.VERSION_UNDEFINED);
if (info == null) {
@@ -26652,28 +26639,30 @@ public class PackageManagerService extends IPackageManager.Stub
if (dependents == null) {
continue;
}
- for (VersionedPackage dependent : dependents) {
+ for (final VersionedPackage dependent : dependents) {
final PackageSetting ps = mSettings.getPackageLPr(
dependent.getPackageName());
if (ps == null) {
continue;
}
- ps.setOverlayPathsForLibrary(libName, overlayPaths, userId);
- updatedPackageNames.add(dependent.getPackageName());
+ if (ps.setOverlayPathsForLibrary(libName, overlayPaths, userId)) {
+ outUpdatedPackageNames.add(dependent.getPackageName());
+ modified = true;
+ }
}
}
}
final PackageSetting ps = mSettings.getPackageLPr(targetPackageName);
- ps.setOverlayPaths(overlayPaths, userId);
-
- outUpdatedPackageNames.add(targetPackageName);
- if (updatedPackageNames != null) {
- outUpdatedPackageNames.addAll(updatedPackageNames);
+ if (ps.setOverlayPaths(overlayPaths, userId)) {
+ outUpdatedPackageNames.add(targetPackageName);
+ modified = true;
}
}
- invalidatePackageInfoCache();
+ if (modified) {
+ invalidatePackageInfoCache();
+ }
return true;
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 3a142837e063..5364cbfede86 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -31,6 +31,7 @@ import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.Signature;
import android.content.pm.SuspendDialogInfo;
+import android.content.pm.overlay.OverlayPaths;
import android.os.PersistableBundle;
import android.os.incremental.IncrementalManager;
import android.service.pm.PackageProto;
@@ -44,7 +45,6 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.File;
import java.util.Arrays;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -327,21 +327,20 @@ public abstract class PackageSettingBase extends SettingBase {
modifyUserState(userId).uninstallReason = uninstallReason;
}
- void setOverlayPaths(List<String> overlayPaths, int userId) {
- modifyUserState(userId).setOverlayPaths(overlayPaths == null ? null :
- overlayPaths.toArray(new String[overlayPaths.size()]));
+ boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) {
+ return modifyUserState(userId).setOverlayPaths(overlayPaths);
}
- String[] getOverlayPaths(int userId) {
+ OverlayPaths getOverlayPaths(int userId) {
return readUserState(userId).getOverlayPaths();
}
- void setOverlayPathsForLibrary(String libName, List<String> overlayPaths, int userId) {
- modifyUserState(userId).setSharedLibraryOverlayPaths(libName,
- overlayPaths == null ? null : overlayPaths.toArray(new String[0]));
+ boolean setOverlayPathsForLibrary(String libName, OverlayPaths overlayPaths,
+ int userId) {
+ return modifyUserState(userId).setSharedLibraryOverlayPaths(libName, overlayPaths);
}
- Map<String, String[]> getOverlayPathsForLibrary(int userId) {
+ Map<String, OverlayPaths> getOverlayPathsForLibrary(int userId) {
return readUserState(userId).getSharedLibraryOverlayPaths();
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fb033e6594b8..a8a6bcec2313 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -40,6 +40,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageUserState;
@@ -49,6 +50,7 @@ import android.content.pm.Signature;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.component.ParsedComponent;
import android.content.pm.parsing.component.ParsedIntentInfo;
@@ -105,9 +107,6 @@ import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.LocalServices;
import com.android.server.backup.PreferredActivityBackupHelper;
import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
-import com.android.server.pm.verify.domain.DomainVerificationPersistence;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -115,6 +114,9 @@ import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.permission.LegacyPermissionSettings;
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.permission.LegacyPermissionState.PermissionState;
+import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationPersistence;
import com.android.server.utils.Snappable;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.utils.Watchable;
@@ -487,7 +489,7 @@ public final class Settings implements Watchable, Snappable {
// App-link priority tracking, per-user
@NonNull
@Watched
- final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray();
+ private final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray();
final StringBuilder mReadMessages = new StringBuilder();
@@ -552,6 +554,7 @@ public final class Settings implements Watchable, Snappable {
mAppIds.registerObserver(mObserver);
mOtherAppIds.registerObserver(mObserver);
mRenamedPackages.registerObserver(mObserver);
+ mNextAppLinkGeneration.registerObserver(mObserver);
mDefaultBrowserApp.registerObserver(mObserver);
Watchable.verifyWatchedAttributes(this, mObserver);
@@ -602,6 +605,7 @@ public final class Settings implements Watchable, Snappable {
mAppIds.registerObserver(mObserver);
mOtherAppIds.registerObserver(mObserver);
mRenamedPackages.registerObserver(mObserver);
+ mNextAppLinkGeneration.registerObserver(mObserver);
mDefaultBrowserApp.registerObserver(mObserver);
Watchable.verifyWatchedAttributes(this, mObserver);
@@ -649,6 +653,7 @@ public final class Settings implements Watchable, Snappable {
mPastSignatures.addAll(r.mPastSignatures);
mKeySetRefs.putAll(r.mKeySetRefs);
mRenamedPackages.snapshot(r.mRenamedPackages);
+ mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration);
mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp);
// mReadMessages
mPendingPackages.addAll(r.mPendingPackages);
@@ -2707,7 +2712,6 @@ public final class Settings implements Watchable, Snappable {
writeSigningKeySetLPr(serializer, pkg.keySetData);
writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
writeKeySetAliasesLPr(serializer, pkg.keySetData);
- mDomainVerificationManager.writeLegacySettings(serializer, pkg.name);
writeMimeGroupLPr(serializer, pkg.mimeGroups);
serializer.endTag(null, "package");
@@ -4686,26 +4690,58 @@ public final class Settings implements Watchable, Snappable {
}
}
- String[] overlayPaths = ps.getOverlayPaths(user.id);
- if (overlayPaths != null && overlayPaths.length > 0) {
- pw.print(prefix); pw.println(" overlay paths:");
- for (String path : overlayPaths) {
- pw.print(prefix); pw.print(" "); pw.println(path);
+ final OverlayPaths overlayPaths = ps.getOverlayPaths(user.id);
+ if (overlayPaths != null) {
+ if (!overlayPaths.getOverlayPaths().isEmpty()) {
+ pw.print(prefix);
+ pw.println(" overlay paths:");
+ for (String path : overlayPaths.getOverlayPaths()) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.println(path);
+ }
+ }
+ if (!overlayPaths.getResourceDirs().isEmpty()) {
+ pw.print(prefix);
+ pw.println(" legacy overlay paths:");
+ for (String path : overlayPaths.getResourceDirs()) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.println(path);
+ }
}
}
- Map<String, String[]> sharedLibraryOverlayPaths =
+ final Map<String, OverlayPaths> sharedLibraryOverlayPaths =
ps.getOverlayPathsForLibrary(user.id);
if (sharedLibraryOverlayPaths != null) {
- for (Map.Entry<String, String[]> libOverlayPaths :
+ for (Map.Entry<String, OverlayPaths> libOverlayPaths :
sharedLibraryOverlayPaths.entrySet()) {
- if (libOverlayPaths.getValue() == null) {
+ final OverlayPaths paths = libOverlayPaths.getValue();
+ if (paths == null) {
continue;
}
- pw.print(prefix); pw.print(" ");
- pw.print(libOverlayPaths.getKey()); pw.println(" overlay paths:");
- for (String path : libOverlayPaths.getValue()) {
- pw.print(prefix); pw.print(" "); pw.println(path);
+ if (!paths.getOverlayPaths().isEmpty()) {
+ pw.print(prefix);
+ pw.println(" ");
+ pw.print(libOverlayPaths.getKey());
+ pw.println(" overlay paths:");
+ for (String path : paths.getOverlayPaths()) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.println(path);
+ }
+ }
+ if (!paths.getResourceDirs().isEmpty()) {
+ pw.print(prefix);
+ pw.println(" ");
+ pw.print(libOverlayPaths.getKey());
+ pw.println(" legacy overlay paths:");
+ for (String path : paths.getResourceDirs()) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.println(path);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
index c521f828ade9..275dd053fdde 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -18,8 +18,10 @@ package com.android.server.pm.verify.domain;
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Process;
@@ -30,10 +32,17 @@ public class DomainVerificationEnforcer {
@NonNull
private final Context mContext;
+ @NonNull
+ private Callback mCallback;
+
public DomainVerificationEnforcer(@NonNull Context context) {
mContext = context;
}
+ public void setCallback(@NonNull Callback callback) {
+ mCallback = callback;
+ }
+
/**
* Enforced when mutating any state from shell or internally in the system process.
*/
@@ -67,6 +76,11 @@ public class DomainVerificationEnforcer {
"Caller " + callingUid
+ " is not allowed to query domain verification state");
}
+
+ mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
+ Binder.getCallingPid(), callingUid,
+ "Caller " + callingUid + " does not hold "
+ + android.Manifest.permission.QUERY_ALL_PACKAGES);
break;
}
}
@@ -84,28 +98,42 @@ public class DomainVerificationEnforcer {
isAllowed = true;
break;
default:
- // TODO(b/159952358): Remove permission check? The component package should
- // have been checked when the verifier component was first scanned in PMS.
- mContext.enforcePermission(
- android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
- Binder.getCallingPid(), callingUid,
- "Caller " + callingUid + " does not hold DOMAIN_VERIFICATION_AGENT");
+ final int callingPid = Binder.getCallingPid();
+ boolean isLegacyVerificationAgent = false;
+ if (mContext.checkPermission(
+ android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, callingPid,
+ callingUid) != PackageManager.PERMISSION_GRANTED) {
+ isLegacyVerificationAgent = mContext.checkPermission(
+ android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+ callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+ if (!isLegacyVerificationAgent) {
+ throw new SecurityException("Caller " + callingUid + " does not hold "
+ + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT);
+ }
+ }
+
+ // If the caller isn't a legacy verifier, it needs the QUERY_ALL permission
+ if (!isLegacyVerificationAgent) {
+ mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
+ callingPid, callingUid, "Caller " + callingUid + " does not hold "
+ + android.Manifest.permission.QUERY_ALL_PACKAGES);
+ }
+
isAllowed = proxy.isCallerVerifier(callingUid);
break;
}
if (!isAllowed) {
throw new SecurityException("Caller " + callingUid
- + " is not the approved domain verification agent, isVerifier = "
- + proxy.isCallerVerifier(callingUid));
+ + " is not the approved domain verification agent");
}
}
/**
* Enforced when mutating user selection state inside an exposed API method.
*/
- public void assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
- @UserIdInt int targetUserId) throws SecurityException {
+ public boolean assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
+ @Nullable String packageName, @UserIdInt int targetUserId) throws SecurityException {
if (callingUserId != targetUserId) {
mContext.enforcePermission(
Manifest.permission.INTERACT_ACROSS_USERS,
@@ -117,12 +145,51 @@ public class DomainVerificationEnforcer {
android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
Binder.getCallingPid(), callingUid,
"Caller is not allowed to edit user selections");
+
+ if (packageName == null) {
+ return true;
+ }
+
+ return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
}
- public void callerIsLegacyUserSelector(int callingUid) {
+ public boolean callerIsLegacyUserSelector(int callingUid, @UserIdInt int callingUserId,
+ @NonNull String packageName, @UserIdInt int targetUserId) {
mContext.enforcePermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS,
Binder.getCallingPid(), callingUid,
"Caller is not allowed to edit user state");
+
+ if (callingUserId != targetUserId) {
+ if (mContext.checkPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS,
+ Binder.getCallingPid(), callingUid) != PackageManager.PERMISSION_GRANTED) {
+ // Legacy API did not enforce this, so for backwards compatibility, fail silently
+ return false;
+ }
+ }
+
+ return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
+ }
+
+ public boolean callerIsLegacyUserQuerent(int callingUid, @UserIdInt int callingUserId,
+ @NonNull String packageName, @UserIdInt int targetUserId) {
+ if (callingUserId != targetUserId) {
+ // The legacy API enforces the _FULL variant, so maintain that here
+ mContext.enforcePermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Binder.getCallingPid(), callingUid,
+ "Caller is not allowed to edit other users");
+ }
+
+ return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
+ }
+
+ public interface Callback {
+ /**
+ * @return true if access to the given package should be filtered and the method failed as
+ * if the package was not installed
+ */
+ boolean filterAppAccess(@NonNull String packageName, int callingUid, @UserIdInt int userId);
}
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 0474d78a3e53..50fd6e3ddea1 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -174,8 +174,10 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
* Set aside a legacy user selection that will be restored to a pending
* {@link DomainVerificationPkgState} once it's added through
* {@link #addPackage(PackageSetting)}.
+ *
+ * @return true if state changed successfully
*/
- void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state);
+ boolean setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state);
/**
* Until the legacy APIs are entirely removed, returns the legacy state from the previously
@@ -184,12 +186,6 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
int getLegacyState(@NonNull String packageName, @UserIdInt int userId);
/**
- * Serialize a legacy setting that wasn't attached yet.
- * TODO: Does this even matter? Should consider for removal.
- */
- void writeLegacySettings(TypedXmlSerializer serializer, String name);
-
- /**
* Print the verification state and user selection state of a package.
*
* @param packageName the package whose state to change, or all packages if none is
@@ -235,7 +231,8 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
throws IllegalArgumentException, NameNotFoundException;
- interface Connection extends Function<String, PackageSetting> {
+ interface Connection extends DomainVerificationEnforcer.Callback,
+ Function<String, PackageSetting> {
/**
* Notify that a settings change has been made and that eventually
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index e24e5bbfa4f6..fa0327414914 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -47,12 +47,12 @@ import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyUnavailable;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import org.xmlpull.v1.XmlPullParserException;
@@ -92,9 +92,9 @@ public class DomainVerificationService extends SystemService
* immediately attached once its available.
* <p>
* Generally this should be not accessed directly. Prefer calling {@link
- * #getAndValidateAttachedLocked(UUID, Set, boolean)}.
+ * #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer)}.
*
- * @see #getAndValidateAttachedLocked(UUID, Set, boolean)
+ * @see #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer)
**/
@GuardedBy("mLock")
@NonNull
@@ -160,6 +160,7 @@ public class DomainVerificationService extends SystemService
@Override
public void setConnection(@NonNull Connection connection) {
mConnection = connection;
+ mEnforcer.setCallback(mConnection);
}
@NonNull
@@ -285,7 +286,7 @@ public class DomainVerificationService extends SystemService
mEnforcer.assertApprovedVerifier(callingUid, mProxy);
synchronized (mLock) {
DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
- true /* forAutoVerify */);
+ true /* forAutoVerify */, callingUid, null /* userId */);
ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
for (String domain : domains) {
Integer previousState = stateMap.get(domain);
@@ -389,8 +390,10 @@ public class DomainVerificationService extends SystemService
public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
boolean allowed, @UserIdInt int userId) throws NameNotFoundException {
- mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
- mConnection.getCallingUserId(), userId);
+ if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), packageName, userId)) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
synchronized (mLock) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
if (pkgState == null) {
@@ -455,11 +458,18 @@ public class DomainVerificationService extends SystemService
public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
@NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
throws InvalidDomainSetException, NameNotFoundException {
- mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
- mConnection.getCallingUserId(), userId);
synchronized (mLock) {
+ final int callingUid = mConnection.getCallingUid();
+ // Pass null for package name here and do the app visibility enforcement inside
+ // getAndValidateAttachedLocked instead, since this has to fail with the same invalid
+ // ID reason if the target app is invisible
+ if (!mEnforcer.assertApprovedUserSelector(callingUid, mConnection.getCallingUserId(),
+ null /* packageName */, userId)) {
+ throw new InvalidDomainSetException(domainSetId, null,
+ InvalidDomainSetException.REASON_ID_INVALID);
+ }
DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
- false /* forAutoVerify */);
+ false /* forAutoVerify */, callingUid, userId);
DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
if (enabled) {
userState.addHosts(domains);
@@ -556,8 +566,10 @@ public class DomainVerificationService extends SystemService
@Override
public DomainVerificationUserSelection getDomainVerificationUserSelection(
@NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException {
- mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
- mConnection.getCallingUserId(), userId);
+ if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), packageName, userId)) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
synchronized (mLock) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
if (pkgState == null) {
@@ -844,23 +856,27 @@ public class DomainVerificationService extends SystemService
}
@Override
- public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) {
- mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid());
+ public boolean setLegacyUserState(@NonNull String packageName, @UserIdInt int userId,
+ int state) {
+ if (!mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), packageName, userId)) {
+ return false;
+ }
mLegacySettings.add(packageName, userId, state);
mConnection.scheduleWriteSettings();
+ return true;
}
@Override
public int getLegacyState(@NonNull String packageName, @UserIdInt int userId) {
+ if (!mEnforcer.callerIsLegacyUserQuerent(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), packageName, userId)) {
+ return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ }
return mLegacySettings.getUserState(packageName, userId);
}
@Override
- public void writeLegacySettings(TypedXmlSerializer serializer, String name) {
-
- }
-
- @Override
public void clearPackage(@NonNull String packageName) {
synchronized (mLock) {
mAttachedPkgStates.remove(packageName);
@@ -935,10 +951,14 @@ public class DomainVerificationService extends SystemService
* Validates parameters provided by an external caller. Checks that an ID is still live and that
* any provided domains are valid. Should be called at the beginning of each API that takes in a
* {@link UUID} domain set ID.
+ *
+ * @param userIdForFilter which user to filter app access to, or null if the caller has already
+ * validated package visibility
*/
@GuardedBy("mLock")
private DomainVerificationPkgState getAndValidateAttachedLocked(@NonNull UUID domainSetId,
- @NonNull Set<String> domains, boolean forAutoVerify)
+ @NonNull Set<String> domains, boolean forAutoVerify, int callingUid,
+ @Nullable Integer userIdForFilter)
throws InvalidDomainSetException, NameNotFoundException {
if (domainSetId == null) {
throw new InvalidDomainSetException(null, null,
@@ -952,6 +972,13 @@ public class DomainVerificationService extends SystemService
}
String pkgName = pkgState.getPackageName();
+
+ if (userIdForFilter != null
+ && mConnection.filterAppAccess(pkgName, callingUid, userIdForFilter)) {
+ throw new InvalidDomainSetException(domainSetId, null,
+ InvalidDomainSetException.REASON_ID_INVALID);
+ }
+
PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
throw DomainVerificationUtils.throwPackageUnavailable(pkgName);
diff --git a/services/core/java/com/android/server/utils/Watchable.java b/services/core/java/com/android/server/utils/Watchable.java
index f936693bd621..44a74593cda2 100644
--- a/services/core/java/com/android/server/utils/Watchable.java
+++ b/services/core/java/com/android/server/utils/Watchable.java
@@ -19,8 +19,8 @@ package com.android.server.utils;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Build;
+import android.util.Log;
-import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
/**
@@ -61,40 +61,54 @@ public interface Watchable {
public void dispatchChange(@Nullable Watchable what);
/**
- * Return true if the field is tagged with @Watched
- */
- private static boolean isWatched(Field f) {
- for (Annotation a : f.getDeclaredAnnotations()) {
- if (a.annotationType().equals(Watched.class)) {
- return true;
- }
- }
- return false;
- }
-
- /**
* Verify that all @Watched {@link Watchable} attributes are being watched by this
* class. This requires reflection and only runs in engineering or user debug
* builds.
+ * @param base The object that contains watched attributes.
+ * @param observer The {@link Watcher} that should be watching these attributes.
+ * @param logOnly If true then log errors; if false then throw an RuntimeExecption on error.
*/
- static void verifyWatchedAttributes(Object base, Watcher observer) {
- if (Build.IS_ENG || Build.IS_USERDEBUG) {
- for (Field f : base.getClass().getDeclaredFields()) {
+ static void verifyWatchedAttributes(Object base, Watcher observer, boolean logOnly) {
+ if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
+ return;
+ }
+ for (Field f : base.getClass().getDeclaredFields()) {
+ if (f.getAnnotation(Watched.class) != null) {
+ final String fn = base.getClass().getName() + "." + f.getName();
try {
- final boolean flagged = isWatched(f);
+ f.setAccessible(true);
final Object o = f.get(base);
- final boolean watchable = o instanceof Watchable;
- if (flagged && watchable) {
- Watchable attr = (Watchable) f.get(base);
+ if (o instanceof Watchable) {
+ Watchable attr = (Watchable) (o);
if (attr != null && !attr.isRegisteredObserver(observer)) {
- throw new RuntimeException(f.getName() + " missing an observer");
+ if (logOnly) {
+ Log.e("Watchable", fn + " missing an observer");
+ } else {
+ throw new RuntimeException("Watchable " + fn
+ + " missing an observer");
+ }
}
}
} catch (IllegalAccessException e) {
// The field is protected; ignore it. Other exceptions that may be thrown by
// Field.get() are allowed to roll up.
+ if (logOnly) {
+ Log.e("Watchable", fn + " not visible");
+ } else {
+ throw new RuntimeException("Watchable " + fn + " not visible");
+ }
}
}
}
}
+
+ /**
+ * Verify that all @Watched {@link Watchable} attributes are being watched by this
+ * class. This calls verifyWatchedAttributes() with logOnly set to false.
+ * @param base The object that contains watched attributes.
+ * @param observer The {@link Watcher} that should be watching these attributes.
+ */
+ static void verifyWatchedAttributes(Object base, Watcher observer) {
+ verifyWatchedAttributes(base, observer, false);
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 61fe023ae1b9..5460e36a5f1b 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2086,6 +2086,7 @@ public class DisplayPolicy {
pi.getResDir(),
null /* splitResDirs */,
pi.getOverlayDirs(),
+ pi.getOverlayPaths(),
pi.getApplicationInfo().sharedLibraryFiles,
mDisplayContent.getDisplayId(),
null /* overrideConfig */,
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ceebe9550846..bd93e045cdc7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -847,6 +847,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction();
+ // Send any pending task-info changes that were queued-up during a layout deferment
+ mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
mWmService.mSyncEngine.onSurfacePlacement();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
@@ -859,8 +861,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
- // Send any pending task-info changes that were queued-up during a layout deferment
- mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
checkAppTransitionReady(surfacePlacer);
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1f8daf6f361f..8b186796db8d 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -104,9 +104,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final boolean mCanAddInternalSystemWindow;
private final boolean mCanStartTasksFromRecents;
- // If non-system overlays from this process can be hidden by the user or app using
- // HIDE_NON_SYSTEM_OVERLAY_WINDOWS.
- final boolean mOverlaysCanBeHidden;
final boolean mCanCreateSystemApplicationOverlay;
final boolean mCanHideNonSystemOverlayWindows;
final boolean mCanAcquireSleepToken;
@@ -136,8 +133,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
== PERMISSION_GRANTED;
mCanStartTasksFromRecents = service.mContext.checkCallingOrSelfPermission(
START_TASKS_FROM_RECENTS) == PERMISSION_GRANTED;
- mOverlaysCanBeHidden = !mCanAddInternalSystemWindow
- && !mService.mAtmInternal.isCallerRecents(mUid);
mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
== PERMISSION_GRANTED;
mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
@@ -253,11 +248,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public void setTransparentRegion(IWindow window, Region region) {
- mService.setTransparentRegionWindow(this, window, region);
- }
-
- @Override
public void setInsets(IWindow window, int touchableInsets,
Rect contentInsets, Rect visibleInsets, Region touchableArea) {
mService.setInsetsWindow(this, window, touchableInsets, contentInsets,
@@ -682,7 +672,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
boolean changed;
- if (mOverlaysCanBeHidden && !mCanCreateSystemApplicationOverlay) {
+ if (!mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay) {
// We want to track non-system apps adding alert windows so we can post an
// on-going notification for the user to control their visibility.
if (visible) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3c7bab3da279..23487252c91c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2150,23 +2150,6 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.i(tag, s, e);
}
- void setTransparentRegionWindow(Session session, IWindow client, Region region) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- WindowState w = windowForClientLocked(session, client, false);
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE transparentRegionHint=%s: %s",
- region, w);
-
- if ((w != null) && w.mHasSurface) {
- w.mWinAnimator.setTransparentRegionHintLocked(region);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets,
Rect visibleInsets, Region touchableRegion) {
int uid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fd3d9ba499b7..a94b0aa9b72f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3100,7 +3100,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) {
- if (!mSession.mOverlaysCanBeHidden
+ if (mSession.mCanAddInternalSystemWindow
|| (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index fe70dc12d3a2..ece256e8c591 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -678,14 +678,6 @@ class WindowStateAnimator {
}
}
- void setTransparentRegionHintLocked(final Region region) {
- if (mSurfaceController == null) {
- Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
- return;
- }
- mSurfaceController.setTransparentRegionHint(region);
- }
-
boolean setWallpaperOffset(int dx, int dy, float scale) {
if (mXOffset == dx && mYOffset == dy && Float.compare(mWallpaperScale, scale) == 0) {
return false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 82ba3c188b76..636f0bb6086f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -194,22 +194,6 @@ class WindowSurfaceController {
return true;
}
- void setTransparentRegionHint(final Region region) {
- if (mSurfaceControl == null) {
- Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
- return;
- }
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setTransparentRegion");
- mService.openSurfaceTransaction();
- try {
- getGlobalTransaction().setTransparentRegionHint(mSurfaceControl, region);
- } finally {
- mService.closeSurfaceTransaction("setTransparentRegion");
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION setTransparentRegion");
- }
- }
-
void setOpaque(boolean isOpaque) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isOpaque=%b: %s", isOpaque, title);
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index d863194d6889..c2e0b776fc60 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -18,6 +18,7 @@ package com.android.server.pm.test.verify.domain
import android.content.Context
import android.content.Intent
+import android.content.pm.PackageManager
import android.content.pm.PackageUserState
import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.parsing.component.ParsedActivity
@@ -25,7 +26,6 @@ import android.content.pm.parsing.component.ParsedIntentInfo
import android.os.Build
import android.os.Process
import android.util.ArraySet
-import android.util.Singleton
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.pm.PackageSetting
@@ -46,14 +46,11 @@ import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
import org.mockito.Mockito.eq
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.testng.Assert.assertThrows
import java.io.File
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
-private typealias Enforcer = DomainVerificationEnforcer
-
@RunWith(Parameterized::class)
class DomainVerificationEnforcerTest {
@@ -64,64 +61,27 @@ class DomainVerificationEnforcerTest {
private const val VERIFIER_UID = Process.FIRST_APPLICATION_UID + 1
private const val NON_VERIFIER_UID = Process.FIRST_APPLICATION_UID + 2
- private const val TEST_PKG = "com.test"
+ private const val VISIBLE_PKG = "com.test.visible"
+ private val VISIBLE_UUID = UUID.fromString("8db01272-270d-4606-a3db-bb35228ff9a2")
+ private const val INVISIBLE_PKG = "com.test.invisible"
+ private val INVISIBLE_UUID = UUID.fromString("16dcb029-d96c-4a19-833a-4c9d72e2ebc3")
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun parameters(): Array<Any> {
- val makeEnforcer: (Context) -> DomainVerificationEnforcer = {
- DomainVerificationEnforcer(it)
- }
+ val visiblePkg = mockPkg(VISIBLE_PKG)
+ val visiblePkgSetting = mockPkgSetting(VISIBLE_PKG, VISIBLE_UUID)
+ val invisiblePkg = mockPkg(INVISIBLE_PKG)
+ val invisiblePkgSetting = mockPkgSetting(INVISIBLE_PKG, INVISIBLE_UUID)
- val mockPkg = mockThrowOnUnmocked<AndroidPackage> {
- whenever(packageName) { TEST_PKG }
- whenever(targetSdkVersion) { Build.VERSION_CODES.S }
- whenever(activities) {
- listOf(
- ParsedActivity().apply {
- addIntent(
- ParsedIntentInfo().apply {
- autoVerify = true
- addAction(Intent.ACTION_VIEW)
- addCategory(Intent.CATEGORY_BROWSABLE)
- addCategory(Intent.CATEGORY_DEFAULT)
- addDataScheme("https")
- addDataAuthority("example.com", null)
- }
- )
+ val makeEnforcer: (Context) -> DomainVerificationEnforcer = {
+ DomainVerificationEnforcer(it).apply {
+ setCallback(mockThrowOnUnmocked {
+ whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
+ whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
+ true
}
- )
- }
- }
-
- val uuid = UUID.randomUUID()
-
- // TODO: PackageSetting field encapsulation to move to whenever(name)
- val mockPkgSetting = spyThrowOnUnmocked(
- PackageSetting(
- TEST_PKG,
- TEST_PKG,
- File("/test"),
- null,
- null,
- null,
- null,
- 1,
- 0,
- 0,
- 0,
- null,
- null,
- null,
- uuid
- )
- ) {
- whenever(getPkg()) { mockPkg }
- whenever(domainSetId) { uuid }
- whenever(userState) {
- SparseArray<PackageUserState>().apply {
- this[0] = PackageUserState()
- }
+ })
}
}
@@ -129,142 +89,160 @@ class DomainVerificationEnforcerTest {
{
val callingUidInt = AtomicInteger(-1)
val callingUserIdInt = AtomicInteger(-1)
- Triple(
- callingUidInt, callingUserIdInt, DomainVerificationService(
- it,
- mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
- mockThrowOnUnmocked {
- whenever(
- isChangeEnabled(
- anyLong(),
- any()
- )
- ) { true }
- }).apply {
- setConnection(mockThrowOnUnmocked {
- whenever(callingUid) { callingUidInt.get() }
- whenever(callingUserId) { callingUserIdInt.get() }
- whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting }
- whenever(getPackageLocked(TEST_PKG)) { mockPkg }
- whenever(schedule(anyInt(), any()))
- whenever(scheduleWriteSettings())
- })
+
+ val connection: DomainVerificationManagerInternal.Connection =
+ mockThrowOnUnmocked {
+ whenever(callingUid) { callingUidInt.get() }
+ whenever(callingUserId) { callingUserIdInt.get() }
+ whenever(getPackageSettingLocked(VISIBLE_PKG)) { visiblePkgSetting }
+ whenever(getPackageLocked(VISIBLE_PKG)) { visiblePkg }
+ whenever(getPackageSettingLocked(INVISIBLE_PKG)) { invisiblePkgSetting }
+ whenever(getPackageLocked(INVISIBLE_PKG)) { invisiblePkg }
+ whenever(schedule(anyInt(), any()))
+ whenever(scheduleWriteSettings())
+ whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
+ whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
+ true
+ }
}
- )
+ val service = DomainVerificationService(
+ it,
+ mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
+ mockThrowOnUnmocked {
+ whenever(
+ isChangeEnabled(
+ anyLong(),
+ any()
+ )
+ ) { true }
+ }).apply {
+ setConnection(connection)
+ }
+
+ Triple(callingUidInt, callingUserIdInt, service)
}
fun enforcer(
type: Type,
name: String,
- block: DomainVerificationEnforcer.(
- callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy
- ) -> Unit
- ) = Params(
- type,
- makeEnforcer,
- name
- ) { enforcer, callingUid, callingUserId, userId, proxy ->
- enforcer.block(callingUid, callingUserId, userId, proxy)
+ block: DomainVerificationEnforcer.(Params.Input<DomainVerificationEnforcer>) -> Any?
+ ) = Params(type, makeEnforcer, name) {
+ it.target.block(it)
}
fun service(
type: Type,
name: String,
- block: DomainVerificationService.(
- callingUid: Int, callingUserId: Int, userId: Int
- ) -> Unit
- ) = Params(
- type,
- makeService,
- name
- ) { uidAndUserIdAndService, callingUid, callingUserId, userId, proxy ->
- val (callingUidInt, callingUserIdInt, service) = uidAndUserIdAndService
- callingUidInt.set(callingUid)
- callingUserIdInt.set(callingUserId)
- service.setProxy(proxy)
- service.addPackage(mockPkgSetting)
- service.block(callingUid, callingUserId, userId)
+ block: DomainVerificationService.(Params.Input<Triple<AtomicInteger, AtomicInteger, DomainVerificationService>>) -> Any?
+ ) = Params(type, makeService, name) {
+ val (callingUidInt, callingUserIdInt, service) = it.target
+ callingUidInt.set(it.callingUid)
+ callingUserIdInt.set(it.callingUserId)
+ service.proxy = it.proxy
+ service.addPackage(visiblePkgSetting)
+ service.addPackage(invisiblePkgSetting)
+ service.block(it)
}
return arrayOf(
- enforcer(Type.INTERNAL, "internal") { callingUid, _, _, _ ->
- assertInternal(callingUid)
+ enforcer(Type.INTERNAL, "internal") {
+ assertInternal(it.callingUid)
},
- enforcer(Type.QUERENT, "approvedQuerent") { callingUid, _, _, proxy ->
- assertApprovedQuerent(callingUid, proxy)
+ enforcer(Type.QUERENT, "approvedQuerent") {
+ assertApprovedQuerent(it.callingUid, it.proxy)
},
- enforcer(Type.VERIFIER, "approvedVerifier") { callingUid, _, _, proxy ->
- assertApprovedVerifier(callingUid, proxy)
+ enforcer(Type.VERIFIER, "approvedVerifier") {
+ assertApprovedVerifier(it.callingUid, it.proxy)
},
enforcer(
Type.SELECTOR,
"approvedUserSelector"
- ) { callingUid, callingUserId, userId, _ ->
- assertApprovedUserSelector(callingUid, callingUserId, userId)
+ ) {
+ assertApprovedUserSelector(
+ it.callingUid, it.callingUserId,
+ it.targetPackageName, it.userId
+ )
},
-
- service(Type.INTERNAL, "setStatusInternalPackageName") { _, _, _ ->
+ service(Type.INTERNAL, "setStatusInternalPackageName") {
setDomainVerificationStatusInternal(
- TEST_PKG,
+ it.targetPackageName,
DomainVerificationManager.STATE_SUCCESS,
ArraySet(setOf("example.com"))
)
},
- service(Type.INTERNAL, "setUserSelectionInternal") { _, _, userId ->
+ service(Type.INTERNAL, "setUserSelectionInternal") {
setDomainVerificationUserSelectionInternal(
- userId,
- TEST_PKG,
+ it.userId,
+ it.targetPackageName,
false,
ArraySet(setOf("example.com"))
)
},
- service(Type.INTERNAL, "verifyPackages") { _, _, _ ->
- verifyPackages(listOf(TEST_PKG), true)
+ service(Type.INTERNAL, "verifyPackages") {
+ verifyPackages(listOf(it.targetPackageName), true)
},
- service(Type.INTERNAL, "clearState") { _, _, _ ->
- clearDomainVerificationState(listOf(TEST_PKG))
+ service(Type.INTERNAL, "clearState") {
+ clearDomainVerificationState(listOf(it.targetPackageName))
},
- service(Type.INTERNAL, "clearUserSelections") { _, _, userId ->
- clearUserSelections(listOf(TEST_PKG), userId)
+ service(Type.INTERNAL, "clearUserSelections") {
+ clearUserSelections(listOf(it.targetPackageName), it.userId)
},
- service(Type.VERIFIER, "getPackageNames") { _, _, _ ->
+ service(Type.VERIFIER, "getPackageNames") {
validVerificationPackageNames
},
- service(Type.QUERENT, "getInfo") { _, _, _ ->
- getDomainVerificationInfo(TEST_PKG)
+ service(Type.QUERENT, "getInfo") {
+ getDomainVerificationInfo(it.targetPackageName)
},
- service(Type.VERIFIER, "setStatus") { _, _, _ ->
+ service(Type.VERIFIER, "setStatus") {
setDomainVerificationStatus(
- uuid,
+ it.targetDomainSetId,
setOf("example.com"),
DomainVerificationManager.STATE_SUCCESS
)
},
- service(Type.VERIFIER, "setStatusInternalUid") { callingUid, _, _ ->
+ service(Type.VERIFIER, "setStatusInternalUid") {
setDomainVerificationStatusInternal(
- callingUid,
- uuid,
+ it.callingUid,
+ it.targetDomainSetId,
setOf("example.com"),
DomainVerificationManager.STATE_SUCCESS
)
},
- service(Type.SELECTOR, "setLinkHandlingAllowed") { _, _, _ ->
- setDomainVerificationLinkHandlingAllowed(TEST_PKG, true)
+ service(Type.SELECTOR, "setLinkHandlingAllowed") {
+ setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true)
},
- service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") { _, _, userId ->
- setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, userId)
+ service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") {
+ setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true, it.userId)
},
- service(Type.SELECTOR, "getUserSelection") { _, _, _ ->
- getDomainVerificationUserSelection(TEST_PKG)
+ service(Type.SELECTOR, "getUserSelection") {
+ getDomainVerificationUserSelection(it.targetPackageName)
},
- service(Type.SELECTOR_USER, "getUserSelectionUserId") { _, _, userId ->
- getDomainVerificationUserSelection(TEST_PKG, userId)
+ service(Type.SELECTOR_USER, "getUserSelectionUserId") {
+ getDomainVerificationUserSelection(it.targetPackageName, it.userId)
},
- service(Type.SELECTOR, "setUserSelection") { _, _, _ ->
- setDomainVerificationUserSelection(uuid, setOf("example.com"), true)
+ service(Type.SELECTOR, "setUserSelection") {
+ setDomainVerificationUserSelection(
+ it.targetDomainSetId,
+ setOf("example.com"),
+ true
+ )
+ },
+ service(Type.SELECTOR_USER, "setUserSelectionUserId") {
+ setDomainVerificationUserSelection(
+ it.targetDomainSetId,
+ setOf("example.com"),
+ true,
+ it.userId
+ )
},
- service(Type.SELECTOR_USER, "setUserSelectionUserId") { _, _, userId ->
- setDomainVerificationUserSelection(uuid, setOf("example.com"), true, userId)
+ service(Type.LEGACY_SELECTOR, "setLegacyUserState") {
+ setLegacyUserState(
+ it.targetPackageName, it.userId,
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER
+ )
+ },
+ service(Type.LEGACY_QUERENT, "getLegacyUserState") {
+ getLegacyState(it.targetPackageName, it.userId)
},
)
}
@@ -273,9 +251,7 @@ class DomainVerificationEnforcerTest {
val type: Type,
val construct: (context: Context) -> T,
val name: String,
- private val method: (
- T, callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy
- ) -> Unit
+ private val method: (Input<T>) -> Any?
) {
override fun toString() = "${type}_$name"
@@ -284,10 +260,79 @@ class DomainVerificationEnforcerTest {
callingUid: Int,
callingUserId: Int,
userId: Int,
+ targetPackageName: String,
+ targetDomainSetId: UUID,
proxy: DomainVerificationProxy
- ) {
- @Suppress("UNCHECKED_CAST")
- method(target as T, callingUid, callingUserId, userId, proxy)
+ ): Any? = method(
+ Input(
+ @Suppress("UNCHECKED_CAST")
+ target as T,
+ callingUid,
+ callingUserId,
+ userId,
+ targetPackageName,
+ targetDomainSetId,
+ proxy
+ )
+ )
+
+ data class Input<T>(
+ val target: T,
+ val callingUid: Int,
+ val callingUserId: Int,
+ val userId: Int,
+ val targetPackageName: String,
+ val targetDomainSetId: UUID,
+ val proxy: DomainVerificationProxy
+ )
+ }
+
+ fun mockPkg(packageName: String) = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(this.packageName) { packageName }
+ whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+ whenever(activities) {
+ listOf(
+ ParsedActivity().apply {
+ addIntent(
+ ParsedIntentInfo().apply {
+ autoVerify = true
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("https")
+ addDataAuthority("example.com", null)
+ }
+ )
+ }
+ )
+ }
+ }
+
+ fun mockPkgSetting(packageName: String, domainSetId: UUID) = spyThrowOnUnmocked(
+ PackageSetting(
+ packageName,
+ packageName,
+ File("/test"),
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ domainSetId
+ )
+ ) {
+ whenever(getPkg()) { mockPkg(packageName) }
+ whenever(this.domainSetId) { domainSetId }
+ whenever(userState) {
+ SparseArray<PackageUserState>().apply {
+ this[0] = PackageUserState()
+ }
}
}
}
@@ -309,6 +354,8 @@ class DomainVerificationEnforcerTest {
Type.VERIFIER -> approvedVerifier()
Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false)
Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true)
+ Type.LEGACY_QUERENT -> legacyQuerent()
+ Type.LEGACY_SELECTOR -> legacyUserSelector()
}.run { /*exhaust*/ }
}
@@ -316,24 +363,30 @@ class DomainVerificationEnforcerTest {
val context: Context = mockThrowOnUnmocked()
val target = params.construct(context)
- INTERNAL_UIDS.forEach { runMethod(target, it) }
- assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) }
- assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ // Internal doesn't care about visibility
+ listOf(true, false).forEach { visible ->
+ INTERNAL_UIDS.forEach { runMethod(target, it, visible) }
+ assertFails { runMethod(target, VERIFIER_UID, visible) }
+ assertFails {
+ runMethod(target, NON_VERIFIER_UID, visible)
+ }
+ }
}
fun approvedQuerent() {
val allowUserSelection = AtomicBoolean(false)
+ val allowPreferredApps = AtomicBoolean(false)
+ val allowQueryAll = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (!allowUserSelection.get()) {
- throw SecurityException()
- }
- }
+ initPermission(
+ allowUserSelection,
+ android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+ )
+ initPermission(
+ allowPreferredApps,
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS
+ )
+ initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES)
}
val target = params.construct(context)
@@ -341,27 +394,41 @@ class DomainVerificationEnforcerTest {
verifyNoMoreInteractions(context)
+ assertFails { runMethod(target, VERIFIER_UID) }
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ // Check that the verifier only needs QUERY_ALL to pass
+ allowQueryAll.set(true)
runMethod(target, VERIFIER_UID)
- assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ allowQueryAll.set(false)
+
+ allowPreferredApps.set(true)
+
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
allowUserSelection.set(true)
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ allowQueryAll.set(true)
+
runMethod(target, NON_VERIFIER_UID)
}
fun approvedVerifier() {
- val shouldThrow = AtomicBoolean(false)
+ val allowDomainVerificationAgent = AtomicBoolean(false)
+ val allowIntentVerificationAgent = AtomicBoolean(false)
+ val allowQueryAll = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (shouldThrow.get()) {
- throw SecurityException()
- }
- }
+ initPermission(
+ allowDomainVerificationAgent,
+ android.Manifest.permission.DOMAIN_VERIFICATION_AGENT
+ )
+ initPermission(
+ allowIntentVerificationAgent,
+ android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT
+ )
+ initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES)
}
val target = params.construct(context)
@@ -369,94 +436,241 @@ class DomainVerificationEnforcerTest {
verifyNoMoreInteractions(context)
+ assertFails { runMethod(target, VERIFIER_UID) }
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ allowDomainVerificationAgent.set(true)
+
+ assertFails { runMethod(target, VERIFIER_UID) }
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ allowQueryAll.set(true)
+
runMethod(target, VERIFIER_UID)
- assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
- shouldThrow.set(true)
+ // Check that v1 verifiers are also allowed through
+ allowDomainVerificationAgent.set(false)
+ allowIntentVerificationAgent.set(true)
- assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) }
- assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ runMethod(target, VERIFIER_UID)
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
}
fun approvedUserSelector(verifyCrossUser: Boolean) {
- val allowUserSelection = AtomicBoolean(true)
- val allowInteractAcrossUsers = AtomicBoolean(true)
+ val allowUserSelection = AtomicBoolean(false)
+ val allowInteractAcrossUsers = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (!allowUserSelection.get()) {
- throw SecurityException()
+ initPermission(
+ allowUserSelection,
+ android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+ )
+ initPermission(
+ allowInteractAcrossUsers,
+ android.Manifest.permission.INTERACT_ACROSS_USERS
+ )
+ }
+ val target = params.construct(context)
+
+ fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+ // User selector makes no distinction by UID
+ val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+ if (throws) {
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
}
- }
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.INTERACT_ACROSS_USERS),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (!allowInteractAcrossUsers.get()) {
- throw SecurityException()
+ } else {
+ allUids.forEach {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
}
}
- }
- val target = params.construct(context)
- fun runEachTestCaseWrapped(
- callingUserId: Int,
- targetUserId: Int,
- block: (testCase: () -> Unit) -> Unit = { it.invoke() }
- ) {
- block { runMethod(target, VERIFIER_UID, callingUserId, targetUserId) }
- block { runMethod(target, NON_VERIFIER_UID, callingUserId, targetUserId) }
+ // User selector doesn't use QUERY_ALL, so the invisible package should always fail
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = false, callingUserId, targetUserId)
+ }
+ }
}
val callingUserId = 0
val notCallingUserId = 1
- runEachTestCaseWrapped(callingUserId, callingUserId)
+ runTestCases(callingUserId, callingUserId, throws = true)
if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
}
- allowInteractAcrossUsers.set(false)
+ allowUserSelection.set(true)
+
+ runTestCases(callingUserId, callingUserId, throws = false)
+ if (verifyCrossUser) {
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+ }
- runEachTestCaseWrapped(callingUserId, callingUserId)
+ allowInteractAcrossUsers.set(true)
+ runTestCases(callingUserId, callingUserId, throws = false)
if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId) {
- assertThrows(SecurityException::class.java, it)
+ runTestCases(callingUserId, notCallingUserId, throws = false)
+ }
+ }
+
+ private fun legacyUserSelector() {
+ val allowInteractAcrossUsers = AtomicBoolean(false)
+ val allowPreferredApps = AtomicBoolean(false)
+ val context: Context = mockThrowOnUnmocked {
+ initPermission(
+ allowInteractAcrossUsers,
+ android.Manifest.permission.INTERACT_ACROSS_USERS
+ )
+ initPermission(
+ allowPreferredApps,
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS
+ )
+ }
+ val target = params.construct(context)
+
+ fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+ // Legacy makes no distinction by UID
+ val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+ if (throws) {
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
+ }
+ } else {
+ allUids.forEach {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
+ }
+
+ // Legacy doesn't use QUERY_ALL, so the invisible package should always fail
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = false, callingUserId, targetUserId)
+ }
}
}
- allowUserSelection.set(false)
+ val callingUserId = 0
+ val notCallingUserId = 1
+
+ runTestCases(callingUserId, callingUserId, throws = true)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ allowPreferredApps.set(true)
- runEachTestCaseWrapped(callingUserId, callingUserId) {
- assertThrows(SecurityException::class.java, it)
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ allowInteractAcrossUsers.set(true)
+
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = false)
+ }
+
+ private fun legacyQuerent() {
+ val allowInteractAcrossUsers = AtomicBoolean(false)
+ val allowInteractAcrossUsersFull = AtomicBoolean(false)
+ val context: Context = mockThrowOnUnmocked {
+ initPermission(
+ allowInteractAcrossUsers,
+ android.Manifest.permission.INTERACT_ACROSS_USERS
+ )
+ initPermission(
+ allowInteractAcrossUsersFull,
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ )
}
- if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId) {
- assertThrows(SecurityException::class.java, it)
+ val target = params.construct(context)
+
+ fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+ // Legacy makes no distinction by UID
+ val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+ if (throws) {
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
+ }
+ } else {
+ allUids.forEach {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
+ }
+
+ // Legacy doesn't use QUERY_ALL, so the invisible package should always fail
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = false, callingUserId, targetUserId)
+ }
}
}
+ val callingUserId = 0
+ val notCallingUserId = 1
+
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ // Legacy requires the _FULL permission, so this should continue to fail
allowInteractAcrossUsers.set(true)
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ allowInteractAcrossUsersFull.set(true)
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = false)
+ }
- runEachTestCaseWrapped(callingUserId, callingUserId) {
- assertThrows(SecurityException::class.java, it)
+ private fun Context.initPermission(boolean: AtomicBoolean, permission: String) {
+ whenever(enforcePermission(eq(permission), anyInt(), anyInt(), anyString())) {
+ if (!boolean.get()) {
+ throw SecurityException()
+ }
}
- if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId) {
- assertThrows(SecurityException::class.java, it)
+ whenever(checkPermission(eq(permission), anyInt(), anyInt())) {
+ if (boolean.get()) {
+ PackageManager.PERMISSION_GRANTED
+ } else {
+ PackageManager.PERMISSION_DENIED
}
}
}
- private fun runMethod(target: Any, callingUid: Int, callingUserId: Int = 0, userId: Int = 0) {
- params.runMethod(target, callingUid, callingUserId, userId, proxy)
+ private fun runMethod(
+ target: Any,
+ callingUid: Int,
+ visible: Boolean = true,
+ callingUserId: Int = 0,
+ userId: Int = 0
+ ): Any? {
+ val packageName = if (visible) VISIBLE_PKG else INVISIBLE_PKG
+ val uuid = if (visible) VISIBLE_UUID else INVISIBLE_UUID
+ return params.runMethod(target, callingUid, callingUserId, userId, packageName, uuid, proxy)
+ }
+
+ private fun assertFails(block: () -> Any?) {
+ try {
+ val value = block()
+ // Some methods return false rather than throwing, so check that as well
+ if ((value as? Boolean) != false) {
+ // Can also return default value if it's a legacy call
+ if ((value as? Int)
+ != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
+ ) {
+ throw AssertionError("Expected call to return false, was $value")
+ }
+ }
+ } catch (e: SecurityException) {
+ } catch (e: PackageManager.NameNotFoundException) {
+ } catch (e: DomainVerificationManager.InvalidDomainSetException) {
+ // Any of these 3 exceptions are considered failures, which is expected
+ }
}
enum class Type {
@@ -473,6 +687,12 @@ class DomainVerificationEnforcerTest {
SELECTOR,
// Holding the user setting permission, but targeting cross user
- SELECTOR_USER
+ SELECTOR_USER,
+
+ // Legacy required no permissions except when cross-user
+ LEGACY_QUERENT,
+
+ // Holding the legacy preferred apps permission
+ LEGACY_SELECTOR
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 5792e02e37a2..5629d1c1107d 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -267,5 +267,8 @@ class DomainVerificationSettingsMutationTest {
whenever(getPackageLocked(TEST_PKG)) { mockPkg() }
whenever(schedule(anyInt(), any()))
whenever(scheduleWriteSettings())
+
+ // This doesn't check for visibility; that's done in the enforcer test
+ whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
index 0311920477a1..059744388197 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
@@ -19,6 +19,8 @@ package com.android.server.location.injector;
import android.os.IPowerManager;
import android.os.PowerManager.LocationPowerSaveMode;
+import com.android.server.location.eventlog.LocationEventLog;
+
/**
* Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no
* change".
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
index c39a77eac23f..6156ba9359cd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
@@ -43,6 +43,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
import org.junit.After;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index 8e5b16e1ee3a..2822d5c69091 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -16,9 +16,10 @@
package com.android.server.location.injector;
+import com.android.server.location.eventlog.LocationEventLog;
+
public class TestInjector implements Injector {
- private final LocationEventLog mLocationEventLog;
private final FakeUserInfoHelper mUserInfoHelper;
private final FakeAlarmHelper mAlarmHelper;
private final FakeAppOpsHelper mAppOpsHelper;
@@ -32,14 +33,17 @@ public class TestInjector implements Injector {
private final LocationUsageLogger mLocationUsageLogger;
public TestInjector() {
- mLocationEventLog = new LocationEventLog();
+ this(new LocationEventLog());
+ }
+
+ public TestInjector(LocationEventLog eventLog) {
mUserInfoHelper = new FakeUserInfoHelper();
mAlarmHelper = new FakeAlarmHelper();
mAppOpsHelper = new FakeAppOpsHelper();
mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
mSettingsHelper = new FakeSettingsHelper();
mAppForegroundHelper = new FakeAppForegroundHelper();
- mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(mLocationEventLog);
+ mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(eventLog);
mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
mEmergencyHelper = new FakeEmergencyHelper();
@@ -100,9 +104,4 @@ public class TestInjector implements Injector {
public LocationUsageLogger getLocationUsageLogger() {
return mLocationUsageLogger;
}
-
- @Override
- public LocationEventLog getLocationEventLog() {
- return mLocationEventLog;
- }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 54fa89a1e24b..66b037d70a40 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -83,6 +83,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.FakeUserInfoHelper;
import com.android.server.location.injector.TestInjector;
@@ -159,17 +160,19 @@ public class LocationProviderManagerTest {
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
- mInjector = new TestInjector();
+ LocationEventLog eventLog = new LocationEventLog();
+
+ mInjector = new TestInjector(eventLog);
mInjector.getUserInfoHelper().startUser(OTHER_USER);
- mPassive = new PassiveLocationProviderManager(mContext, mInjector);
+ mPassive = new PassiveLocationProviderManager(mContext, mInjector, eventLog);
mPassive.startManager();
mPassive.setRealProvider(new PassiveLocationProvider(mContext));
mProvider = new TestProvider(PROPERTIES, IDENTITY);
mProvider.setProviderAllowed(true);
- mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive);
+ mManager = new LocationProviderManager(mContext, mInjector, eventLog, NAME, mPassive);
mManager.startManager();
mManager.setRealProvider(mProvider);
}
diff --git a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
index 50e7a0395a2a..58d6dae1637a 100644
--- a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
+++ b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
@@ -34,7 +34,7 @@ public class EntropyMixerTest extends AndroidTestCase {
assertEquals(0, FileUtils.readTextFile(file, 0, null).length());
// The constructor has the side effect of writing to file
- new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath(), "/dev/null");
+ new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath());
assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0);
}
diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
index caa46da306fa..d039a9d6bfe1 100644
--- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -18,14 +18,23 @@ package com.android.server.app;
import static org.junit.Assert.assertEquals;
+import android.Manifest;
import android.app.GameManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.HashMap;
+import java.util.function.Supplier;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -37,15 +46,88 @@ public class GameManagerServiceTests {
private static final int USER_ID_1 = 1001;
private static final int USER_ID_2 = 1002;
+ // Stolen from ConnectivityServiceTest.MockContext
+ class MockContext extends ContextWrapper {
+ private static final String TAG = "MockContext";
+
+ // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
+ private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
+
+ MockContext(Context base) {
+ super(base);
+ }
+
+ /**
+ * Mock checks for the specified permission, and have them behave as per {@code granted}.
+ *
+ * <p>Passing null reverts to default behavior, which does a real permission check on the
+ * test package.
+ * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
+ * {@link PackageManager#PERMISSION_DENIED}.
+ */
+ public void setPermission(String permission, Integer granted) {
+ mMockedPermissions.put(permission, granted);
+ }
+
+ private int checkMockedPermission(String permission, Supplier<Integer> ifAbsent) {
+ final Integer granted = mMockedPermissions.get(permission);
+ return granted != null ? granted : ifAbsent.get();
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid) {
+ return checkMockedPermission(
+ permission, () -> super.checkPermission(permission, pid, uid));
+ }
+
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ return checkMockedPermission(
+ permission, () -> super.checkCallingOrSelfPermission(permission));
+ }
+
+ @Override
+ public void enforceCallingOrSelfPermission(String permission, String message) {
+ final Integer granted = mMockedPermissions.get(permission);
+ if (granted == null) {
+ super.enforceCallingOrSelfPermission(permission, message);
+ return;
+ }
+
+ if (!granted.equals(PackageManager.PERMISSION_GRANTED)) {
+ throw new SecurityException("[Test] permission denied: " + permission);
+ }
+ }
+ }
+
+ @Mock
+ private MockContext mMockContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockContext = new MockContext(InstrumentationRegistry.getContext());
+ }
+
+ private void mockModifyGameModeGranted() {
+ mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE,
+ PackageManager.PERMISSION_GRANTED);
+ }
+
+ private void mockModifyGameModeDenied() {
+ mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE,
+ PackageManager.PERMISSION_DENIED);
+ }
+
/**
* By default game mode is not supported.
*/
@Test
public void testGameModeDefaultValue() {
- GameManagerService gameManagerService =
- new GameManagerService(InstrumentationRegistry.getContext());
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
gameManagerService.onUserStarting(USER_ID_1);
+ mockModifyGameModeGranted();
+
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(PACKAGE_NAME_0, USER_ID_1));
}
@@ -55,10 +137,11 @@ public class GameManagerServiceTests {
*/
@Test
public void testDefaultValueForNonexistentUser() {
- GameManagerService gameManagerService =
- new GameManagerService(InstrumentationRegistry.getContext());
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
gameManagerService.onUserStarting(USER_ID_1);
+ mockModifyGameModeGranted();
+
gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_2);
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_2));
@@ -69,10 +152,11 @@ public class GameManagerServiceTests {
*/
@Test
public void testGameMode() {
- GameManagerService gameManagerService =
- new GameManagerService(InstrumentationRegistry.getContext());
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
gameManagerService.onUserStarting(USER_ID_1);
+ mockModifyGameModeGranted();
+
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1);
@@ -83,4 +167,48 @@ public class GameManagerServiceTests {
assertEquals(GameManager.GAME_MODE_PERFORMANCE,
gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
}
+
+ /**
+ * Test permission.MANAGE_GAME_MODE is checked
+ */
+ @Test
+ public void testGetGameModePermissionDenied() {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+
+ // Update the game mode so we can read back something valid.
+ mockModifyGameModeGranted();
+ gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+
+ // Deny permission.MANAGE_GAME_MODE and verify we get back GameManager.GAME_MODE_UNSUPPORTED
+ mockModifyGameModeDenied();
+ assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+ gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ /**
+ * Test permission.MANAGE_GAME_MODE is checked
+ */
+ @Test
+ public void testSetGameModePermissionDenied() {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+
+ // Update the game mode so we can read back something valid.
+ mockModifyGameModeGranted();
+ gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+
+ // Deny permission.MANAGE_GAME_MODE and verify the game mode is not updated.
+ mockModifyGameModeDenied();
+ gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_PERFORMANCE,
+ USER_ID_1);
+
+ mockModifyGameModeGranted();
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index e6a238a775c6..ba6011140cf1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -814,6 +814,7 @@ public class PackageParserTest {
assertArrayEquals(a.splitSourceDirs, that.splitSourceDirs);
assertArrayEquals(a.splitPublicSourceDirs, that.splitPublicSourceDirs);
assertArrayEquals(a.resourceDirs, that.resourceDirs);
+ assertArrayEquals(a.overlayPaths, that.overlayPaths);
assertEquals(a.seInfo, that.seInfo);
assertArrayEquals(a.sharedLibraryFiles, that.sharedLibraryFiles);
assertEquals(a.dataDir, that.dataDir);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 938e4cc84e62..7297622b523f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -21,11 +21,14 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import android.content.pm.PackageManager;
import android.content.pm.PackageUserState;
import android.content.pm.SuspendDialogInfo;
+import android.content.pm.overlay.OverlayPaths;
import android.os.PersistableBundle;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -346,4 +349,40 @@ public class PackageUserStateTest {
assertLastPackageUsageSet(
testState6, PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT - 1, 60L);
}
+
+ @Test
+ public void testOverlayPaths() {
+ final PackageUserState testState = new PackageUserState();
+ assertFalse(testState.setOverlayPaths(null));
+ assertFalse(testState.setOverlayPaths(new OverlayPaths.Builder().build()));
+
+ assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder()
+ .addApkPath("/path/to/some.apk").build()));
+ assertFalse(testState.setOverlayPaths(new OverlayPaths.Builder()
+ .addApkPath("/path/to/some.apk").build()));
+
+ assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder().build()));
+ assertFalse(testState.setOverlayPaths(null));
+ }
+ @Test
+ public void testSharedLibOverlayPaths() {
+ final PackageUserState testState = new PackageUserState();
+ final String LIB_ONE = "lib1";
+ final String LIB_TW0 = "lib2";
+ assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, null));
+ assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE,
+ new OverlayPaths.Builder().build()));
+
+ assertTrue(testState.setSharedLibraryOverlayPaths(LIB_ONE, new OverlayPaths.Builder()
+ .addApkPath("/path/to/some.apk").build()));
+ assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, new OverlayPaths.Builder()
+ .addApkPath("/path/to/some.apk").build()));
+ assertTrue(testState.setSharedLibraryOverlayPaths(LIB_TW0, new OverlayPaths.Builder()
+ .addApkPath("/path/to/some.apk").build()));
+
+ assertTrue(testState.setSharedLibraryOverlayPaths(LIB_ONE,
+ new OverlayPaths.Builder().build()));
+ assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, null));
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 61252c473530..ff0aec71a0df 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -248,6 +248,7 @@ open class AndroidPackageParsingTestBase {
.ignored("Deferred pre-R, but assigned immediately in R")}
requiresSmallestWidthDp=${this.requiresSmallestWidthDp}
resourceDirs=${this.resourceDirs?.contentToString()}
+ overlayPaths=${this.overlayPaths?.contentToString()}
roundIconRes=${this.roundIconRes}
scanPublicSourceDir=${this.scanPublicSourceDir
.ignored("Deferred pre-R, but assigned immediately in R")}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index b3116d970725..17c941179853 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -15,6 +15,9 @@
*/
package com.android.server.notification;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
@@ -138,6 +141,30 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
}
@Test
+ public void testXmlMigratingAllowedAdjustments() throws Exception {
+ // Old tag, need migration
+ String xml = "<q_allowed_adjustments types=\"adj_1\"/>";
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readExtraTag("q_allowed_adjustments", parser);
+ assertTrue(mAssistants.isAdjustmentAllowed("adj_1"));
+ assertEquals(mNm.DEFAULT_ALLOWED_ADJUSTMENTS.length + 1,
+ mAssistants.getAllowedAssistantAdjustments().size());
+
+ // New TAG
+ xml = "<s_allowed_adjustments types=\"adj_2\"/>";
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readExtraTag("s_allowed_adjustments", parser);
+ assertTrue(mAssistants.isAdjustmentAllowed("adj_2"));
+ assertEquals(1, mAssistants.getAllowedAssistantAdjustments().size());
+ }
+
+ @Test
public void testSetPackageOrComponentEnabled_onlyOnePackage() throws Exception {
ComponentName component1 = ComponentName.unflattenFromString("package/Component1");
ComponentName component2 = ComponentName.unflattenFromString("package/Component2");
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0cb1255c6830..9e419d4c3f3b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1526,15 +1526,19 @@ public class UsageStatsService extends SystemService implements
@Override
public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime,
- long endTime, String callingPackage) {
+ long endTime, String callingPackage, int userId) {
if (!hasPermission(callingPackage)) {
return null;
}
+ final int callingUid = Binder.getCallingUid();
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
+ userId, false, true, "queryUsageStats", callingPackage);
+
+ // Check the caller's userId for obfuscation decision, not the user being queried
final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
- Binder.getCallingUid(), UserHandle.getCallingUserId());
+ callingUid, UserHandle.getCallingUserId());
- final int userId = UserHandle.getCallingUserId();
final long token = Binder.clearCallingIdentity();
try {
final List<UsageStats> results = UsageStatsService.this.queryUsageStats(
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ac584c175c59..21cf3e57115d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4748,6 +4748,21 @@ public class CarrierConfigManager {
public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL =
"network_temp_not_metered_supported_bool";
+ /*
+ * Boolean indicating whether the SIM PIN can be stored and verified
+ * seamlessly after an unattended reboot.
+ *
+ * The device configuration value {@code config_allow_pin_storage_for_unattended_reboot}
+ * ultimately controls whether this carrier configuration option is used. Where
+ * {@code config_allow_pin_storage_for_unattended_reboot} is false, the value of the
+ * {@link #KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL} carrier configuration option is
+ * ignored.
+ *
+ * @hide
+ */
+ public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL =
+ "store_sim_pin_for_unattended_reboot_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -5304,6 +5319,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true);
sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0);
+ sDefaults.putBoolean(KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, true);
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 65b8de2b2216..441446cdc48d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -14994,6 +14994,7 @@ public class TelephonyManager {
}
return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR;
}
+
/**
* Registers a listener object to receive notification of changes
* in specified telephony states.
@@ -15350,4 +15351,66 @@ public class TelephonyManager {
return PhoneCapability.DEFAULT_SSSS_CAPABILITY;
}
}
+
+ /**
+ * The unattended reboot was prepared successfully.
+ * @hide
+ */
+ @SystemApi
+ public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0;
+
+ /**
+ * The unattended reboot was prepared, but the user will need to manually
+ * enter the PIN code of at least one SIM card present in the device.
+ * @hide
+ */
+ @SystemApi
+ public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1;
+
+ /**
+ * The unattended reboot was not prepared due to generic error.
+ * @hide
+ */
+ @SystemApi
+ public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"PREPARE_UNATTENDED_REBOOT_"},
+ value = {
+ PREPARE_UNATTENDED_REBOOT_SUCCESS,
+ PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED,
+ PREPARE_UNATTENDED_REBOOT_ERROR
+ })
+ public @interface PrepareUnattendedRebootResult {}
+
+ /**
+ * Prepare TelephonyManager for an unattended reboot. The reboot is required to be done
+ * shortly (e.g. within 15 seconds) after the API is invoked.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#REBOOT}
+ *
+ * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success.
+ * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains
+ * at least one SIM card for which the user needs to manually enter the PIN
+ * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case
+ * of error.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.REBOOT)
+ @PrepareUnattendedRebootResult
+ public int prepareForUnattendedReboot() {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.prepareForUnattendedReboot();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Telephony#prepareForUnattendedReboot RemoteException", e);
+ e.rethrowFromSystemServer();
+ }
+ return PREPARE_UNATTENDED_REBOOT_ERROR;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0cd17da3c0c5..1d049530ba77 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2387,4 +2387,18 @@ interface ITelephony {
* Gets the current phone capability.
*/
PhoneCapability getPhoneCapability();
+
+ /**
+ * Prepare TelephonyManager for an unattended reboot. The reboot is
+ * required to be done shortly after the API is invoked.
+ *
+ * Requires system privileges.
+ *
+ * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success.
+ * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains
+ * at least one SIM card for which the user needs to manually enter the PIN
+ * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case
+ * of error.
+ */
+ int prepareForUnattendedReboot();
}
diff --git a/tests/ApkVerityTest/OWNERS b/tests/ApkVerityTest/OWNERS
new file mode 100644
index 000000000000..d67285ede44a
--- /dev/null
+++ b/tests/ApkVerityTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 36824
+
+victorhsieh@google.com
diff --git a/tools/hiddenapi/Android.bp b/tools/hiddenapi/Android.bp
deleted file mode 100644
index e0eb06cbea7f..000000000000
--- a/tools/hiddenapi/Android.bp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-python_binary_host {
- name: "merge_csv",
- main: "merge_csv.py",
- srcs: ["merge_csv.py"],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true
- },
- },
-}
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
deleted file mode 100755
index 28ff606d0381..000000000000
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ /dev/null
@@ -1,383 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Generate API lists for non-SDK API enforcement."""
-import argparse
-from collections import defaultdict, namedtuple
-import functools
-import os
-import re
-import sys
-
-# Names of flags recognized by the `hiddenapi` tool.
-FLAG_SDK = 'sdk'
-FLAG_UNSUPPORTED = 'unsupported'
-FLAG_BLOCKED = 'blocked'
-FLAG_MAX_TARGET_O = 'max-target-o'
-FLAG_MAX_TARGET_P = 'max-target-p'
-FLAG_MAX_TARGET_Q = 'max-target-q'
-FLAG_MAX_TARGET_R = 'max-target-r'
-FLAG_CORE_PLATFORM_API = 'core-platform-api'
-FLAG_PUBLIC_API = 'public-api'
-FLAG_SYSTEM_API = 'system-api'
-FLAG_TEST_API = 'test-api'
-
-# List of all known flags.
-FLAGS_API_LIST = [
- FLAG_SDK,
- FLAG_UNSUPPORTED,
- FLAG_BLOCKED,
- FLAG_MAX_TARGET_O,
- FLAG_MAX_TARGET_P,
- FLAG_MAX_TARGET_Q,
- FLAG_MAX_TARGET_R,
-]
-ALL_FLAGS = FLAGS_API_LIST + [
- FLAG_CORE_PLATFORM_API,
- FLAG_PUBLIC_API,
- FLAG_SYSTEM_API,
- FLAG_TEST_API,
-]
-
-FLAGS_API_LIST_SET = set(FLAGS_API_LIST)
-ALL_FLAGS_SET = set(ALL_FLAGS)
-
-# Option specified after one of FLAGS_API_LIST to indicate that
-# only known and otherwise unassigned entries should be assign the
-# given flag.
-# For example, the max-target-P list is checked in as it was in P,
-# but signatures have changes since then. The flag instructs this
-# script to skip any entries which do not exist any more.
-FLAG_IGNORE_CONFLICTS = "ignore-conflicts"
-
-# Option specified after one of FLAGS_API_LIST to express that all
-# apis within a given set of packages should be assign the given flag.
-FLAG_PACKAGES = "packages"
-
-# Option specified after one of FLAGS_API_LIST to indicate an extra
-# tag that should be added to the matching APIs.
-FLAG_TAG = "tag"
-
-# Regex patterns of fields/methods used in serialization. These are
-# considered public API despite being hidden.
-SERIALIZATION_PATTERNS = [
- r'readObject\(Ljava/io/ObjectInputStream;\)V',
- r'readObjectNoData\(\)V',
- r'readResolve\(\)Ljava/lang/Object;',
- r'serialVersionUID:J',
- r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
- r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
- r'writeReplace\(\)Ljava/lang/Object;',
-]
-
-# Single regex used to match serialization API. It combines all the
-# SERIALIZATION_PATTERNS into a single regular expression.
-SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
-
-# Predicates to be used with filter_apis.
-HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags)
-IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
-
-
-class StoreOrderedOptions(argparse.Action):
- """An argparse action that stores a number of option arguments in the order that
- they were specified.
- """
- def __call__(self, parser, args, values, option_string = None):
- items = getattr(args, self.dest, None)
- if items is None:
- items = []
- items.append([option_string.lstrip('-'), values])
- setattr(args, self.dest, items)
-
-def get_args():
- """Parses command line arguments.
-
- Returns:
- Namespace: dictionary of parsed arguments
- """
- parser = argparse.ArgumentParser()
- parser.add_argument('--output', required=True)
- parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
- help='CSV files to be merged into output')
-
- for flag in ALL_FLAGS:
- parser.add_argument('--' + flag, dest='ordered_flags', metavar='TXT_FILE',
- action=StoreOrderedOptions, help='lists of entries with flag "' + flag + '"')
- parser.add_argument('--' + FLAG_IGNORE_CONFLICTS, dest='ordered_flags', nargs=0,
- action=StoreOrderedOptions, help='Indicates that only known and otherwise unassigned '
- 'entries should be assign the given flag. Must follow a list of entries and applies '
- 'to the preceding such list.')
- parser.add_argument('--' + FLAG_PACKAGES, dest='ordered_flags', nargs=0,
- action=StoreOrderedOptions, help='Indicates that the previous list of entries '
- 'is a list of packages. All members in those packages will be given the flag. '
- 'Must follow a list of entries and applies to the preceding such list.')
- parser.add_argument('--' + FLAG_TAG, dest='ordered_flags', nargs=1,
- action=StoreOrderedOptions, help='Adds an extra tag to the previous list of entries. '
- 'Must follow a list of entries and applies to the preceding such list.')
-
- return parser.parse_args()
-
-
-def read_lines(filename):
- """Reads entire file and return it as a list of lines.
-
- Lines which begin with a hash are ignored.
-
- Args:
- filename (string): Path to the file to read from.
-
- Returns:
- Lines of the file as a list of string.
- """
- with open(filename, 'r') as f:
- lines = f.readlines();
- lines = filter(lambda line: not line.startswith('#'), lines)
- lines = map(lambda line: line.strip(), lines)
- return set(lines)
-
-
-def write_lines(filename, lines):
- """Writes list of lines into a file, overwriting the file if it exists.
-
- Args:
- filename (string): Path to the file to be writting into.
- lines (list): List of strings to write into the file.
- """
- lines = map(lambda line: line + '\n', lines)
- with open(filename, 'w') as f:
- f.writelines(lines)
-
-
-def extract_package(signature):
- """Extracts the package from a signature.
-
- Args:
- signature (string): JNI signature of a method or field.
-
- Returns:
- The package name of the class containing the field/method.
- """
- full_class_name = signature.split(";->")[0]
- # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy
- if (full_class_name[0] != "L"):
- raise ValueError("Expected to start with 'L': %s" % full_class_name)
- full_class_name = full_class_name[1:]
- # If full_class_name doesn't contain '/', then package_name will be ''.
- package_name = full_class_name.rpartition("/")[0]
- return package_name.replace('/', '.')
-
-
-class FlagsDict:
- def __init__(self):
- self._dict_keyset = set()
- self._dict = defaultdict(set)
-
- def _check_entries_set(self, keys_subset, source):
- assert isinstance(keys_subset, set)
- assert keys_subset.issubset(self._dict_keyset), (
- "Error: {} specifies signatures not present in code:\n"
- "{}"
- "Please visit go/hiddenapi for more information.").format(
- source, "".join(map(lambda x: " " + str(x) + "\n", keys_subset - self._dict_keyset)))
-
- def _check_flags_set(self, flags_subset, source):
- assert isinstance(flags_subset, set)
- assert flags_subset.issubset(ALL_FLAGS_SET), (
- "Error processing: {}\n"
- "The following flags were not recognized: \n"
- "{}\n"
- "Please visit go/hiddenapi for more information.").format(
- source, "\n".join(flags_subset - ALL_FLAGS_SET))
-
- def filter_apis(self, filter_fn):
- """Returns APIs which match a given predicate.
-
- This is a helper function which allows to filter on both signatures (keys) and
- flags (values). The built-in filter() invokes the lambda only with dict's keys.
-
- Args:
- filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
-
- Returns:
- A set of APIs which match the predicate.
- """
- return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
-
- def get_valid_subset_of_unassigned_apis(self, api_subset):
- """Sanitizes a key set input to only include keys which exist in the dictionary
- and have not been assigned any API list flags.
-
- Args:
- entries_subset (set/list): Key set to be sanitized.
-
- Returns:
- Sanitized key set.
- """
- assert isinstance(api_subset, set)
- return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
-
- def generate_csv(self):
- """Constructs CSV entries from a dictionary.
-
- Old versions of flags are used to generate the file.
-
- Returns:
- List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
- """
- lines = []
- for api in self._dict:
- flags = sorted(self._dict[api])
- lines.append(",".join([api] + flags))
- return sorted(lines)
-
- def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
- """Parses CSV entries and merges them into a given dictionary.
-
- The expected CSV format is:
- <api signature>,<flag1>,<flag2>,...,<flagN>
-
- Args:
- csv_lines (list of strings): Lines read from a CSV file.
- source (string): Origin of `csv_lines`. Will be printed in error messages.
-
- Throws:
- AssertionError if parsed flags are invalid.
- """
- # Split CSV lines into arrays of values.
- csv_values = [ line.split(',') for line in csv_lines ]
-
- # Update the full set of API signatures.
- self._dict_keyset.update([ csv[0] for csv in csv_values ])
-
- # Check that all flags are known.
- csv_flags = set()
- for csv in csv_values:
- csv_flags.update(csv[1:])
- self._check_flags_set(csv_flags, source)
-
- # Iterate over all CSV lines, find entry in dict and append flags to it.
- for csv in csv_values:
- flags = csv[1:]
- if (FLAG_PUBLIC_API in flags) or (FLAG_SYSTEM_API in flags):
- flags.append(FLAG_SDK)
- self._dict[csv[0]].update(flags)
-
- def assign_flag(self, flag, apis, source="<unknown>", tag = None):
- """Assigns a flag to given subset of entries.
-
- Args:
- flag (string): One of ALL_FLAGS.
- apis (set): Subset of APIs to receive the flag.
- source (string): Origin of `entries_subset`. Will be printed in error messages.
-
- Throws:
- AssertionError if parsed API signatures of flags are invalid.
- """
- # Check that all APIs exist in the dict.
- self._check_entries_set(apis, source)
-
- # Check that the flag is known.
- self._check_flags_set(set([ flag ]), source)
-
- # Iterate over the API subset, find each entry in dict and assign the flag to it.
- for api in apis:
- self._dict[api].add(flag)
- if tag:
- self._dict[api].add(tag)
-
-
-FlagFile = namedtuple('FlagFile', ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
-
-def parse_ordered_flags(ordered_flags):
- r = []
- currentflag, file, ignore_conflicts, packages, tag = None, None, False, False, None
- for flag_value in ordered_flags:
- flag, value = flag_value[0], flag_value[1]
- if flag in ALL_FLAGS_SET:
- if currentflag:
- r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
- ignore_conflicts, packages, tag = False, False, None
- currentflag = flag
- file = value
- else:
- if currentflag is None:
- raise argparse.ArgumentError('--%s is only allowed after one of %s' % (
- flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
- if flag == FLAG_IGNORE_CONFLICTS:
- ignore_conflicts = True
- elif flag == FLAG_PACKAGES:
- packages = True
- elif flag == FLAG_TAG:
- tag = value[0]
-
-
- if currentflag:
- r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
- return r
-
-
-def main(argv):
- # Parse arguments.
- args = vars(get_args())
- flagfiles = parse_ordered_flags(args['ordered_flags'])
-
- # Initialize API->flags dictionary.
- flags = FlagsDict()
-
- # Merge input CSV files into the dictionary.
- # Do this first because CSV files produced by parsing API stubs will
- # contain the full set of APIs. Subsequent additions from text files
- # will be able to detect invalid entries, and/or filter all as-yet
- # unassigned entries.
- for filename in args["csv"]:
- flags.parse_and_merge_csv(read_lines(filename), filename)
-
- # Combine inputs which do not require any particular order.
- # (1) Assign serialization API to SDK.
- flags.assign_flag(FLAG_SDK, flags.filter_apis(IS_SERIALIZATION))
-
- # (2) Merge text files with a known flag into the dictionary.
- for info in flagfiles:
- if (not info.ignore_conflicts) and (not info.packages):
- flags.assign_flag(info.flag, read_lines(info.file), info.file, info.tag)
-
- # Merge text files where conflicts should be ignored.
- # This will only assign the given flag if:
- # (a) the entry exists, and
- # (b) it has not been assigned any other flag.
- # Because of (b), this must run after all strict assignments have been performed.
- for info in flagfiles:
- if info.ignore_conflicts:
- valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(info.file))
- flags.assign_flag(info.flag, valid_entries, filename, info.tag)
-
- # All members in the specified packages will be assigned the appropriate flag.
- for info in flagfiles:
- if info.packages:
- packages_needing_list = set(read_lines(info.file))
- should_add_signature_to_list = lambda sig,lists: extract_package(
- sig) in packages_needing_list and not lists
- valid_entries = flags.filter_apis(should_add_signature_to_list)
- flags.assign_flag(info.flag, valid_entries, info.file, info.tag)
-
- # Mark all remaining entries as blocked.
- flags.assign_flag(FLAG_BLOCKED, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED))
-
- # Write output.
- write_lines(args["output"], flags.generate_csv())
-
-if __name__ == "__main__":
- main(sys.argv)
diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py
deleted file mode 100755
index 82d117fbbbab..000000000000
--- a/tools/hiddenapi/generate_hiddenapi_lists_test.py
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Unit tests for Hidden API list generation."""
-import unittest
-from generate_hiddenapi_lists import *
-
-class TestHiddenapiListGeneration(unittest.TestCase):
-
- def test_filter_apis(self):
- # Initialize flags so that A and B are put on the whitelist and
- # C, D, E are left unassigned. Try filtering for the unassigned ones.
- flags = FlagsDict()
- flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B,' + FLAG_SDK,
- 'C', 'D', 'E'])
- filter_set = flags.filter_apis(lambda api, flags: not flags)
- self.assertTrue(isinstance(filter_set, set))
- self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
-
- def test_get_valid_subset_of_unassigned_keys(self):
- # Create flags where only A is unassigned.
- flags = FlagsDict()
- flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B', 'C'])
- flags.assign_flag(FLAG_UNSUPPORTED, set(['C']))
- self.assertEqual(flags.generate_csv(),
- [ 'A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED ])
-
- # Check three things:
- # (1) B is selected as valid unassigned
- # (2) A is not selected because it is assigned 'whitelist'
- # (3) D is not selected because it is not a valid key
- self.assertEqual(
- flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
-
- def test_parse_and_merge_csv(self):
- flags = FlagsDict()
-
- # Test empty CSV entry.
- self.assertEqual(flags.generate_csv(), [])
-
- # Test new additions.
- flags.parse_and_merge_csv([
- 'A,' + FLAG_UNSUPPORTED,
- 'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O,
- 'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
- 'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
- 'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
- ])
- self.assertEqual(flags.generate_csv(), [
- 'A,' + FLAG_UNSUPPORTED,
- 'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O,
- 'C,' + FLAG_SYSTEM_API + ',' + FLAG_SDK,
- 'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
- 'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
- ])
-
- # Test unknown flag.
- with self.assertRaises(AssertionError):
- flags.parse_and_merge_csv([ 'Z,foo' ])
-
- def test_assign_flag(self):
- flags = FlagsDict()
- flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B'])
-
- # Test new additions.
- flags.assign_flag(FLAG_UNSUPPORTED, set([ 'A', 'B' ]))
- self.assertEqual(flags.generate_csv(),
- [ 'A,' + FLAG_UNSUPPORTED + "," + FLAG_SDK, 'B,' + FLAG_UNSUPPORTED ])
-
- # Test invalid API signature.
- with self.assertRaises(AssertionError):
- flags.assign_flag(FLAG_SDK, set([ 'C' ]))
-
- # Test invalid flag.
- with self.assertRaises(AssertionError):
- flags.assign_flag('foo', set([ 'A' ]))
-
- def test_extract_package(self):
- signature = 'Lcom/foo/bar/Baz;->method1()Lcom/bar/Baz;'
- expected_package = 'com.foo.bar'
- self.assertEqual(extract_package(signature), expected_package)
-
- signature = 'Lcom/foo1/bar/MyClass;->method2()V'
- expected_package = 'com.foo1.bar'
- self.assertEqual(extract_package(signature), expected_package)
-
- signature = 'Lcom/foo_bar/baz/MyClass;->method3()V'
- expected_package = 'com.foo_bar.baz'
- self.assertEqual(extract_package(signature), expected_package)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py
deleted file mode 100755
index 6a5b0e1347b2..000000000000
--- a/tools/hiddenapi/merge_csv.py
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-Merge multiple CSV files, possibly with different columns.
-"""
-
-import argparse
-import csv
-import io
-
-from zipfile import ZipFile
-
-args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
-args_parser.add_argument('--header', help='Comma separated field names; '
- 'if missing determines the header from input files.')
-args_parser.add_argument('--zip_input', help='ZIP archive with all CSV files to merge.')
-args_parser.add_argument('--output', help='Output file for merged CSV.',
- default='-', type=argparse.FileType('w'))
-args_parser.add_argument('files', nargs=argparse.REMAINDER)
-args = args_parser.parse_args()
-
-
-def dict_reader(input):
- return csv.DictReader(input, delimiter=',', quotechar='|')
-
-
-if args.zip_input and len(args.files) > 0:
- raise ValueError('Expecting either a single ZIP with CSV files'
- ' or a list of CSV files as input; not both.')
-
-csv_readers = []
-if len(args.files) > 0:
- for file in args.files:
- csv_readers.append(dict_reader(open(file, 'r')))
-elif args.zip_input:
- with ZipFile(args.zip_input) as zip:
- for entry in zip.namelist():
- if entry.endswith('.uau'):
- csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
-
-headers = set()
-if args.header:
- fieldnames = args.header.split(',')
-else:
- # Build union of all columns from source files:
- for reader in csv_readers:
- headers = headers.union(reader.fieldnames)
- fieldnames = sorted(headers)
-
-# Concatenate all files to output:
-writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
- dialect='unix', fieldnames=fieldnames)
-writer.writeheader()
-for reader in csv_readers:
- for row in reader:
- writer.writerow(row)