Merge "Wrap some calls out of OomAdjuster with the injector" into main
diff --git a/apct-tests/perftests/core/AndroidTest.xml b/apct-tests/perftests/core/AndroidTest.xml
index 86f41e1..c2d5470 100644
--- a/apct-tests/perftests/core/AndroidTest.xml
+++ b/apct-tests/perftests/core/AndroidTest.xml
@@ -17,6 +17,17 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-metric-instrumentation" />
+ // Deal with Play Protect blocking apk installations.
+ // The first setting disables the verification, the second one lowers the timeout from
+ // 1hr to 10s, the third one resets the value after the test is complete, and the final
+ // setting skips the device reboot after modifying the settings.
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+ <option name="set-global-setting" key="verifier_engprod" value="1" />
+ <option name="restore-settings" value="true" />
+ <option name="force-skip-system-props" value="true" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
diff --git a/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
index fcb13a8..a12121f 100644
--- a/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
@@ -37,9 +37,13 @@
import org.junit.Test;
import java.util.ArrayList;
-import java.util.concurrent.Executor;
-import java.util.concurrent.FutureTask;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
/**
* Benchmarks for {@link android.content.om.OverlayManager}.
@@ -49,7 +53,6 @@
private static final int OVERLAY_PKG_COUNT = 10;
private static Context sContext;
private static OverlayManager sOverlayManager;
- private static Executor sExecutor;
private static ArrayList<TestPackageInstaller.InstalledPackage> sSmallOverlays =
new ArrayList<>();
private static ArrayList<TestPackageInstaller.InstalledPackage> sLargeOverlays =
@@ -58,18 +61,45 @@
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ // Uncheck the checked exceptions in a callable for convenient stream usage.
+ // Any exception will fail the test anyway.
+ private static <T> T uncheck(Callable<T> c) {
+ try {
+ return c.call();
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
@BeforeClass
public static void classSetUp() throws Exception {
sContext = InstrumentationRegistry.getTargetContext();
sOverlayManager = new OverlayManager(sContext);
- sExecutor = (command) -> new Thread(command).start();
- // Install all of the test overlays.
- TestPackageInstaller installer = new TestPackageInstaller(sContext);
+ // Install all of the test overlays. Play Protect likes to block these for 10 sec each
+ // so let's install them in parallel to speed up the wait.
+ final var installer = new TestPackageInstaller(sContext);
+ final var es = Executors.newFixedThreadPool(2 * OVERLAY_PKG_COUNT);
+ final var smallFutures = new ArrayList<Future<TestPackageInstaller.InstalledPackage>>(
+ OVERLAY_PKG_COUNT);
+ final var largeFutures = new ArrayList<Future<TestPackageInstaller.InstalledPackage>>(
+ OVERLAY_PKG_COUNT);
for (int i = 0; i < OVERLAY_PKG_COUNT; i++) {
- sSmallOverlays.add(installer.installPackage("Overlay" + i +".apk"));
- sLargeOverlays.add(installer.installPackage("LargeOverlay" + i +".apk"));
+ final var index = i;
+ smallFutures.add(es.submit(() -> installer.installPackage("Overlay" + index + ".apk")));
+ largeFutures.add(
+ es.submit(() -> installer.installPackage("LargeOverlay" + index + ".apk")));
}
+ es.shutdown();
+ assertTrue(es.awaitTermination(15 * 2 * OVERLAY_PKG_COUNT, TimeUnit.SECONDS));
+ sSmallOverlays.addAll(smallFutures.stream().map(f -> uncheck(f::get)).sorted(
+ Comparator.comparing(
+ TestPackageInstaller.InstalledPackage::getPackageName)).toList());
+ sLargeOverlays.addAll(largeFutures.stream().map(f -> uncheck(f::get)).sorted(
+ Comparator.comparing(
+ TestPackageInstaller.InstalledPackage::getPackageName)).toList());
}
@AfterClass
@@ -77,7 +107,6 @@
for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
overlay.uninstall();
}
-
for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
overlay.uninstall();
}
@@ -86,37 +115,39 @@
@After
public void tearDown() throws Exception {
// Disable all test overlays after each test.
- for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
- assertSetEnabled(sContext, overlay.getPackageName(), false);
- }
-
- for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
- assertSetEnabled(sContext, overlay.getPackageName(), false);
- }
+ assertSetEnabled(false, sContext,
+ Stream.concat(sSmallOverlays.stream(), sLargeOverlays.stream()).map(
+ p -> p.getPackageName()));
}
/**
- * Enables the overlay and waits for the APK path change sto be propagated to the context
+ * Enables the overlay and waits for the APK path changes to be propagated to the context
* AssetManager.
*/
- private void assertSetEnabled(Context context, String overlayPackage, boolean eanabled)
- throws Exception {
- sOverlayManager.setEnabled(overlayPackage, true, UserHandle.SYSTEM);
+ private void assertSetEnabled(boolean enabled, Context context, Stream<String> packagesStream) {
+ final var overlayPackages = packagesStream.toList();
+ overlayPackages.forEach(
+ name -> sOverlayManager.setEnabled(name, enabled, UserHandle.SYSTEM));
// Wait for the overlay changes to propagate
- FutureTask<Boolean> task = new FutureTask<>(() -> {
- while (true) {
- for (String path : context.getAssets().getApkPaths()) {
- if (eanabled == path.contains(overlayPackage)) {
- return true;
- }
- }
+ final var endTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(20);
+ final var expectedPackagesFound = enabled ? overlayPackages.size() : 0;
+ boolean assetsUpdated = false;
+ do {
+ final var packagesFound = Arrays.stream(context.getAssets().getApkPaths()).filter(
+ assetPath -> overlayPackages.stream().anyMatch(assetPath::contains)).count();
+ if (packagesFound == expectedPackagesFound) {
+ assetsUpdated = true;
+ break;
}
- });
+ Thread.yield();
+ } while (System.nanoTime() < endTime);
+ assertTrue("Failed to set state to " + enabled + " for overlays " + overlayPackages,
+ assetsUpdated);
+ }
- sExecutor.execute(task);
- assertTrue("Failed to load overlay " + overlayPackage,
- task.get(20, TimeUnit.SECONDS));
+ private void assertSetEnabled(boolean enabled, Context context, String overlayPackage) {
+ assertSetEnabled(enabled, context, Stream.of(overlayPackage));
}
@Test
@@ -124,11 +155,11 @@
String packageName = sSmallOverlays.get(0).getPackageName();
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- assertSetEnabled(sContext, packageName, true);
+ assertSetEnabled(true, sContext, packageName);
// Disable the overlay for the next iteration of the test
state.pauseTiming();
- assertSetEnabled(sContext, packageName, false);
+ assertSetEnabled(false, sContext, packageName);
state.resumeTiming();
}
}
@@ -138,11 +169,11 @@
String packageName = sSmallOverlays.get(0).getPackageName();
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- assertSetEnabled(sContext, packageName, true);
+ assertSetEnabled(true, sContext, packageName);
// Disable the overlay and remove the idmap for the next iteration of the test
state.pauseTiming();
- assertSetEnabled(sContext, packageName, false);
+ assertSetEnabled(false, sContext, packageName);
sOverlayManager.invalidateCachesForOverlay(packageName, UserHandle.SYSTEM);
state.resumeTiming();
}
@@ -153,11 +184,11 @@
String packageName = sLargeOverlays.get(0).getPackageName();
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- assertSetEnabled(sContext, packageName, true);
+ assertSetEnabled(true, sContext, packageName);
// Disable the overlay and remove the idmap for the next iteration of the test
state.pauseTiming();
- assertSetEnabled(sContext, packageName, false);
+ assertSetEnabled(false, sContext, packageName);
sOverlayManager.invalidateCachesForOverlay(packageName, UserHandle.SYSTEM);
state.resumeTiming();
}
@@ -169,30 +200,28 @@
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- assertSetEnabled(sContext, packageName, true);
+ assertSetEnabled(true, sContext, packageName);
state.resumeTiming();
- assertSetEnabled(sContext, packageName, false);
+ assertSetEnabled(false, sContext, packageName);
}
}
@Test
public void getStringOneSmallOverlay() throws Exception {
String packageName = sSmallOverlays.get(0).getPackageName();
- assertSetEnabled(sContext, packageName, true);
+ assertSetEnabled(true, sContext, packageName);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
sContext.getString(R.string.short_text);
}
-
- assertSetEnabled(sContext, packageName, false);
}
@Test
public void getStringOneLargeOverlay() throws Exception {
String packageName = sLargeOverlays.get(0).getPackageName();
- assertSetEnabled(sContext, packageName, true);
+ assertSetEnabled(true, sContext, packageName);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
@@ -200,16 +229,12 @@
sContext.getString(resId);
}
}
-
- assertSetEnabled(sContext, packageName, false);
}
@Test
public void getStringTenOverlays() throws Exception {
- // Enable all test overlays
- for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
- assertSetEnabled(sContext, overlay.getPackageName(), true);
- }
+ // Enable all small test overlays
+ assertSetEnabled(true, sContext, sSmallOverlays.stream().map(p -> p.getPackageName()));
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
@@ -219,10 +244,8 @@
@Test
public void getStringLargeTenOverlays() throws Exception {
- // Enable all test overlays
- for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
- assertSetEnabled(sContext, overlay.getPackageName(), true);
- }
+ // Enable all large test overlays
+ assertSetEnabled(true, sContext, sLargeOverlays.stream().map(p -> p.getPackageName()));
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 4f2efa4..cb2b8ad 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -18,7 +18,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.app.admin.flags.Flags;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -388,11 +387,8 @@
}
mSupportsTransferOwnership = true;
} else if (tagName.equals("headless-system-user")) {
- String deviceOwnerModeStringValue = null;
- if (Flags.headlessSingleUserCompatibilityFix()) {
- deviceOwnerModeStringValue = parser.getAttributeValue(
- null, "headless-device-owner-mode");
- }
+ String deviceOwnerModeStringValue = parser.getAttributeValue(
+ null, "headless-device-owner-mode");
if (deviceOwnerModeStringValue == null) {
deviceOwnerModeStringValue =
parser.getAttributeValue(null, "device-owner-mode");
@@ -405,13 +401,8 @@
} else if ("single_user".equalsIgnoreCase(deviceOwnerModeStringValue)) {
mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
} else {
- if (Flags.headlessSingleUserCompatibilityFix()) {
- Log.e(TAG, "Unknown headless-system-user mode: "
- + deviceOwnerModeStringValue);
- } else {
- throw new XmlPullParserException(
- "headless-system-user mode must be valid");
- }
+ Log.e(TAG, "Unknown headless-system-user mode: "
+ + deviceOwnerModeStringValue);
}
}
}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index faa5a91..9ed5aa6 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -83,13 +83,6 @@
}
flag {
- name: "coexistence_migration_for_non_emm_management_enabled"
- namespace: "enterprise"
- description: "Migrate existing APIs to be coexistable, and enable DMRH to call them to support non-EMM device management."
- bug: "289520697"
-}
-
-flag {
name: "coexistence_migration_for_supervision_enabled"
is_exported: true
namespace: "enterprise"
@@ -163,16 +156,6 @@
}
flag {
- name: "hsum_unlock_notification_fix"
- namespace: "enterprise"
- description: "Using the right userId when starting the work profile unlock flow "
- bug: "327350831"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "allow_querying_profile_type"
is_exported: true
namespace: "enterprise"
@@ -239,16 +222,6 @@
}
flag {
- name: "copy_account_with_retry_enabled"
- namespace: "enterprise"
- description: "Retry copy and remove account from personal to work profile in case of failure"
- bug: "329424312"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "disallow_user_control_stopped_state_fix"
namespace: "enterprise"
description: "Ensure DPM.setUserControlDisabledPackages() clears FLAG_STOPPED for the app"
@@ -266,16 +239,6 @@
}
flag {
- name: "always_persist_do"
- namespace: "enterprise"
- description: "Always write device_owners2.xml so that migration flags aren't lost"
- bug: "335232744"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "is_recursive_required_app_merging_enabled"
namespace: "enterprise"
description: "Guards a new flow for recursive required enterprise app list merging"
@@ -283,16 +246,6 @@
}
flag {
- name: "headless_single_user_bad_device_admin_state_fix"
- namespace: "enterprise"
- description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change"
- bug: "332477138"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "onboarding_bugreport_storage_bug_fix"
namespace: "enterprise"
description: "Add a separate storage limit for deferred bugreports"
@@ -320,16 +273,6 @@
}
flag {
- name: "headless_single_user_compatibility_fix"
- namespace: "enterprise"
- description: "Fix for compatibility issue introduced from using single_user mode on pre-Android V builds"
- bug: "338050276"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "headless_single_min_target_sdk"
namespace: "enterprise"
description: "Only allow DPCs targeting Android V to provision into single user mode"
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index 8f609de..4682f3d 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -50,7 +50,7 @@
* Creates an instance.
*
* @param service An interface to the backing service.
- * @param context A {@link Context}.
+ * @param context A {@link Context}.
* @hide
*/
public AppFunctionManager(IAppFunctionManager service, Context context) {
@@ -60,42 +60,42 @@
/**
* Executes the app function.
- * <p>
- * Note: Applications can execute functions they define. To execute functions defined in
- * another component, apps would need to have
- * {@code android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or
- * {@code android.permission.EXECUTE_APP_FUNCTIONS}.
*
- * @param request the request to execute the app function
+ * <p>Note: Applications can execute functions they define. To execute functions defined in
+ * another component, apps would need to have {@code
+ * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
+ * android.permission.EXECUTE_APP_FUNCTIONS}.
+ *
+ * @param request the request to execute the app function
* @param executor the executor to run the callback
* @param callback the callback to receive the function execution result. if the calling app
- * does not own the app function or does not have {@code
- * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
- * android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain
- * {@code ExecuteAppFunctionResponse.RESULT_DENIED}.
+ * does not own the app function or does not have {@code
+ * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
+ * android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code
+ * ExecuteAppFunctionResponse.RESULT_DENIED}.
*/
// TODO(b/360864791): Document that apps can opt-out from being executed by callers with
// EXECUTE_APP_FUNCTIONS and how a caller knows whether a function is opted out.
// TODO(b/357551503): Update documentation when get / set APIs are implemented that this will
// also return RESULT_DENIED if the app function is disabled.
@RequiresPermission(
- anyOf = {Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
- Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional = true)
+ anyOf = {
+ Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+ Manifest.permission.EXECUTE_APP_FUNCTIONS
+ },
+ conditional = true)
@UserHandleAware
public void executeAppFunction(
@NonNull ExecuteAppFunctionRequest request,
@NonNull @CallbackExecutor Executor executor,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback
- ) {
+ @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
Objects.requireNonNull(request);
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
ExecuteAppFunctionAidlRequest aidlRequest =
new ExecuteAppFunctionAidlRequest(
- request,
- mContext.getUser(),
- mContext.getPackageName());
+ request, mContext.getUser(), mContext.getPackageName());
try {
mService.executeAppFunction(
aidlRequest,
@@ -107,8 +107,11 @@
} catch (RuntimeException e) {
// Ideally shouldn't happen since errors are wrapped into the
// response, but we catch it here for additional safety.
- callback.accept(ExecuteAppFunctionResponse.newFailure(
- getResultCode(e), e.getMessage(), /*extras=*/ null));
+ callback.accept(
+ ExecuteAppFunctionResponse.newFailure(
+ getResultCode(e),
+ e.getMessage(),
+ /* extras= */ null));
}
}
});
diff --git a/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java b/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java
index e4784b4..fa77e79 100644
--- a/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java
+++ b/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java
@@ -23,8 +23,8 @@
import android.content.pm.PackageManager;
/**
- * Represents the system configuration of support for the {@code AppFunctionManager} and
- * associated systems.
+ * Represents the system configuration of support for the {@code AppFunctionManager} and associated
+ * systems.
*
* @hide
*/
@@ -33,6 +33,7 @@
/**
* Constructs a new instance of {@code AppFunctionManagerConfiguration}.
+ *
* @param context context
*/
public AppFunctionManagerConfiguration(@NonNull final Context context) {
@@ -41,15 +42,16 @@
/**
* Indicates whether the current target is intended to support {@code AppFunctionManager}.
+ *
* @return {@code true} if supported; otherwise {@code false}
*/
public boolean isSupported() {
return enableAppFunctionManager() && !isWatch();
-
}
/**
* Indicates whether the current target is intended to support {@code AppFunctionManager}.
+ *
* @param context context
* @return {@code true} if supported; otherwise {@code false}
*/
diff --git a/core/java/android/app/appfunctions/AppFunctionManagerHelper.java b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
index 3169f0e..d6f45e4 100644
--- a/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
+++ b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
@@ -49,25 +49,25 @@
/**
* Returns (through a callback) a boolean indicating whether the app function is enabled.
- * <p>
- * This method can only check app functions that are owned by the caller owned by packages
+ *
+ * <p>This method can only check app functions that are owned by the caller owned by packages
* visible to the caller.
- * <p>
- * If operation fails, the callback's {@link OutcomeReceiver#onError} is called with errors:
+ *
+ * <p>If operation fails, the callback's {@link OutcomeReceiver#onError} is called with errors:
+ *
* <ul>
- * <li>{@link IllegalArgumentException}, if the function is not found</li>
- * <li>{@link SecurityException}, if the caller does not have permission to query the
- * target package
- * </li>
- * </ul>
+ * <li>{@link IllegalArgumentException}, if the function is not found
+ * <li>{@link SecurityException}, if the caller does not have permission to query the target
+ * package
+ * </ul>
*
* @param functionIdentifier the identifier of the app function to check (unique within the
- * target package) and in most cases, these are automatically
- * generated by the AppFunctions SDK
- * @param targetPackage the package name of the app function's owner
- * @param appSearchExecutor the executor to run the metadata search mechanism through AppSearch
- * @param callbackExecutor the executor to run the callback
- * @param callback the callback to receive the function enabled check result
+ * target package) and in most cases, these are automatically generated by the AppFunctions
+ * SDK
+ * @param targetPackage the package name of the app function's owner
+ * @param appSearchExecutor the executor to run the metadata search mechanism through AppSearch
+ * @param callbackExecutor the executor to run the callback
+ * @param callback the callback to receive the function enabled check result
* @hide
*/
public static void isAppFunctionEnabled(
@@ -76,8 +76,7 @@
@NonNull AppSearchManager appSearchManager,
@NonNull Executor appSearchExecutor,
@NonNull @CallbackExecutor Executor callbackExecutor,
- @NonNull OutcomeReceiver<Boolean, Exception> callback
- ) {
+ @NonNull OutcomeReceiver<Boolean, Exception> callback) {
Objects.requireNonNull(functionIdentifier);
Objects.requireNonNull(targetPackage);
Objects.requireNonNull(appSearchManager);
@@ -85,27 +84,37 @@
Objects.requireNonNull(callbackExecutor);
Objects.requireNonNull(callback);
- appSearchManager.createGlobalSearchSession(appSearchExecutor,
+ appSearchManager.createGlobalSearchSession(
+ appSearchExecutor,
(searchSessionResult) -> {
if (!searchSessionResult.isSuccess()) {
- callbackExecutor.execute(() ->
- callback.onError(failedResultToException(searchSessionResult)));
+ callbackExecutor.execute(
+ () ->
+ callback.onError(
+ failedResultToException(searchSessionResult)));
return;
}
try (GlobalSearchSession searchSession = searchSessionResult.getResultValue()) {
- SearchResults results = searchJoinedStaticWithRuntimeAppFunctions(
- searchSession, targetPackage, functionIdentifier);
- results.getNextPage(appSearchExecutor,
- listAppSearchResult -> callbackExecutor.execute(() -> {
- if (listAppSearchResult.isSuccess()) {
- callback.onResult(getEnabledStateFromSearchResults(
- Objects.requireNonNull(
- listAppSearchResult.getResultValue())));
- } else {
- callback.onError(
- failedResultToException(listAppSearchResult));
- }
- }));
+ SearchResults results =
+ searchJoinedStaticWithRuntimeAppFunctions(
+ searchSession, targetPackage, functionIdentifier);
+ results.getNextPage(
+ appSearchExecutor,
+ listAppSearchResult ->
+ callbackExecutor.execute(
+ () -> {
+ if (listAppSearchResult.isSuccess()) {
+ callback.onResult(
+ getEnabledStateFromSearchResults(
+ Objects.requireNonNull(
+ listAppSearchResult
+ .getResultValue())));
+ } else {
+ callback.onError(
+ failedResultToException(
+ listAppSearchResult));
+ }
+ }));
} catch (Exception e) {
callbackExecutor.execute(() -> callback.onError(e));
}
@@ -122,27 +131,27 @@
@NonNull GlobalSearchSession session,
@NonNull String targetPackage,
@NonNull String functionIdentifier) {
- SearchSpec runtimeSearchSpec = getAppFunctionRuntimeMetadataSearchSpecByFunctionId(
- targetPackage);
- JoinSpec joinSpec = new JoinSpec.Builder(
- PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID)
- .setNestedSearch(
- functionIdentifier,
- runtimeSearchSpec).build();
- SearchSpec joinedStaticWithRuntimeSearchSpec = new SearchSpec.Builder()
- .setJoinSpec(joinSpec)
- .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
- .addFilterSchemas(
- AppFunctionStaticMetadataHelper.getStaticSchemaNameForPackage(
- targetPackage))
- .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
- .build();
+ SearchSpec runtimeSearchSpec =
+ getAppFunctionRuntimeMetadataSearchSpecByFunctionId(targetPackage);
+ JoinSpec joinSpec =
+ new JoinSpec.Builder(PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID)
+ .setNestedSearch(functionIdentifier, runtimeSearchSpec)
+ .build();
+ SearchSpec joinedStaticWithRuntimeSearchSpec =
+ new SearchSpec.Builder()
+ .setJoinSpec(joinSpec)
+ .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
+ .addFilterSchemas(
+ AppFunctionStaticMetadataHelper.getStaticSchemaNameForPackage(
+ targetPackage))
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .build();
return session.search(functionIdentifier, joinedStaticWithRuntimeSearchSpec);
}
/**
- * Finds whether the function is enabled or not from the search results returned by
- * {@link #searchJoinedStaticWithRuntimeAppFunctions}.
+ * Finds whether the function is enabled or not from the search results returned by {@link
+ * #searchJoinedStaticWithRuntimeAppFunctions}.
*
* @throws IllegalArgumentException if the function is not found in the results
* @hide
@@ -156,15 +165,20 @@
List<SearchResult> runtimeMetadataResults =
joinedStaticRuntimeResults.getFirst().getJoinedResults();
if (!runtimeMetadataResults.isEmpty()) {
- Boolean result = (Boolean) runtimeMetadataResults
- .getFirst().getGenericDocument()
- .getProperty(PROPERTY_ENABLED);
+ Boolean result =
+ (Boolean)
+ runtimeMetadataResults
+ .getFirst()
+ .getGenericDocument()
+ .getProperty(PROPERTY_ENABLED);
if (result != null) {
return result;
}
}
// Runtime metadata not found. Using the default value in the static metadata.
- return joinedStaticRuntimeResults.getFirst().getGenericDocument()
+ return joinedStaticRuntimeResults
+ .getFirst()
+ .getGenericDocument()
.getPropertyBoolean(STATIC_PROPERTY_ENABLED_BY_DEFAULT);
}
}
@@ -180,11 +194,9 @@
return new SearchSpec.Builder()
.addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
.addFilterSchemas(
- AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage(
- targetPackage))
+ AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage(targetPackage))
.addFilterProperties(
- AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage(
- targetPackage),
+ AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage(targetPackage),
List.of(AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID))
.setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
.build();
@@ -198,12 +210,12 @@
public static @NonNull Exception failedResultToException(
@NonNull AppSearchResult appSearchResult) {
return switch (appSearchResult.getResultCode()) {
- case AppSearchResult.RESULT_INVALID_ARGUMENT -> new IllegalArgumentException(
- appSearchResult.getErrorMessage());
- case AppSearchResult.RESULT_IO_ERROR -> new IOException(
- appSearchResult.getErrorMessage());
- case AppSearchResult.RESULT_SECURITY_ERROR -> new SecurityException(
- appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_INVALID_ARGUMENT ->
+ new IllegalArgumentException(appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_IO_ERROR ->
+ new IOException(appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_SECURITY_ERROR ->
+ new SecurityException(appSearchResult.getErrorMessage());
default -> new IllegalStateException(appSearchResult.getErrorMessage());
};
}
diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
index fdd12b0..c4e8b41 100644
--- a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
+++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
@@ -56,16 +56,12 @@
super(genericDocument);
}
- /**
- * Returns a per-app runtime metadata schema name, to store all functions for that package.
- */
+ /** Returns a per-app runtime metadata schema name, to store all functions for that package. */
public static String getRuntimeSchemaNameForPackage(@NonNull String pkg) {
return RUNTIME_SCHEMA_TYPE + RUNTIME_SCHEMA_TYPE_SEPARATOR + Objects.requireNonNull(pkg);
}
- /**
- * Returns the document id for an app function's runtime metadata.
- */
+ /** Returns the document id for an app function's runtime metadata. */
public static String getDocumentIdForAppFunction(
@NonNull String pkg, @NonNull String functionId) {
return pkg + "/" + functionId;
@@ -74,6 +70,7 @@
/**
* Different packages have different visibility requirements. To allow for different visibility,
* we need to have per-package app function schemas.
+ *
* <p>This schema should be set visible to callers from the package owner itself and for callers
* with {@link android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@link
* android.permission.EXECUTE_APP_FUNCTIONS} permissions.
@@ -109,7 +106,7 @@
.build())
.addProperty(
new AppSearchSchema.StringPropertyConfig.Builder(
- PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID)
+ PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setJoinableValueType(
AppSearchSchema.StringPropertyConfig
@@ -119,17 +116,13 @@
.build();
}
- /**
- * Returns the function id. This might look like "com.example.message#send_message".
- */
+ /** Returns the function id. This might look like "com.example.message#send_message". */
@NonNull
public String getFunctionId() {
return Objects.requireNonNull(getPropertyString(PROPERTY_FUNCTION_ID));
}
- /**
- * Returns the package name of the package that owns this function.
- */
+ /** Returns the package name of the package that owns this function. */
@NonNull
public String getPackageName() {
return Objects.requireNonNull(getPropertyString(PROPERTY_PACKAGE_NAME));
@@ -144,9 +137,7 @@
return (Boolean) getProperty(PROPERTY_ENABLED);
}
- /**
- * Returns the qualified id linking to the static metadata of the app function.
- */
+ /** Returns the qualified id linking to the static metadata of the app function. */
@Nullable
@VisibleForTesting
public String getAppFunctionStaticMetadataQualifiedId() {
@@ -157,10 +148,10 @@
/**
* Creates a Builder for a {@link AppFunctionRuntimeMetadata}.
*
- * @param packageName the name of the package that owns the function.
- * @param functionId the id of the function.
+ * @param packageName the name of the package that owns the function.
+ * @param functionId the id of the function.
* @param staticMetadataQualifiedId the qualified static metadata id that this runtime
- * metadata refers to.
+ * metadata refers to.
*/
public Builder(
@NonNull String packageName,
@@ -177,11 +168,9 @@
// Set qualified id automatically
setPropertyString(
- PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID,
- staticMetadataQualifiedId);
+ PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID, staticMetadataQualifiedId);
}
-
/**
* Sets an indicator specifying if the function is enabled or not. This would override the
* default enabled state in the static metadata ({@link
@@ -193,9 +182,7 @@
return this;
}
- /**
- * Creates the {@link AppFunctionRuntimeMetadata} GenericDocument.
- */
+ /** Creates the {@link AppFunctionRuntimeMetadata} GenericDocument. */
@NonNull
public AppFunctionRuntimeMetadata build() {
return new AppFunctionRuntimeMetadata(super.build());
diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java
index 22bc908..c27141a 100644
--- a/core/java/android/app/appfunctions/AppFunctionService.java
+++ b/core/java/android/app/appfunctions/AppFunctionService.java
@@ -58,8 +58,7 @@
* applications can not abuse it.
*/
@NonNull
- public static final String SERVICE_INTERFACE =
- "android.app.appfunctions.AppFunctionService";
+ public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
private final Binder mBinder =
new IAppFunctionService.Stub() {
@@ -67,23 +66,20 @@
public void executeAppFunction(
@NonNull ExecuteAppFunctionRequest request,
@NonNull IExecuteAppFunctionCallback callback) {
- if (AppFunctionService.this.checkCallingPermission(
- BIND_APP_FUNCTION_SERVICE) == PERMISSION_DENIED) {
+ if (AppFunctionService.this.checkCallingPermission(BIND_APP_FUNCTION_SERVICE)
+ == PERMISSION_DENIED) {
throw new SecurityException("Can only be called by the system server.");
}
SafeOneTimeExecuteAppFunctionCallback safeCallback =
new SafeOneTimeExecuteAppFunctionCallback(callback);
try {
- AppFunctionService.this.onExecuteFunction(
- request,
- safeCallback::onResult);
+ AppFunctionService.this.onExecuteFunction(request, safeCallback::onResult);
} catch (Exception ex) {
// Apps should handle exceptions. But if they don't, report the error on
// behalf of them.
safeCallback.onResult(
ExecuteAppFunctionResponse.newFailure(
- getResultCode(ex),
- ex.getMessage(), /*extras=*/ null));
+ getResultCode(ex), ex.getMessage(), /* extras= */ null));
}
}
};
@@ -111,7 +107,7 @@
* thread and dispatch the result with the given callback. You should always report back the
* result using the callback, no matter if the execution was successful or not.
*
- * @param request The function execution request.
+ * @param request The function execution request.
* @param callback A callback to report back the result.
*/
@MainThread
diff --git a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
index 6d4172a..926cc9a 100644
--- a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
+++ b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
@@ -27,9 +27,9 @@
/**
* Contains constants and helper related to static metadata represented with {@code
* com.android.server.appsearch.appsindexer.appsearchtypes.AppFunctionStaticMetadata}.
- * <p>
- * The constants listed here **must not change** and be kept consistent with the canonical
- * static metadata class.
+ *
+ * <p>The constants listed here **must not change** and be kept consistent with the canonical static
+ * metadata class.
*
* @hide
*/
@@ -45,9 +45,7 @@
public static final String APP_FUNCTION_STATIC_METADATA_DB = "apps-db";
public static final String APP_FUNCTION_INDEXER_PACKAGE = "android";
- /**
- * Returns a per-app static metadata schema name, to store all functions for that package.
- */
+ /** Returns a per-app static metadata schema name, to store all functions for that package. */
public static String getStaticSchemaNameForPackage(@NonNull String pkg) {
return STATIC_SCHEMA_TYPE + "-" + Objects.requireNonNull(pkg);
}
@@ -59,8 +57,8 @@
}
/**
- * Returns the fully qualified Id used in AppSearch for the given package and function id
- * app function static metadata.
+ * Returns the fully qualified Id used in AppSearch for the given package and function id app
+ * function static metadata.
*/
public static String getStaticMetadataQualifiedId(String packageName, String functionId) {
return DocumentIdUtil.createQualifiedId(
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java
index 2f3c555..e623fa1 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java
@@ -23,7 +23,6 @@
import android.os.Parcelable;
import android.os.UserHandle;
-
import java.util.Objects;
/**
@@ -40,8 +39,7 @@
public ExecuteAppFunctionAidlRequest createFromParcel(Parcel in) {
ExecuteAppFunctionRequest clientRequest =
ExecuteAppFunctionRequest.CREATOR.createFromParcel(in);
- UserHandle userHandle =
- UserHandle.CREATOR.createFromParcel(in);
+ UserHandle userHandle = UserHandle.CREATOR.createFromParcel(in);
String callingPackage = in.readString8();
return new ExecuteAppFunctionAidlRequest(
clientRequest, userHandle, callingPackage);
@@ -53,19 +51,13 @@
}
};
- /**
- * The client request to execute an app function.
- */
+ /** The client request to execute an app function. */
private final ExecuteAppFunctionRequest mClientRequest;
- /**
- * The user handle of the user to execute the app function.
- */
+ /** The user handle of the user to execute the app function. */
private final UserHandle mUserHandle;
- /**
- * The package name of the app that is requesting to execute the app function.
- */
+ /** The package name of the app that is requesting to execute the app function. */
private final String mCallingPackage;
public ExecuteAppFunctionAidlRequest(
@@ -87,25 +79,19 @@
dest.writeString8(mCallingPackage);
}
- /**
- * Returns the client request to execute an app function.
- */
+ /** Returns the client request to execute an app function. */
@NonNull
public ExecuteAppFunctionRequest getClientRequest() {
return mClientRequest;
}
- /**
- * Returns the user handle of the user to execute the app function.
- */
+ /** Returns the user handle of the user to execute the app function. */
@NonNull
public UserHandle getUserHandle() {
return mUserHandle;
}
- /**
- * Returns the package name of the app that is requesting to execute the app function.
- */
+ /** Returns the package name of the app that is requesting to execute the app function. */
@NonNull
public String getCallingPackage() {
return mCallingPackage;
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
index db3de62..fe7fd88 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
@@ -16,7 +16,6 @@
package android.app.appfunctions;
-
import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
import android.annotation.FlaggedApi;
@@ -28,9 +27,7 @@
import java.util.Objects;
-/**
- * A request to execute an app function.
- */
+/** A request to execute an app function. */
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
public final class ExecuteAppFunctionRequest implements Parcelable {
@NonNull
@@ -40,8 +37,8 @@
public ExecuteAppFunctionRequest createFromParcel(Parcel parcel) {
String targetPackageName = parcel.readString8();
String functionIdentifier = parcel.readString8();
- GenericDocumentWrapper parameters = GenericDocumentWrapper
- .CREATOR.createFromParcel(parcel);
+ GenericDocumentWrapper parameters =
+ GenericDocumentWrapper.CREATOR.createFromParcel(parcel);
Bundle extras = parcel.readBundle(Bundle.class.getClassLoader());
return new ExecuteAppFunctionRequest(
targetPackageName, functionIdentifier, extras, parameters);
@@ -52,34 +49,30 @@
return new ExecuteAppFunctionRequest[size];
}
};
+
+ /** Returns the package name of the app that hosts the function. */
+ @NonNull private final String mTargetPackageName;
+
/**
- * Returns the package name of the app that hosts the function.
+ * Returns the unique string identifier of the app function to be executed. TODO(b/357551503):
+ * Document how callers can get the available function identifiers.
*/
- @NonNull
- private final String mTargetPackageName;
+ @NonNull private final String mFunctionIdentifier;
+
+ /** Returns additional metadata relevant to this function execution request. */
+ @NonNull private final Bundle mExtras;
+
/**
- * Returns the unique string identifier of the app function to be executed.
- * TODO(b/357551503): Document how callers can get the available function identifiers.
- */
- @NonNull
- private final String mFunctionIdentifier;
- /**
- * Returns additional metadata relevant to this function execution request.
- */
- @NonNull
- private final Bundle mExtras;
- /**
- * Returns the parameters required to invoke this function. Within this [GenericDocument],
- * the property names are the names of the function parameters and the property values are the
+ * Returns the parameters required to invoke this function. Within this [GenericDocument], the
+ * property names are the names of the function parameters and the property values are the
* values of those parameters.
*
* <p>The document may have missing parameters. Developers are advised to implement defensive
* handling measures.
- * <p>
- * TODO(b/357551503): Document how function parameters can be obtained for function execution
+ *
+ * <p>TODO(b/357551503): Document how function parameters can be obtained for function execution
*/
- @NonNull
- private final GenericDocumentWrapper mParameters;
+ @NonNull private final GenericDocumentWrapper mParameters;
private ExecuteAppFunctionRequest(
@NonNull String targetPackageName,
@@ -92,17 +85,13 @@
mParameters = Objects.requireNonNull(parameters);
}
- /**
- * Returns the package name of the app that hosts the function.
- */
+ /** Returns the package name of the app that hosts the function. */
@NonNull
public String getTargetPackageName() {
return mTargetPackageName;
}
- /**
- * Returns the unique string identifier of the app function to be executed.
- */
+ /** Returns the unique string identifier of the app function to be executed. */
@NonNull
public String getFunctionIdentifier() {
return mFunctionIdentifier;
@@ -111,8 +100,8 @@
/**
* Returns the function parameters. The key is the parameter name, and the value is the
* parameter value.
- * <p>
- * The bundle may have missing parameters. Developers are advised to implement defensive
+ *
+ * <p>The bundle may have missing parameters. Developers are advised to implement defensive
* handling measures.
*/
@NonNull
@@ -120,9 +109,7 @@
return mParameters.getValue();
}
- /**
- * Returns the additional data relevant to this function execution.
- */
+ /** Returns the additional data relevant to this function execution. */
@NonNull
public Bundle getExtras() {
return mExtras;
@@ -141,37 +128,28 @@
return 0;
}
- /**
- * Builder for {@link ExecuteAppFunctionRequest}.
- */
+ /** Builder for {@link ExecuteAppFunctionRequest}. */
public static final class Builder {
+ @NonNull private final String mTargetPackageName;
+ @NonNull private final String mFunctionIdentifier;
+ @NonNull private Bundle mExtras = Bundle.EMPTY;
+
@NonNull
- private final String mTargetPackageName;
- @NonNull
- private final String mFunctionIdentifier;
- @NonNull
- private Bundle mExtras = Bundle.EMPTY;
- @NonNull
- private GenericDocument mParameters =
- new GenericDocument.Builder<>("", "", "").build();
+ private GenericDocument mParameters = new GenericDocument.Builder<>("", "", "").build();
public Builder(@NonNull String targetPackageName, @NonNull String functionIdentifier) {
mTargetPackageName = Objects.requireNonNull(targetPackageName);
mFunctionIdentifier = Objects.requireNonNull(functionIdentifier);
}
- /**
- * Sets the additional data relevant to this function execution.
- */
+ /** Sets the additional data relevant to this function execution. */
@NonNull
public Builder setExtras(@NonNull Bundle extras) {
mExtras = Objects.requireNonNull(extras);
return this;
}
- /**
- * Sets the function parameters.
- */
+ /** Sets the function parameters. */
@NonNull
public Builder setParameters(@NonNull GenericDocument parameters) {
Objects.requireNonNull(parameters);
@@ -179,13 +157,13 @@
return this;
}
- /**
- * Builds the {@link ExecuteAppFunctionRequest}.
- */
+ /** Builds the {@link ExecuteAppFunctionRequest}. */
@NonNull
public ExecuteAppFunctionRequest build() {
return new ExecuteAppFunctionRequest(
- mTargetPackageName, mFunctionIdentifier, mExtras,
+ mTargetPackageName,
+ mFunctionIdentifier,
+ mExtras,
new GenericDocumentWrapper(mParameters));
}
}
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
index 58d4088..f6580e6 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
@@ -31,9 +31,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
-/**
- * The response to an app function execution.
- */
+/** The response to an app function execution. */
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
public final class ExecuteAppFunctionResponse implements Parcelable {
@NonNull
@@ -43,10 +41,10 @@
public ExecuteAppFunctionResponse createFromParcel(Parcel parcel) {
GenericDocumentWrapper resultWrapper =
Objects.requireNonNull(
- GenericDocumentWrapper
- .CREATOR.createFromParcel(parcel));
- Bundle extras = Objects.requireNonNull(
- parcel.readBundle(Bundle.class.getClassLoader()));
+ GenericDocumentWrapper.CREATOR.createFromParcel(parcel));
+ Bundle extras =
+ Objects.requireNonNull(
+ parcel.readBundle(Bundle.class.getClassLoader()));
int resultCode = parcel.readInt();
String errorMessage = parcel.readString8();
return new ExecuteAppFunctionResponse(
@@ -58,14 +56,15 @@
return new ExecuteAppFunctionResponse[size];
}
};
+
/**
- * The name of the property that stores the function return value within the
- * {@code resultDocument}.
+ * The name of the property that stores the function return value within the {@code
+ * resultDocument}.
*
* <p>See {@link GenericDocument#getProperty(String)} for more information.
*
- * <p>If the function returns {@code void} or throws an error, the {@code resultDocument}
- * will be empty {@link GenericDocument}.
+ * <p>If the function returns {@code void} or throws an error, the {@code resultDocument} will
+ * be empty {@link GenericDocument}.
*
* <p>If the {@code resultDocument} is empty, {@link GenericDocument#getProperty(String)} will
* return {@code null}.
@@ -74,19 +73,13 @@
*/
public static final String PROPERTY_RETURN_VALUE = "returnValue";
- /**
- * The call was successful.
- */
+ /** The call was successful. */
public static final int RESULT_OK = 0;
- /**
- * The caller does not have the permission to execute an app function.
- */
+ /** The caller does not have the permission to execute an app function. */
public static final int RESULT_DENIED = 1;
- /**
- * An unknown error occurred while processing the call in the AppFunctionService.
- */
+ /** An unknown error occurred while processing the call in the AppFunctionService. */
public static final int RESULT_APP_UNKNOWN_ERROR = 2;
/**
@@ -103,45 +96,36 @@
*/
public static final int RESULT_INVALID_ARGUMENT = 4;
- /**
- * The operation was timed out.
- */
+ /** The operation was timed out. */
public static final int RESULT_TIMED_OUT = 5;
- /**
- * The result code of the app function execution.
- */
- @ResultCode
- private final int mResultCode;
+ /** The result code of the app function execution. */
+ @ResultCode private final int mResultCode;
/**
* The error message associated with the result, if any. This is {@code null} if the result code
* is {@link #RESULT_OK}.
*/
- @Nullable
- private final String mErrorMessage;
+ @Nullable private final String mErrorMessage;
/**
* Returns the return value of the executed function.
*
- * <p>The return value is stored in a {@link GenericDocument} with the key
- * {@link #PROPERTY_RETURN_VALUE}.
+ * <p>The return value is stored in a {@link GenericDocument} with the key {@link
+ * #PROPERTY_RETURN_VALUE}.
*
* <p>See {@link #getResultDocument} for more information on extracting the return value.
*/
- @NonNull
- private final GenericDocumentWrapper mResultDocumentWrapper;
+ @NonNull private final GenericDocumentWrapper mResultDocumentWrapper;
- /**
- * Returns the additional metadata data relevant to this function execution response.
- */
- @NonNull
- private final Bundle mExtras;
+ /** Returns the additional metadata data relevant to this function execution response. */
+ @NonNull private final Bundle mExtras;
- private ExecuteAppFunctionResponse(@NonNull GenericDocumentWrapper resultDocumentWrapper,
- @NonNull Bundle extras,
- @ResultCode int resultCode,
- @Nullable String errorMessage) {
+ private ExecuteAppFunctionResponse(
+ @NonNull GenericDocumentWrapper resultDocumentWrapper,
+ @NonNull Bundle extras,
+ @ResultCode int resultCode,
+ @Nullable String errorMessage) {
mResultDocumentWrapper = Objects.requireNonNull(resultDocumentWrapper);
mExtras = Objects.requireNonNull(extras);
mResultCode = resultCode;
@@ -165,42 +149,38 @@
* Returns a successful response.
*
* @param resultDocument The return value of the executed function.
- * @param extras The additional metadata data relevant to this function execution
- * response.
+ * @param extras The additional metadata data relevant to this function execution response.
*/
@NonNull
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
- public static ExecuteAppFunctionResponse newSuccess(@NonNull GenericDocument resultDocument,
- @Nullable Bundle extras) {
+ public static ExecuteAppFunctionResponse newSuccess(
+ @NonNull GenericDocument resultDocument, @Nullable Bundle extras) {
Objects.requireNonNull(resultDocument);
Bundle actualExtras = getActualExtras(extras);
GenericDocumentWrapper resultDocumentWrapper = new GenericDocumentWrapper(resultDocument);
return new ExecuteAppFunctionResponse(
- resultDocumentWrapper, actualExtras, RESULT_OK, /*errorMessage=*/null);
+ resultDocumentWrapper, actualExtras, RESULT_OK, /* errorMessage= */ null);
}
/**
* Returns a failure response.
*
- * @param resultCode The result code of the app function execution.
- * @param extras The additional metadata data relevant to this function execution
- * response.
+ * @param resultCode The result code of the app function execution.
+ * @param extras The additional metadata data relevant to this function execution response.
* @param errorMessage The error message associated with the result, if any.
*/
@NonNull
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
- public static ExecuteAppFunctionResponse newFailure(@ResultCode int resultCode,
- @Nullable String errorMessage,
- @Nullable Bundle extras) {
+ public static ExecuteAppFunctionResponse newFailure(
+ @ResultCode int resultCode, @Nullable String errorMessage, @Nullable Bundle extras) {
if (resultCode == RESULT_OK) {
throw new IllegalArgumentException("resultCode must not be RESULT_OK");
}
Bundle actualExtras = getActualExtras(extras);
- GenericDocumentWrapper emptyWrapper = new GenericDocumentWrapper(
- new GenericDocument.Builder<>("", "", "").build());
- return new ExecuteAppFunctionResponse(
- emptyWrapper, actualExtras, resultCode, errorMessage);
+ GenericDocumentWrapper emptyWrapper =
+ new GenericDocumentWrapper(new GenericDocument.Builder<>("", "", "").build());
+ return new ExecuteAppFunctionResponse(emptyWrapper, actualExtras, resultCode, errorMessage);
}
private static Bundle getActualExtras(@Nullable Bundle extras) {
@@ -213,12 +193,13 @@
/**
* Returns a generic document containing the return value of the executed function.
*
- * <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.</p>
+ * <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.
*
* <p>An empty document is returned if {@link #isSuccess} is {@code false} or if the executed
* function does not produce a return value.
*
* <p>Sample code for extracting the return value:
+ *
* <pre>
* GenericDocument resultDocument = response.getResultDocument();
* Object returnValue = resultDocument.getProperty(PROPERTY_RETURN_VALUE);
@@ -234,17 +215,15 @@
return mResultDocumentWrapper.getValue();
}
- /**
- * Returns the extras of the app function execution.
- */
+ /** Returns the extras of the app function execution. */
@NonNull
public Bundle getExtras() {
return mExtras;
}
/**
- * Returns {@code true} if {@link #getResultCode} equals
- * {@link ExecuteAppFunctionResponse#RESULT_OK}.
+ * Returns {@code true} if {@link #getResultCode} equals {@link
+ * ExecuteAppFunctionResponse#RESULT_OK}.
*/
public boolean isSuccess() {
return getResultCode() == RESULT_OK;
@@ -289,14 +268,13 @@
@IntDef(
prefix = {"RESULT_"},
value = {
- RESULT_OK,
- RESULT_DENIED,
- RESULT_APP_UNKNOWN_ERROR,
- RESULT_INTERNAL_ERROR,
- RESULT_INVALID_ARGUMENT,
- RESULT_TIMED_OUT,
+ RESULT_OK,
+ RESULT_DENIED,
+ RESULT_APP_UNKNOWN_ERROR,
+ RESULT_INTERNAL_ERROR,
+ RESULT_INVALID_ARGUMENT,
+ RESULT_TIMED_OUT,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface ResultCode {
- }
+ public @interface ResultCode {}
}
diff --git a/core/java/android/app/appfunctions/GenericDocumentWrapper.java b/core/java/android/app/appfunctions/GenericDocumentWrapper.java
index 8c76c8e..84b1837 100644
--- a/core/java/android/app/appfunctions/GenericDocumentWrapper.java
+++ b/core/java/android/app/appfunctions/GenericDocumentWrapper.java
@@ -56,16 +56,13 @@
return new GenericDocumentWrapper[size];
}
};
- @NonNull
- private final GenericDocument mGenericDocument;
+ @NonNull private final GenericDocument mGenericDocument;
public GenericDocumentWrapper(@NonNull GenericDocument genericDocument) {
mGenericDocument = Objects.requireNonNull(genericDocument);
}
- /**
- * Returns the wrapped {@link android.app.appsearch.GenericDocument}
- */
+ /** Returns the wrapped {@link android.app.appsearch.GenericDocument} */
@NonNull
public GenericDocument getValue() {
return mGenericDocument;
@@ -86,6 +83,5 @@
} finally {
parcel.recycle();
}
-
}
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index cf34452..473ab27 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -1259,6 +1259,8 @@
/**
* Called when a window with a secure surface is shown on the device.
*
+ * <p>Note that this is called only when the window is associated with an activity.</p>
+ *
* @param displayId The display ID on which the window was shown.
* @param componentName The component name of the activity that showed the window.
* @param user The user associated with the activity.
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 83f2685..2d96bba 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -241,12 +241,12 @@
KeyGlyphMap getKeyGlyphMap(int deviceId);
- @EnforcePermission("MANAGE_KEY_GESTURES")
+ @PermissionManuallyEnforced
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
void registerKeyGestureEventListener(IKeyGestureEventListener listener);
- @EnforcePermission("MANAGE_KEY_GESTURES")
+ @PermissionManuallyEnforced
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
void unregisterKeyGestureEventListener(IKeyGestureEventListener listener);
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 077bd82..fcd6c31 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -101,8 +101,19 @@
}
flag {
+ namespace: "input_native"
+ name: "manage_key_gestures"
+ description: "Manage key gestures through Input APIs"
+ is_exported: true
+ bug: "358569822"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "keyboard_repeat_keys"
- namespace: "input"
+ namespace: "input_native"
description: "Allow configurable timeout before key repeat and repeat delay rate for key repeats"
bug: "336585002"
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index ff737a4..49e2358 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -664,9 +664,14 @@
int mStatusIcon;
+ /** Latest reported value of back disposition mode. */
@BackDispositionMode
int mBackDisposition;
+ /** Latest reported value of IME window visibility state. */
+ @ImeWindowVisibility
+ private int mImeWindowVisibility;
+
private Object mLock = new Object();
@GuardedBy("mLock")
private boolean mNotifyUserActionSent;
@@ -1047,7 +1052,7 @@
ImeTracker.forLogging().onFailed(statsToken,
ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE);
}
- setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+ setImeWindowVisibility(computeImeWindowVis());
final boolean isVisible = isInputViewShown();
final boolean visibilityChanged = isVisible != wasVisible;
@@ -1357,9 +1362,22 @@
mImeSurfaceRemoverRunnable = null;
}
- private void setImeWindowStatus(@ImeWindowVisibility int visibilityFlags,
+ /**
+ * Sets the IME window visibility state.
+ *
+ * @param vis the IME window visibility state to be set.
+ */
+ private void setImeWindowVisibility(@ImeWindowVisibility int vis) {
+ if (vis == mImeWindowVisibility) {
+ return;
+ }
+ mImeWindowVisibility = vis;
+ setImeWindowStatus(mImeWindowVisibility, mBackDisposition);
+ }
+
+ private void setImeWindowStatus(@ImeWindowVisibility int vis,
@BackDispositionMode int backDisposition) {
- mPrivOps.setImeWindowStatusAsync(visibilityFlags, backDisposition);
+ mPrivOps.setImeWindowStatusAsync(vis, backDisposition);
}
/** Set region of the keyboard to be avoided from back gesture */
@@ -1986,7 +2004,7 @@
}
// If user uses hard keyboard, IME button should always be shown.
boolean showing = onEvaluateInputViewShown();
- setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
+ setImeWindowVisibility(IME_ACTIVE | (showing ? IME_VISIBLE : 0));
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -2053,7 +2071,7 @@
return;
}
mBackDisposition = disposition;
- setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+ setImeWindowStatus(mImeWindowVisibility, mBackDisposition);
}
/**
@@ -3132,14 +3150,8 @@
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
mDecorViewWasVisible = mDecorViewVisible;
mInShowWindow = true;
- final int previousImeWindowStatus =
- (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
- ? (!mWindowVisible ? -1 : IME_VISIBLE) : 0);
startViews(prepareWindow(showInput));
- final int nextImeWindowStatus = mapToImeWindowStatus();
- if (previousImeWindowStatus != nextImeWindowStatus) {
- setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
- }
+ setImeWindowVisibility(computeImeWindowVis());
mNavigationBarController.onWindowShown();
// compute visibility
@@ -3317,7 +3329,7 @@
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_WINDOW);
ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
null /* icProto */);
- setImeWindowStatus(0 /* visibilityFlags */, mBackDisposition);
+ setImeWindowVisibility(0 /* vis */);
if (android.view.inputmethod.Flags.refactorInsetsController()) {
// The ImeInsetsSourceProvider need the statsToken when dispatching the control. We
// send the token here, so that another request in the provider can be cancelled.
@@ -4492,10 +4504,10 @@
};
}
+ /** Computes the IME window visibility state. */
@ImeWindowVisibility
- private int mapToImeWindowStatus() {
- return IME_ACTIVE
- | (isInputViewShown() ? IME_VISIBLE : 0);
+ private int computeImeWindowVis() {
+ return IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
}
/**
diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig
index 04dba46..fb1bd17 100644
--- a/core/java/android/tracing/flags.aconfig
+++ b/core/java/android/tracing/flags.aconfig
@@ -48,6 +48,22 @@
}
flag {
+ name: "perfetto_wm_dump"
+ namespace: "windowing_tools"
+ description: "Migrate WindowManager dump to Perfetto"
+ is_fixed_read_only: true
+ bug: "323165543"
+}
+
+flag {
+ name: "perfetto_wm_dump_cts"
+ namespace: "windowing_tools"
+ description: "Migrate WindowManager dump in CTS tests to Perfetto"
+ is_fixed_read_only: true
+ bug: "323165543"
+}
+
+flag {
name: "client_side_proto_logging"
namespace: "windowing_tools"
description: "Add support for client side protologging"
diff --git a/core/java/android/tracing/perfetto/DataSource.java b/core/java/android/tracing/perfetto/DataSource.java
index 4de7b62..b65471d 100644
--- a/core/java/android/tracing/perfetto/DataSource.java
+++ b/core/java/android/tracing/perfetto/DataSource.java
@@ -16,6 +16,8 @@
package android.tracing.perfetto;
+import android.annotation.Nullable;
+import android.annotation.NonNull;
import android.util.proto.ProtoInputStream;
/**
@@ -41,6 +43,7 @@
* @param configStream A ProtoInputStream to read the tracing instance's config.
* @return A new data source instance setup with the provided config.
*/
+ @NonNull
public abstract DataSourceInstanceType createInstance(
ProtoInputStream configStream, int instanceIndex);
@@ -102,8 +105,8 @@
/**
* Override this method to create a custom TlsState object for your DataSource. A new instance
* will be created per trace instance per thread.
- *
*/
+ @Nullable
public TlsStateType createTlsState(CreateTlsStateArgs<DataSourceInstanceType> args) {
return null;
}
@@ -112,6 +115,7 @@
* Override this method to create and use a custom IncrementalState object for your DataSource.
*
*/
+ @Nullable
public IncrementalStateType createIncrementalState(
CreateIncrementalStateArgs<DataSourceInstanceType> args) {
return null;
@@ -141,6 +145,7 @@
* @return The DataSourceInstance at index instanceIndex.
* Null if the datasource instance at the requested index doesn't exist.
*/
+ @Nullable
public DataSourceInstanceType getDataSourceInstanceLocked(int instanceIndex) {
return (DataSourceInstanceType) nativeGetPerfettoInstanceLocked(mNativeObj, instanceIndex);
}
@@ -159,6 +164,7 @@
* @param rawConfig byte array of the PerfettoConfig encoded proto.
* @return A new Java DataSourceInstance object.
*/
+ @NonNull
private DataSourceInstanceType createInstance(byte[] rawConfig, int instanceIndex) {
final ProtoInputStream inputStream = new ProtoInputStream(rawConfig);
return this.createInstance(inputStream, instanceIndex);
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index ed2bf79..513587e 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -19,6 +19,13 @@
}
flag {
+ name: "a11y_selection_api"
+ namespace: "accessibility"
+ description: "Enables new APIs for an AccessibilityService to control selection across nodes."
+ bug: "362782866"
+}
+
+flag {
namespace: "accessibility"
name: "allow_shortcut_chooser_on_lockscreen"
description: "Allows the a11y shortcut disambig dialog to appear on the lockscreen"
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index ec79f94..1083f64 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -1141,7 +1141,6 @@
// Customize activity transition animation
private CustomActivityTransition mCustomActivityOpenTransition;
private CustomActivityTransition mCustomActivityCloseTransition;
- private int mUserId;
private AnimationOptions(int type) {
mType = type;
@@ -1160,7 +1159,6 @@
mAnimations = in.readInt();
mCustomActivityOpenTransition = in.readTypedObject(CustomActivityTransition.CREATOR);
mCustomActivityCloseTransition = in.readTypedObject(CustomActivityTransition.CREATOR);
- mUserId = in.readInt();
}
/** Make basic customized animation for a package */
@@ -1285,14 +1283,6 @@
return options;
}
- public void setUserId(int userId) {
- mUserId = userId;
- }
-
- public int getUserId() {
- return mUserId;
- }
-
public int getType() {
return mType;
}
@@ -1359,7 +1349,6 @@
dest.writeInt(mAnimations);
dest.writeTypedObject(mCustomActivityOpenTransition, flags);
dest.writeTypedObject(mCustomActivityCloseTransition, flags);
- dest.writeInt(mUserId);
}
@NonNull
@@ -1417,7 +1406,6 @@
if (mExitResId != DEFAULT_ANIMATION_RESOURCES_ID) {
sb.append(" exitResId=").append(mExitResId);
}
- sb.append(" mUserId=").append(mUserId);
sb.append('}');
return sb.toString();
}
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index cab6d8e..405217c 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -2,14 +2,6 @@
container: "system"
flag {
- name: "enable_scaled_resizing"
- namespace: "lse_desktop_experience"
- description: "Enable the resizing of un-resizable apps through scaling their bounds up/down"
- bug: "320350734"
- is_fixed_read_only: true
-}
-
-flag {
name: "enable_desktop_windowing_mode"
namespace: "lse_desktop_experience"
description: "Enables desktop windowing"
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index ce17d78..ef4acd1 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -23,6 +23,7 @@
import android.os.Bundle;
import android.os.LocaleList;
import android.text.TextUtils;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -265,6 +266,11 @@
public void onListItemClick(ListView parent, View v, int position, long id) {
final LocaleStore.LocaleInfo locale =
(LocaleStore.LocaleInfo) parent.getAdapter().getItem(position);
+ if (locale == null) {
+ Log.d(TAG, "Can not get the locale.");
+ return;
+ }
+
// Special case for resetting the app locale to equal the system locale.
boolean isSystemLocale = locale.isSystemLocale();
boolean isRegionLocale = locale.getParent() != null;
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 201f267..238e6f5 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -49,7 +49,7 @@
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
-import android.os.UserHandle;
+import android.os.SystemProperties;
import android.util.Slog;
import android.view.InflateException;
import android.view.SurfaceControl;
@@ -187,44 +187,23 @@
return createHiddenByKeyguardExit(mContext, mInterpolator, onWallpaper, toShade, subtle);
}
- /** Load keyguard unocclude animation for user. */
- @Nullable
- public Animation loadKeyguardUnoccludeAnimation(int userId) {
- return loadDefaultAnimationRes(com.android.internal.R.anim.wallpaper_open_exit, userId);
- }
-
- /** Same as {@code loadKeyguardUnoccludeAnimation} for current user. */
@Nullable
public Animation loadKeyguardUnoccludeAnimation() {
- return loadKeyguardUnoccludeAnimation(UserHandle.USER_CURRENT);
+ return loadDefaultAnimationRes(com.android.internal.R.anim.wallpaper_open_exit);
}
- /** Load voice activity open animation for user. */
- @Nullable
- public Animation loadVoiceActivityOpenAnimation(boolean enter, int userId) {
- return loadDefaultAnimationRes(enter
- ? com.android.internal.R.anim.voice_activity_open_enter
- : com.android.internal.R.anim.voice_activity_open_exit, userId);
- }
-
- /** Same as {@code loadVoiceActivityOpenAnimation} for current user. */
@Nullable
public Animation loadVoiceActivityOpenAnimation(boolean enter) {
- return loadVoiceActivityOpenAnimation(enter, UserHandle.USER_CURRENT);
- }
-
- /** Load voice activity exit animation for user. */
- @Nullable
- public Animation loadVoiceActivityExitAnimation(boolean enter, int userId) {
return loadDefaultAnimationRes(enter
- ? com.android.internal.R.anim.voice_activity_close_enter
- : com.android.internal.R.anim.voice_activity_close_exit, userId);
+ ? com.android.internal.R.anim.voice_activity_open_enter
+ : com.android.internal.R.anim.voice_activity_open_exit);
}
- /** Same as {@code loadVoiceActivityExitAnimation} for current user. */
@Nullable
public Animation loadVoiceActivityExitAnimation(boolean enter) {
- return loadVoiceActivityExitAnimation(enter, UserHandle.USER_CURRENT);
+ return loadDefaultAnimationRes(enter
+ ? com.android.internal.R.anim.voice_activity_close_enter
+ : com.android.internal.R.anim.voice_activity_close_exit);
}
@Nullable
@@ -232,17 +211,10 @@
return loadAnimationRes(packageName, resId);
}
- /** Load cross profile app enter animation for user. */
- @Nullable
- public Animation loadCrossProfileAppEnterAnimation(int userId) {
- return loadAnimationRes(DEFAULT_PACKAGE,
- com.android.internal.R.anim.task_open_enter_cross_profile_apps, userId);
- }
-
- /** Same as {@code loadCrossProfileAppEnterAnimation} for current user. */
@Nullable
public Animation loadCrossProfileAppEnterAnimation() {
- return loadCrossProfileAppEnterAnimation(UserHandle.USER_CURRENT);
+ return loadAnimationRes(DEFAULT_PACKAGE,
+ com.android.internal.R.anim.task_open_enter_cross_profile_apps);
}
@Nullable
@@ -258,11 +230,11 @@
appRect.height(), 0, null);
}
- /** Load animation by resource Id from specific package for user. */
+ /** Load animation by resource Id from specific package. */
@Nullable
- public Animation loadAnimationRes(String packageName, int resId, int userId) {
+ public Animation loadAnimationRes(String packageName, int resId) {
if (ResourceId.isValid(resId)) {
- AttributeCache.Entry ent = getCachedAnimations(packageName, resId, userId);
+ AttributeCache.Entry ent = getCachedAnimations(packageName, resId);
if (ent != null) {
return loadAnimationSafely(ent.context, resId, mTag);
}
@@ -270,22 +242,10 @@
return null;
}
- /** Same as {@code loadAnimationRes} for current user. */
- @Nullable
- public Animation loadAnimationRes(String packageName, int resId) {
- return loadAnimationRes(packageName, resId, UserHandle.USER_CURRENT);
- }
-
- /** Load animation by resource Id from android package for user. */
- @Nullable
- public Animation loadDefaultAnimationRes(int resId, int userId) {
- return loadAnimationRes(DEFAULT_PACKAGE, resId, userId);
- }
-
- /** Same as {@code loadDefaultAnimationRes} for current user. */
+ /** Load animation by resource Id from android package. */
@Nullable
public Animation loadDefaultAnimationRes(int resId) {
- return loadAnimationRes(DEFAULT_PACKAGE, resId, UserHandle.USER_CURRENT);
+ return loadAnimationRes(DEFAULT_PACKAGE, resId);
}
/** Load animation by attribute Id from specific LayoutParams */
@@ -418,10 +378,10 @@
}
@Nullable
- private AttributeCache.Entry getCachedAnimations(String packageName, int resId, int userId) {
+ private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
if (mDebug) {
- Slog.v(mTag, "Loading animations: package=" + packageName + " resId=0x"
- + Integer.toHexString(resId) + " for user=" + userId);
+ Slog.v(mTag, "Loading animations: package="
+ + packageName + " resId=0x" + Integer.toHexString(resId));
}
if (packageName != null) {
if ((resId & 0xFF000000) == 0x01000000) {
@@ -432,16 +392,11 @@
+ packageName);
}
return AttributeCache.instance().get(packageName, resId,
- com.android.internal.R.styleable.WindowAnimation, userId);
+ com.android.internal.R.styleable.WindowAnimation);
}
return null;
}
- @Nullable
- private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
- return getCachedAnimations(packageName, resId, UserHandle.USER_CURRENT);
- }
-
/** Returns window animation style ID from {@link LayoutParams} or from system in some cases */
public int getAnimationStyleResId(@NonNull LayoutParams lp) {
int resId = lp.windowAnimations;
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index 4264358..1fd933f 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -99,11 +99,8 @@
public static final String NULL_STRING = "null";
private final AtomicInteger mTracingInstances = new AtomicInteger();
- private final ProtoLogDataSource mDataSource = new ProtoLogDataSource(
- this::onTracingInstanceStart,
- this::onTracingFlush,
- this::onTracingInstanceStop
- );
+ @NonNull
+ private final ProtoLogDataSource mDataSource;
@Nullable
private final ProtoLogViewerConfigReader mViewerConfigReader;
@Nullable
@@ -156,8 +153,11 @@
@Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
@Nullable ProtoLogViewerConfigReader viewerConfigReader,
@NonNull Runnable cacheUpdater,
- @NonNull IProtoLogGroup[] groups) {
- this(null, viewerConfigInputStreamProvider, viewerConfigReader, cacheUpdater, groups);
+ @NonNull IProtoLogGroup[] groups,
+ @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+ @NonNull ProtoLogConfigurationService configurationService) {
+ this(null, viewerConfigInputStreamProvider, viewerConfigReader, cacheUpdater,
+ groups, dataSourceBuilder, configurationService);
}
private PerfettoProtoLogImpl(
@@ -166,11 +166,31 @@
@Nullable ProtoLogViewerConfigReader viewerConfigReader,
@NonNull Runnable cacheUpdater,
@NonNull IProtoLogGroup[] groups) {
+ this(viewerConfigFilePath, viewerConfigInputStreamProvider, viewerConfigReader,
+ cacheUpdater, groups,
+ ProtoLogDataSource::new,
+ IProtoLogConfigurationService.Stub
+ .asInterface(ServiceManager.getService(PROTOLOG_CONFIGURATION_SERVICE))
+ );
+ }
+
+ private PerfettoProtoLogImpl(
+ @Nullable String viewerConfigFilePath,
+ @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
+ @Nullable ProtoLogViewerConfigReader viewerConfigReader,
+ @NonNull Runnable cacheUpdater,
+ @NonNull IProtoLogGroup[] groups,
+ @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+ @Nullable IProtoLogConfigurationService configurationService) {
if (viewerConfigFilePath != null && viewerConfigInputStreamProvider != null) {
throw new RuntimeException("Only one of viewerConfigFilePath and "
+ "viewerConfigInputStreamProvider can be set");
}
+ mDataSource = dataSourceBuilder.build(
+ this::onTracingInstanceStart,
+ this::onTracingFlush,
+ this::onTracingInstanceStop);
Producer.init(InitArguments.DEFAULTS);
DataSourceParams params =
new DataSourceParams.Builder()
@@ -186,9 +206,7 @@
registerGroupsLocally(groups);
if (android.tracing.Flags.clientSideProtoLogging()) {
- mProtoLogConfigurationService =
- IProtoLogConfigurationService.Stub.asInterface(ServiceManager.getService(
- PROTOLOG_CONFIGURATION_SERVICE));
+ mProtoLogConfigurationService = configurationService;
Objects.requireNonNull(mProtoLogConfigurationService,
"ServiceManager returned a null ProtoLog Configuration Service");
@@ -733,6 +751,10 @@
incrementalState.protologMessageInterningSet.add(messageHash);
final ProtoOutputStream os = ctx.newTracePacket();
+
+ // Dependent on the ProtoLog viewer config packet that contains the group information.
+ os.write(SEQUENCE_FLAGS, SEQ_NEEDS_INCREMENTAL_STATE);
+
final long protologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
final long messageConfigToken = os.start(MESSAGES);
diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
index eeac139..d54a80b 100644
--- a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
+++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
@@ -75,11 +75,7 @@
public final class ProtoLogConfigurationService extends IProtoLogConfigurationService.Stub {
private static final String LOG_TAG = "ProtoLogConfigurationService";
- private final ProtoLogDataSource mDataSource = new ProtoLogDataSource(
- this::onTracingInstanceStart,
- this::onTracingInstanceFlush,
- this::onTracingInstanceStop
- );
+ private final ProtoLogDataSource mDataSource;
/**
* Keeps track of how many of each viewer config file is currently registered.
@@ -116,11 +112,28 @@
private final ViewerConfigFileTracer mViewerConfigFileTracer;
public ProtoLogConfigurationService() {
- this(ProtoLogConfigurationService::dumpTransitionTraceConfig);
+ this(ProtoLogDataSource::new, ProtoLogConfigurationService::dumpTransitionTraceConfig);
+ }
+
+ @VisibleForTesting
+ public ProtoLogConfigurationService(@NonNull ProtoLogDataSourceBuilder dataSourceBuilder) {
+ this(dataSourceBuilder, ProtoLogConfigurationService::dumpTransitionTraceConfig);
}
@VisibleForTesting
public ProtoLogConfigurationService(@NonNull ViewerConfigFileTracer tracer) {
+ this(ProtoLogDataSource::new, tracer);
+ }
+
+ private ProtoLogConfigurationService(
+ @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+ @NonNull ViewerConfigFileTracer tracer) {
+ mDataSource = dataSourceBuilder.build(
+ this::onTracingInstanceStart,
+ this::onTracingInstanceFlush,
+ this::onTracingInstanceStop
+ );
+
// Initialize the Perfetto producer and register the Perfetto ProtoLog datasource to be
// receive the lifecycle callbacks of the datasource and write the viewer configs if and
// when required to the datasource.
diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSource.java b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
index 837622f..1b2f5f7 100644
--- a/core/java/com/android/internal/protolog/ProtoLogDataSource.java
+++ b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
@@ -25,6 +25,7 @@
import static android.internal.perfetto.protos.ProtologConfig.ProtoLogGroup.GROUP_NAME;
import static android.internal.perfetto.protos.ProtologConfig.ProtoLogGroup.LOG_FROM;
+import android.annotation.NonNull;
import android.internal.perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig;
import android.internal.perfetto.protos.ProtologCommon;
import android.tracing.perfetto.CreateIncrementalStateArgs;
@@ -51,18 +52,26 @@
ProtoLogDataSource.IncrementalState> {
private static final String DATASOURCE_NAME = "android.protolog";
+ @NonNull
private final Instance.TracingInstanceStartCallback mOnStart;
+ @NonNull
private final Runnable mOnFlush;
+ @NonNull
private final Instance.TracingInstanceStopCallback mOnStop;
- public ProtoLogDataSource(Instance.TracingInstanceStartCallback onStart, Runnable onFlush,
- Instance.TracingInstanceStopCallback onStop) {
+ public ProtoLogDataSource(
+ @NonNull Instance.TracingInstanceStartCallback onStart,
+ @NonNull Runnable onFlush,
+ @NonNull Instance.TracingInstanceStopCallback onStop) {
this(onStart, onFlush, onStop, DATASOURCE_NAME);
}
@VisibleForTesting
- public ProtoLogDataSource(Instance.TracingInstanceStartCallback onStart, Runnable onFlush,
- Instance.TracingInstanceStopCallback onStop, String dataSourceName) {
+ public ProtoLogDataSource(
+ @NonNull Instance.TracingInstanceStartCallback onStart,
+ @NonNull Runnable onFlush,
+ @NonNull Instance.TracingInstanceStopCallback onStop,
+ @NonNull String dataSourceName) {
super(dataSourceName);
this.mOnStart = onStart;
this.mOnFlush = onFlush;
@@ -70,7 +79,8 @@
}
@Override
- public Instance createInstance(ProtoInputStream configStream, int instanceIndex) {
+ @NonNull
+ public Instance createInstance(@NonNull ProtoInputStream configStream, int instanceIndex) {
ProtoLogConfig config = null;
try {
@@ -100,7 +110,8 @@
}
@Override
- public TlsState createTlsState(CreateTlsStateArgs<Instance> args) {
+ @NonNull
+ public TlsState createTlsState(@NonNull CreateTlsStateArgs<Instance> args) {
try (Instance dsInstance = args.getDataSourceInstanceLocked()) {
if (dsInstance == null) {
// Datasource instance has been removed
@@ -111,14 +122,17 @@
}
@Override
- public IncrementalState createIncrementalState(CreateIncrementalStateArgs<Instance> args) {
+ @NonNull
+ public IncrementalState createIncrementalState(
+ @NonNull CreateIncrementalStateArgs<Instance> args) {
return new IncrementalState();
}
public static class TlsState {
+ @NonNull
private final ProtoLogConfig mConfig;
- private TlsState(ProtoLogConfig config) {
+ private TlsState(@NonNull ProtoLogConfig config) {
this.mConfig = config;
}
@@ -269,26 +283,30 @@
public static class Instance extends DataSourceInstance {
public interface TracingInstanceStartCallback {
- void run(int instanceIdx, ProtoLogConfig config);
+ void run(int instanceIdx, @NonNull ProtoLogConfig config);
}
public interface TracingInstanceStopCallback {
- void run(int instanceIdx, ProtoLogConfig config);
+ void run(int instanceIdx, @NonNull ProtoLogConfig config);
}
+ @NonNull
private final TracingInstanceStartCallback mOnStart;
+ @NonNull
private final Runnable mOnFlush;
+ @NonNull
private final TracingInstanceStopCallback mOnStop;
+ @NonNull
private final ProtoLogConfig mConfig;
private final int mInstanceIndex;
public Instance(
- DataSource<Instance, TlsState, IncrementalState> dataSource,
+ @NonNull DataSource<Instance, TlsState, IncrementalState> dataSource,
int instanceIdx,
- ProtoLogConfig config,
- TracingInstanceStartCallback onStart,
- Runnable onFlush,
- TracingInstanceStopCallback onStop
+ @NonNull ProtoLogConfig config,
+ @NonNull TracingInstanceStartCallback onStart,
+ @NonNull Runnable onFlush,
+ @NonNull TracingInstanceStopCallback onStop
) {
super(dataSource, instanceIdx);
this.mInstanceIndex = instanceIdx;
diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSourceBuilder.java b/core/java/com/android/internal/protolog/ProtoLogDataSourceBuilder.java
new file mode 100644
index 0000000..da78b62
--- /dev/null
+++ b/core/java/com/android/internal/protolog/ProtoLogDataSourceBuilder.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.protolog;
+
+import android.annotation.NonNull;
+
+public interface ProtoLogDataSourceBuilder {
+ /**
+ * Builder method for the DataSource the PerfettoProtoLogImpl is going to us.
+ * @param onStart The onStart callback that should be used by the created datasource.
+ * @param onFlush The onFlush callback that should be used by the created datasource.
+ * @param onStop The onStop callback that should be used by the created datasource.
+ * @return A new DataSource that uses the provided callbacks.
+ */
+ @NonNull
+ ProtoLogDataSource build(
+ @NonNull ProtoLogDataSource.Instance.TracingInstanceStartCallback onStart,
+ @NonNull Runnable onFlush,
+ @NonNull ProtoLogDataSource.Instance.TracingInstanceStopCallback onStop);
+}
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index 3b24f27..6c8996e 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -24,7 +24,9 @@
public class ProtoLogViewerConfigReader {
@NonNull
private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
+ @NonNull
private final Map<String, Set<Long>> mGroupHashes = new TreeMap<>();
+ @NonNull
private final LongSparseArray<String> mLogMessageMap = new LongSparseArray<>();
public ProtoLogViewerConfigReader(
@@ -41,14 +43,14 @@
return mLogMessageMap.get(messageHash);
}
- public synchronized void loadViewerConfig(String[] groups) {
+ public synchronized void loadViewerConfig(@NonNull String[] groups) {
loadViewerConfig(groups, (message) -> {});
}
/**
* Loads the viewer config into memory. No-op if already loaded in memory.
*/
- public synchronized void loadViewerConfig(String[] groups, @NonNull ILogger logger) {
+ public synchronized void loadViewerConfig(@NonNull String[] groups, @NonNull ILogger logger) {
for (String group : groups) {
if (mGroupHashes.containsKey(group)) {
continue;
@@ -69,14 +71,14 @@
}
}
- public synchronized void unloadViewerConfig(String[] groups) {
+ public synchronized void unloadViewerConfig(@NonNull String[] groups) {
unloadViewerConfig(groups, (message) -> {});
}
/**
* Unload the viewer config from memory.
*/
- public synchronized void unloadViewerConfig(String[] groups, @NonNull ILogger logger) {
+ public synchronized void unloadViewerConfig(@NonNull String[] groups, @NonNull ILogger logger) {
for (String group : groups) {
if (!mGroupHashes.containsKey(group)) {
continue;
@@ -90,8 +92,10 @@
}
}
- private Map<Long, String> loadViewerConfigMappingForGroup(String group) throws IOException {
- Long targetGroupId = loadGroupId(group);
+ @NonNull
+ private Map<Long, String> loadViewerConfigMappingForGroup(@NonNull String group)
+ throws IOException {
+ long targetGroupId = loadGroupId(group);
final Map<Long, String> hashesForGroup = new TreeMap<>();
final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
@@ -140,7 +144,7 @@
return hashesForGroup;
}
- private Long loadGroupId(String group) throws IOException {
+ private long loadGroupId(@NonNull String group) throws IOException {
final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
diff --git a/core/res/Android.bp b/core/res/Android.bp
index bcc0a97..17d7bfa 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -167,6 +167,7 @@
"android.os.flags-aconfig",
"android.os.vibrator.flags-aconfig",
"android.media.tv.flags-aconfig",
+ "com.android.hardware.input.input-aconfig",
],
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5decf7f..a19b71c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8171,7 +8171,8 @@
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.MANAGE_KEY_GESTURES"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature"
+ android:featureFlag="com.android.hardware.input.manage_key_gestures" />
<uses-permission android:name="android.permission.HANDLE_QUERY_PACKAGE_RESTART" />
diff --git a/core/res/res/drawable/ic_zen_priority_modes.xml b/core/res/res/drawable/ic_zen_priority_modes.xml
index e8691fc..9c72f51 100644
--- a/core/res/res/drawable/ic_zen_priority_modes.xml
+++ b/core/res/res/drawable/ic_zen_priority_modes.xml
@@ -19,7 +19,7 @@
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M480,720Q580,720 650,650Q720,580 720,480Q720,380 650,310Q580,240 480,240L480,480L310,650Q345,683 388.5,701.5Q432,720 480,720ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M280,520L680,520L680,440L280,440L280,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z" />
</vector>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4beeb17..38aff75 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4153,6 +4153,9 @@
<!-- Indicating if keyboard vibration settings supported or not. -->
<bool name="config_keyboardVibrationSettingsSupported">false</bool>
+ <!-- Indicating if ringtone vibration settings supported or not. -->
+ <bool name="config_ringtoneVibrationSettingsSupported">false</bool>
+
<!-- If the device should still vibrate even in low power mode, for certain priority vibrations
(e.g. accessibility, alarms). This is mainly for Wear devices that don't have speakers. -->
<bool name="config_allowPriorityVibrationsInLowPowerMode">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 80ec67a..4693894 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2132,6 +2132,7 @@
<java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
<java-symbol type="dimen" name="config_keyboardHapticFeedbackFixedAmplitude" />
<java-symbol type="bool" name="config_keyboardVibrationSettingsSupported" />
+ <java-symbol type="bool" name="config_ringtoneVibrationSettingsSupported" />
<java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
<java-symbol type="bool" name="config_ignoreVibrationsOnWirelessCharger" />
<java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 9b0fb20..4fc6c44 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -18,8 +18,8 @@
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
import static android.app.ActivityOptions.ANIM_CUSTOM;
-import static android.app.ActivityOptions.ANIM_FROM_STYLE;
import static android.app.ActivityOptions.ANIM_NONE;
+import static android.app.ActivityOptions.ANIM_FROM_STYLE;
import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
@@ -473,7 +473,7 @@
change.getLeash(),
startTransaction);
} else if (isOnlyTranslucent && TransitionUtil.isOpeningType(info.getType())
- && TransitionUtil.isClosingType(mode)) {
+ && TransitionUtil.isClosingType(mode)) {
// If there is a closing translucent task in an OPENING transition, we will
// actually select a CLOSING animation, so move the closing task into
// the animating part of the z-order.
@@ -767,12 +767,12 @@
a = mTransitionAnimation.loadKeyguardExitAnimation(flags,
(changeFlags & FLAG_SHOW_WALLPAPER) != 0);
} else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) {
- a = mTransitionAnimation.loadKeyguardUnoccludeAnimation(options.getUserId());
+ a = mTransitionAnimation.loadKeyguardUnoccludeAnimation();
} else if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
if (isOpeningType) {
- a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter, options.getUserId());
+ a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter);
} else {
- a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter, options.getUserId());
+ a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter);
}
} else if (changeMode == TRANSIT_CHANGE) {
// In the absence of a specific adapter, we just want to keep everything stationary.
@@ -783,9 +783,9 @@
} else if (overrideType == ANIM_CUSTOM
&& (!isTask || options.getOverrideTaskTransition())) {
a = mTransitionAnimation.loadAnimationRes(options.getPackageName(), enter
- ? options.getEnterResId() : options.getExitResId(), options.getUserId());
+ ? options.getEnterResId() : options.getExitResId());
} else if (overrideType == ANIM_OPEN_CROSS_PROFILE_APPS && enter) {
- a = mTransitionAnimation.loadCrossProfileAppEnterAnimation(options.getUserId());
+ a = mTransitionAnimation.loadCrossProfileAppEnterAnimation();
} else if (overrideType == ANIM_CLIP_REVEAL) {
a = mTransitionAnimation.createClipRevealAnimationLocked(type, wallpaperTransit, enter,
endBounds, endBounds, options.getTransitionBounds());
@@ -905,9 +905,9 @@
final Rect bounds = change.getEndAbsBounds();
// Show the right drawable depending on the user we're transitioning to.
final Drawable thumbnailDrawable = change.hasFlags(FLAG_CROSS_PROFILE_OWNER_THUMBNAIL)
- ? mContext.getDrawable(R.drawable.ic_account_circle)
- : change.hasFlags(FLAG_CROSS_PROFILE_WORK_THUMBNAIL)
- ? mEnterpriseThumbnailDrawable : null;
+ ? mContext.getDrawable(R.drawable.ic_account_circle)
+ : change.hasFlags(FLAG_CROSS_PROFILE_WORK_THUMBNAIL)
+ ? mEnterpriseThumbnailDrawable : null;
if (thumbnailDrawable == null) {
return;
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index 880e021..ec1d4f7 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -16,12 +16,15 @@
package com.android.wm.shell.flicker
+import android.tools.PlatformConsts.DESKTOP_MODE_MINIMUM_WINDOW_HEIGHT
+import android.tools.PlatformConsts.DESKTOP_MODE_MINIMUM_WINDOW_WIDTH
import android.tools.flicker.AssertionInvocationGroup
import android.tools.flicker.assertors.assertions.AppLayerIncreasesInSize
import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart
import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible
+import android.tools.flicker.assertors.assertions.AppWindowAlignsWithOnlyOneDisplayCornerAtEnd
import android.tools.flicker.assertors.assertions.AppWindowCoversLeftHalfScreenAtEnd
import android.tools.flicker.assertors.assertions.AppWindowCoversRightHalfScreenAtEnd
import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd
@@ -29,6 +32,7 @@
import android.tools.flicker.assertors.assertions.AppWindowHasMaxDisplayHeight
import android.tools.flicker.assertors.assertions.AppWindowHasMaxDisplayWidth
import android.tools.flicker.assertors.assertions.AppWindowHasSizeOfAtLeast
+import android.tools.flicker.assertors.assertions.AppWindowInsideDisplayBoundsAtEnd
import android.tools.flicker.assertors.assertions.AppWindowIsInvisibleAtEnd
import android.tools.flicker.assertors.assertions.AppWindowIsVisibleAlways
import android.tools.flicker.assertors.assertions.AppWindowMaintainsAspectRatioAlways
@@ -181,7 +185,13 @@
.build(),
assertions =
AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
- listOf(AppWindowHasSizeOfAtLeast(DESKTOP_MODE_APP, 770, 700))
+ listOf(
+ AppWindowHasSizeOfAtLeast(
+ DESKTOP_MODE_APP,
+ DESKTOP_MODE_MINIMUM_WINDOW_WIDTH,
+ DESKTOP_MODE_MINIMUM_WINDOW_HEIGHT
+ )
+ )
.associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
@@ -300,5 +310,28 @@
AppWindowHasMaxBoundsInOnlyOneDimension(DESKTOP_MODE_APP)
).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
+
+ val CASCADE_APP =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("CASCADE_APP"),
+ extractor =
+ ShellTransitionScenarioExtractor(
+ transitionMatcher =
+ object : ITransitionMatcher {
+ override fun findAll(
+ transitions: Collection<Transition>
+ ): Collection<Transition> {
+ return transitions.filter { it.type == TransitionType.OPEN }
+ }
+ }
+ ),
+ assertions =
+ listOf(
+ AppWindowInsideDisplayBoundsAtEnd(DESKTOP_MODE_APP),
+ AppWindowOnTopAtEnd(DESKTOP_MODE_APP),
+ AppWindowBecomesVisible(DESKTOP_MODE_APP),
+ AppWindowAlignsWithOnlyOneDisplayCornerAtEnd(DESKTOP_MODE_APP)
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModeLandscape.kt
new file mode 100644
index 0000000..a07fa99
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModeLandscape.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 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
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppsInDesktopMode
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppsInDesktopModeLandscape : OpenAppsInDesktopMode(rotation = ROTATION_90) {
+ @ExpectedScenarios(["CASCADE_APP"])
+ @Test
+ override fun openApps() = super.openApps()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModePortrait.kt
new file mode 100644
index 0000000..c7a958a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModePortrait.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 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
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppsInDesktopMode
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppsInDesktopModePortrait : OpenAppsInDesktopMode() {
+ @ExpectedScenarios(["CASCADE_APP"])
+ @Test
+ override fun openApps() = super.openApps()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
index 5f759e8..f4d6414 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
@@ -19,6 +19,7 @@
import android.platform.test.annotations.Postsubmit
import android.tools.NavBar
import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
import com.android.window.flags.Flags
import com.android.wm.shell.Utils
import org.junit.After
@@ -46,6 +47,8 @@
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ tapl.enableTransientTaskbar(false)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt
new file mode 100644
index 0000000..a5c794b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 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.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class OpenAppsInDesktopMode(val rotation: Rotation = Rotation.ROTATION_0) {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val firstApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+ private val secondApp = MailAppHelper(instrumentation)
+ private val thirdApp = NewTasksAppHelper(instrumentation)
+ private val fourthApp = ImeAppHelper(instrumentation)
+ private val fifthApp = NonResizeableAppHelper(instrumentation)
+
+ @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_3BUTTON, rotation)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ tapl.enableTransientTaskbar(false)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ firstApp.enterDesktopWithDrag(wmHelper, device)
+ }
+
+ @Test
+ open fun openApps() {
+ secondApp.launchViaIntent(wmHelper)
+ thirdApp.launchViaIntent(wmHelper)
+ fourthApp.launchViaIntent(wmHelper)
+ fifthApp.launchViaIntent(wmHelper)
+ }
+
+ @After
+ fun teardown() {
+ fifthApp.exit(wmHelper)
+ fourthApp.exit(wmHelper)
+ thirdApp.exit(wmHelper)
+ secondApp.exit(wmHelper)
+ firstApp.exit(wmHelper)
+ }
+}
\ No newline at end of file
diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp
index 5f5ffe9..27add35 100644
--- a/libs/hwui/AutoBackendTextureRelease.cpp
+++ b/libs/hwui/AutoBackendTextureRelease.cpp
@@ -17,11 +17,12 @@
#include "AutoBackendTextureRelease.h"
#include <SkImage.h>
-#include <include/gpu/ganesh/SkImageGanesh.h>
-#include <include/gpu/GrDirectContext.h>
-#include <include/gpu/GrBackendSurface.h>
#include <include/gpu/MutableTextureState.h>
+#include <include/gpu/ganesh/GrBackendSurface.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/SkImageGanesh.h>
#include <include/gpu/vk/VulkanMutableTextureState.h>
+
#include "renderthread/RenderThread.h"
#include "utils/Color.h"
#include "utils/PaintUtils.h"
diff --git a/libs/hwui/AutoBackendTextureRelease.h b/libs/hwui/AutoBackendTextureRelease.h
index f0eb2a8..d58cd17 100644
--- a/libs/hwui/AutoBackendTextureRelease.h
+++ b/libs/hwui/AutoBackendTextureRelease.h
@@ -16,10 +16,10 @@
#pragma once
-#include <GrAHardwareBufferUtils.h>
-#include <GrBackendSurface.h>
#include <SkImage.h>
#include <android/hardware_buffer.h>
+#include <include/android/GrAHardwareBufferUtils.h>
+#include <include/gpu/ganesh/GrBackendSurface.h>
#include <system/graphics.h>
namespace android {
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 27ea150..a2748b0 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -21,8 +21,6 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <GLES3/gl3.h>
-#include <GrDirectContext.h>
-#include <GrTypes.h>
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkImage.h>
@@ -30,6 +28,8 @@
#include <SkImageInfo.h>
#include <SkRefCnt.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/GrTypes.h>
#include <utils/GLUtils.h>
#include <utils/NdkUtils.h>
#include <utils/Trace.h>
diff --git a/libs/hwui/Mesh.h b/libs/hwui/Mesh.h
index 8c6ca97..afc6adf 100644
--- a/libs/hwui/Mesh.h
+++ b/libs/hwui/Mesh.h
@@ -17,8 +17,8 @@
#ifndef MESH_H_
#define MESH_H_
-#include <GrDirectContext.h>
#include <SkMesh.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <include/gpu/ganesh/SkMeshGanesh.h>
#include <jni.h>
#include <log/log.h>
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index d026379..60d7f2d 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -16,9 +16,12 @@
#include "RecordingCanvas.h"
-#include <GrRecordingContext.h>
#include <SkMesh.h>
#include <hwui/Paint.h>
+#include <include/gpu/GpuTypes.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/GrRecordingContext.h>
+#include <include/gpu/ganesh/SkMeshGanesh.h>
#include <log/log.h>
#include <experimental/type_traits>
@@ -48,9 +51,6 @@
#include "Tonemapper.h"
#include "VectorDrawable.h"
#include "effects/GainmapRenderer.h"
-#include "include/gpu/GpuTypes.h" // from Skia
-#include "include/gpu/GrDirectContext.h"
-#include "include/gpu/ganesh/SkMeshGanesh.h"
#include "pipeline/skia/AnimatedDrawables.h"
#include "pipeline/skia/FunctorDrawable.h"
#ifdef __ANDROID__
diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
index 756b937..3800645 100644
--- a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
+++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
@@ -16,12 +16,11 @@
#include "ATraceMemoryDump.h"
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <utils/Trace.h>
#include <cstring>
-#include "GrDirectContext.h"
-
namespace android {
namespace uirenderer {
namespace skiapipeline {
diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h
index 777d1a2..86ff33f 100644
--- a/libs/hwui/pipeline/skia/ATraceMemoryDump.h
+++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h
@@ -16,9 +16,9 @@
#pragma once
-#include <GrDirectContext.h>
#include <SkString.h>
#include <SkTraceMemoryDump.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <string>
#include <unordered_map>
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 5d3fb30..85432bf 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -15,24 +15,26 @@
*/
#include "GLFunctorDrawable.h"
-#include <GrDirectContext.h>
+
+#include <effects/GainmapRenderer.h>
+#include <include/gpu/GpuTypes.h>
+#include <include/gpu/ganesh/GrBackendSurface.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
+#include <include/gpu/ganesh/gl/GrGLTypes.h>
#include <private/hwui/DrawGlInfo.h>
+
#include "FunctorDrawable.h"
-#include "GrBackendSurface.h"
#include "RenderNode.h"
#include "SkAndroidFrameworkUtils.h"
#include "SkCanvas.h"
#include "SkCanvasAndroid.h"
#include "SkClipStack.h"
-#include "SkRect.h"
#include "SkM44.h"
-#include <include/gpu/ganesh/SkSurfaceGanesh.h>
-#include "include/gpu/GpuTypes.h" // from Skia
-#include <include/gpu/gl/GrGLTypes.h>
-#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
-#include "utils/GLUtils.h"
-#include <effects/GainmapRenderer.h>
+#include "SkRect.h"
#include "renderthread/CanvasContext.h"
+#include "utils/GLUtils.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 99f54c1..8f3366c 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -16,17 +16,17 @@
#include "LayerDrawable.h"
+#include <include/gpu/ganesh/GrBackendSurface.h>
+#include <include/gpu/ganesh/gl/GrGLTypes.h>
#include <shaders/shaders.h>
#include <utils/Color.h>
#include <utils/MathUtils.h>
#include "DeviceInfo.h"
-#include "GrBackendSurface.h"
#include "SkColorFilter.h"
#include "SkRuntimeEffect.h"
#include "SkSurface.h"
#include "Tonemapper.h"
-#include "gl/GrGLTypes.h"
#include "math/mat4.h"
#include "system/graphics-base-v1.0.h"
#include "system/window.h"
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 8e07a2f..22f59a6 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -16,9 +16,9 @@
#include "ShaderCache.h"
-#include <GrDirectContext.h>
#include <SkData.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <log/log.h>
#include <openssl/sha.h>
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 40dfc9d..4c01161 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -17,10 +17,10 @@
#pragma once
#include <FileBlobCache.h>
-#include <GrContextOptions.h>
#include <SkRefCnt.h>
#include <cutils/compiler.h>
#include <ftl/shared_mutex.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
#include <utils/Mutex.h>
#include <memory>
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index e4b1f91..0768f45 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -16,14 +16,14 @@
#include "pipeline/skia/SkiaOpenGLPipeline.h"
-#include <GrBackendSurface.h>
#include <SkBlendMode.h>
#include <SkImageInfo.h>
#include <cutils/properties.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrBackendSurface.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
-#include <include/gpu/gl/GrGLTypes.h>
+#include <include/gpu/ganesh/gl/GrGLTypes.h>
#include <strings.h>
#include "DeferredLayerUpdater.h"
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index d06dba0..e1de1e6 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -16,14 +16,14 @@
#include "pipeline/skia/SkiaVulkanPipeline.h"
-#include <GrDirectContext.h>
-#include <GrTypes.h>
#include <SkSurface.h>
#include <SkTypes.h>
#include <cutils/properties.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/GrTypes.h>
+#include <include/gpu/ganesh/vk/GrVkTypes.h>
#include <strings.h>
-#include <vk/GrVkTypes.h>
#include "DeferredLayerUpdater.h"
#include "LightingInfo.h"
diff --git a/libs/hwui/pipeline/skia/StretchMask.h b/libs/hwui/pipeline/skia/StretchMask.h
index dc698b8..0baed9f 100644
--- a/libs/hwui/pipeline/skia/StretchMask.h
+++ b/libs/hwui/pipeline/skia/StretchMask.h
@@ -15,9 +15,10 @@
*/
#pragma once
-#include "GrRecordingContext.h"
-#include <effects/StretchEffect.h>
#include <SkSurface.h>
+#include <effects/StretchEffect.h>
+#include <include/gpu/ganesh/GrRecordingContext.h>
+
#include "SkiaDisplayList.h"
namespace android::uirenderer {
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 21fe6ff..1ebc3c8 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -19,12 +19,12 @@
#include <SkAndroidFrameworkUtils.h>
#include <SkImage.h>
#include <SkM44.h>
-#include <include/gpu/ganesh/vk/GrBackendDrawableInfo.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/vk/GrBackendDrawableInfo.h>
+#include <include/gpu/ganesh/vk/GrVkTypes.h>
#include <private/hwui/DrawVkInfo.h>
#include <utils/Color.h>
#include <utils/Trace.h>
-#include <vk/GrVkTypes.h>
#include <thread>
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index ac2a936..2771780 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -16,10 +16,10 @@
#include "CacheManager.h"
-#include <GrContextOptions.h>
-#include <GrTypes.h>
#include <SkExecutor.h>
#include <SkGraphics.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
+#include <include/gpu/ganesh/GrTypes.h>
#include <math.h>
#include <utils/Trace.h>
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index bcfa4f3..94f1005 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -18,7 +18,7 @@
#define CACHEMANAGER_H
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
-#include <GrDirectContext.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#endif
#include <SkSurface.h>
#include <utils/String8.h>
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index a024aeb..92c6ad1 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,12 +16,12 @@
#include "RenderThread.h"
-#include <GrContextOptions.h>
#include <android-base/properties.h>
#include <dlfcn.h>
-#include <gl/GrGLInterface.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
+#include <include/gpu/ganesh/gl/GrGLInterface.h>
#include <private/android/choreographer.h>
#include <sys/resource.h>
#include <ui/FatVector.h>
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 045d26f..86fddba 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -17,9 +17,9 @@
#ifndef RENDERTHREAD_H_
#define RENDERTHREAD_H_
-#include <GrDirectContext.h>
#include <SkBitmap.h>
#include <cutils/compiler.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <surface_control_private.h>
#include <utils/Thread.h>
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 4d185c6..bce84ae 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -18,19 +18,19 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include <GrBackendSemaphore.h>
-#include <GrBackendSurface.h>
-#include <GrDirectContext.h>
-#include <GrTypes.h>
#include <android/sync.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrBackendSemaphore.h>
+#include <include/gpu/ganesh/GrBackendSurface.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/GrTypes.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
#include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
+#include <include/gpu/ganesh/vk/GrVkTypes.h>
#include <include/gpu/vk/VulkanBackendContext.h>
#include <ui/FatVector.h>
-#include <vk/GrVkTypes.h>
#include <sstream>
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 08f9d42..f042571 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -20,9 +20,9 @@
#if !defined(VK_USE_PLATFORM_ANDROID_KHR)
#define VK_USE_PLATFORM_ANDROID_KHR
#endif
-#include <GrContextOptions.h>
#include <SkSurface.h>
#include <android-base/unique_fd.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
#include <utils/StrongPointer.h>
#include <vk/VulkanExtensions.h>
#include <vulkan/vulkan.h>
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 0f29613..20c2b1a 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -16,12 +16,13 @@
#include "VulkanSurface.h"
-#include <include/android/SkSurfaceAndroid.h>
-#include <GrDirectContext.h>
#include <SkSurface.h>
+#include <gui/TraceUtils.h>
+#include <include/android/SkSurfaceAndroid.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+
#include <algorithm>
-#include <gui/TraceUtils.h>
#include "VulkanManager.h"
#include "utils/Color.h"
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 0f8bd13..b714534 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <GrDirectContext.h>
#include <Properties.h>
#include <SkData.h>
#include <SkRefCnt.h>
@@ -22,6 +21,7 @@
#include <dirent.h>
#include <errno.h>
#include <gtest/gtest.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index e78dc31..b448ecd 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -16,6 +16,8 @@
package android.media;
+import static android.media.Utils.parseVibrationEffect;
+
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentProvider;
@@ -24,17 +26,23 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources.NotFoundException;
import android.database.Cursor;
+import android.media.audio.Flags;
import android.media.audiofx.HapticGenerator;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
import android.os.Trace;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
import android.provider.Settings;
import android.util.Log;
+
import com.android.internal.annotations.VisibleForTesting;
+
import java.io.IOException;
import java.util.ArrayList;
@@ -62,6 +70,11 @@
// keep references on active Ringtones until stopped or completion listener called.
private static final ArrayList<Ringtone> sActiveRingtones = new ArrayList<Ringtone>();
+ private static final VibrationAttributes VIBRATION_ATTRIBUTES =
+ new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_RINGTONE).build();
+
+ private static final int VIBRATION_LOOP_DELAY_MS = 200;
+
private final Context mContext;
private final AudioManager mAudioManager;
private VolumeShaper.Configuration mVolumeShaperConfig;
@@ -95,6 +108,10 @@
private float mVolume = 1.0f;
private boolean mHapticGeneratorEnabled = false;
private final Object mPlaybackSettingsLock = new Object();
+ private final Vibrator mVibrator;
+ private final boolean mRingtoneVibrationSupported;
+ private VibrationEffect mVibrationEffect;
+ private boolean mIsVibrating;
/** {@hide} */
@UnsupportedAppUsage
@@ -104,6 +121,8 @@
mAllowRemote = allowRemote;
mRemotePlayer = allowRemote ? mAudioManager.getRingtonePlayer() : null;
mRemoteToken = allowRemote ? new Binder() : null;
+ mVibrator = mContext.getSystemService(Vibrator.class);
+ mRingtoneVibrationSupported = Utils.isRingtoneVibrationSettingsSupported(mContext);
}
/**
@@ -487,6 +506,23 @@
if (mUri == null) {
destroyLocalPlayer();
}
+ if (Flags.enableRingtoneHapticsCustomization()
+ && mRingtoneVibrationSupported && mUri != null) {
+ mVibrationEffect = parseVibrationEffect(mVibrator, Utils.getVibrationUri(mUri));
+ if (mVibrationEffect != null) {
+ mVibrationEffect =
+ mVibrationEffect.applyRepeatingIndefinitely(true, VIBRATION_LOOP_DELAY_MS);
+ }
+ }
+ }
+
+ /**
+ * Returns the {@link VibrationEffect} has been created for this ringtone.
+ * @hide
+ */
+ @VisibleForTesting
+ public VibrationEffect getVibrationEffect() {
+ return mVibrationEffect;
}
/** {@hide} */
@@ -530,6 +566,17 @@
Log.w(TAG, "Neither local nor remote playback available");
}
}
+ if (Flags.enableRingtoneHapticsCustomization() && mRingtoneVibrationSupported) {
+ playVibration();
+ }
+ }
+
+ private void playVibration() {
+ if (mVibrationEffect == null) {
+ return;
+ }
+ mIsVibrating = true;
+ mVibrator.vibrate(mVibrationEffect, VIBRATION_ATTRIBUTES);
}
/**
@@ -545,6 +592,11 @@
Log.w(TAG, "Problem stopping ringtone: " + e);
}
}
+ if (Flags.enableRingtoneHapticsCustomization()
+ && mRingtoneVibrationSupported && mIsVibrating) {
+ mVibrator.cancel();
+ mIsVibrating = false;
+ }
}
private void destroyLocalPlayer() {
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 47e3a0f..f0ab6ec 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -34,6 +34,7 @@
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.StaleDataException;
+import android.media.audio.Flags;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
@@ -809,6 +810,12 @@
// Don't set the stream type
Ringtone ringtone = getRingtone(context, ringtoneUri, -1 /* streamType */,
volumeShaperConfig, false);
+ if (Flags.enableRingtoneHapticsCustomization()
+ && Utils.isRingtoneVibrationSettingsSupported(context)
+ && Utils.hasVibration(ringtoneUri) && hasHapticChannels(ringtoneUri)) {
+ audioAttributes = new AudioAttributes.Builder(
+ audioAttributes).setHapticChannelsMuted(true).build();
+ }
if (ringtone != null) {
ringtone.setAudioAttributesField(audioAttributes);
if (!ringtone.createLocalMediaPlayer()) {
diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java
index d07f611..41e9b65 100644
--- a/media/java/android/media/Utils.java
+++ b/media/java/android/media/Utils.java
@@ -20,12 +20,17 @@
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.os.vibrator.persistence.ParsedVibration;
+import android.os.vibrator.persistence.VibrationXmlParser;
import android.provider.OpenableColumns;
import android.util.Log;
import android.util.Pair;
@@ -36,7 +41,11 @@
import com.android.internal.annotations.GuardedBy;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
@@ -55,6 +64,8 @@
public class Utils {
private static final String TAG = "Utils";
+ public static final String VIBRATION_URI_PARAM = "vibration_uri";
+
/**
* Sorts distinct (non-intersecting) range array in ascending order.
* @throws java.lang.IllegalArgumentException if ranges are not distinct
@@ -688,4 +699,78 @@
}
return anonymizeBluetoothAddress(address);
}
+
+ /**
+ * Whether the device supports ringtone vibration settings.
+ *
+ * @param context the {@link Context}
+ * @return {@code true} if the device supports ringtone vibration
+ */
+ public static boolean isRingtoneVibrationSettingsSupported(Context context) {
+ final Resources res = context.getResources();
+ return res != null && res.getBoolean(
+ com.android.internal.R.bool.config_ringtoneVibrationSettingsSupported);
+ }
+
+ /**
+ * Whether the given ringtone Uri has vibration Uri parameter
+ *
+ * @param ringtoneUri the ringtone Uri
+ * @return {@code true} if the Uri has vibration parameter
+ */
+ public static boolean hasVibration(Uri ringtoneUri) {
+ final String vibrationUriString = ringtoneUri.getQueryParameter(VIBRATION_URI_PARAM);
+ return vibrationUriString != null;
+ }
+
+ /**
+ * Gets the vibration Uri from given ringtone Uri
+ *
+ * @param ringtoneUri the ringtone Uri
+ * @return parsed {@link Uri} of vibration parameter, {@code null} if the vibration parameter
+ * is not found.
+ */
+ public static Uri getVibrationUri(Uri ringtoneUri) {
+ final String vibrationUriString = ringtoneUri.getQueryParameter(VIBRATION_URI_PARAM);
+ if (vibrationUriString == null) {
+ return null;
+ }
+ return Uri.parse(vibrationUriString);
+ }
+
+ /**
+ * Returns the parsed {@link VibrationEffect} from given vibration Uri.
+ *
+ * @param vibrator the vibrator to resolve the vibration file
+ * @param vibrationUri the vibration file Uri to represent a vibration
+ */
+ @SuppressWarnings("FlaggedApi") // VibrationXmlParser is available internally as hidden APIs.
+ public static VibrationEffect parseVibrationEffect(Vibrator vibrator, Uri vibrationUri) {
+ if (vibrationUri == null) {
+ Log.w(TAG, "The vibration Uri is null.");
+ return null;
+ }
+ String filePath = vibrationUri.getPath();
+ if (filePath == null) {
+ Log.w(TAG, "The file path is null.");
+ return null;
+ }
+ File vibrationFile = new File(filePath);
+ if (vibrationFile.exists() && vibrationFile.canRead()) {
+ try {
+ FileInputStream fileInputStream = new FileInputStream(vibrationFile);
+ ParsedVibration parsedVibration =
+ VibrationXmlParser.parseDocument(
+ new InputStreamReader(fileInputStream, StandardCharsets.UTF_8));
+ return parsedVibration.resolve(vibrator);
+ } catch (IOException e) {
+ Log.e(TAG, "FileNotFoundException" + e);
+ }
+ } else {
+ // File not found or cannot be read
+ Log.w(TAG, "File exists:" + vibrationFile.exists()
+ + ", canRead:" + vibrationFile.canRead());
+ }
+ return null;
+ }
}
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 0f97b2c..cdf8f89 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -97,6 +97,7 @@
public final class CardEmulation {
method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static android.content.ComponentName getPreferredPaymentService(@NonNull android.content.Context);
method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int);
+ method @FlaggedApi("android.nfc.enable_card_emulation_euicc") public boolean isEuiccSupported();
method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void overrideRoutingTable(@NonNull android.app.Activity, int, int);
method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void recoverRoutingTable(@NonNull android.app.Activity);
}
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index 79f1275..19b9e0f 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -50,4 +50,5 @@
void overrideRoutingTable(int userHandle, String protocol, String technology, in String pkg);
void recoverRoutingTable(int userHandle);
+ boolean isEuiccSupported();
}
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 22ae612..f478793 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -721,7 +721,7 @@
*
* @return List<String> containing secure elements on the device which supports
* off host card emulation. eSE for Embedded secure element,
- * SIM for UICC and so on.
+ * SIM for UICC, eSIM for EUICC and so on.
* @hide
*/
public @NonNull List<String> getSupportedOffHostSecureElements() {
@@ -741,6 +741,11 @@
if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE)) {
offHostSE.add("eSE");
}
+ if (Flags.enableCardEmulationEuicc()
+ && callServiceReturn(
+ () -> sCardEmulationService.isEuiccSupported(), false)) {
+ offHostSE.add("eSIM");
+ }
return offHostSE;
}
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index 3cf0a4d..ea5a036 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -308,6 +308,8 @@
mOffHostName = "eSE1";
} else if (mOffHostName.equals("SIM")) {
mOffHostName = "SIM1";
+ } else if (Flags.enableCardEmulationEuicc() && mOffHostName.equals("eSIM")) {
+ mOffHostName = "eSIM1";
}
}
mStaticOffHostName = mOffHostName;
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index 497309c..a72a896 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -548,11 +548,13 @@
List<String> validSE = adapter.getSupportedOffHostSecureElements();
if ((offHostSecureElement.startsWith("eSE") && !validSE.contains("eSE"))
- || (offHostSecureElement.startsWith("SIM") && !validSE.contains("SIM"))) {
+ || (offHostSecureElement.startsWith("SIM") && !validSE.contains("SIM"))
+ || (offHostSecureElement.startsWith("eSIM") && !validSE.contains("eSIM"))) {
return false;
}
- if (!offHostSecureElement.startsWith("eSE") && !offHostSecureElement.startsWith("SIM")) {
+ if (!offHostSecureElement.startsWith("eSE") && !offHostSecureElement.startsWith("SIM")
+ && !(Flags.enableCardEmulationEuicc() && offHostSecureElement.startsWith("eSIM"))) {
return false;
}
@@ -560,6 +562,8 @@
offHostSecureElement = "eSE1";
} else if (offHostSecureElement.equals("SIM")) {
offHostSecureElement = "SIM1";
+ } else if (Flags.enableCardEmulationEuicc() && offHostSecureElement.equals("eSIM")) {
+ offHostSecureElement = "eSIM1";
}
final String offHostSecureElementV = new String(offHostSecureElement);
return callServiceReturn(() ->
@@ -985,6 +989,18 @@
}
/**
+ * Is EUICC supported as a Secure Element EE which supports off host card emulation.
+ *
+ * @return true if the device supports EUICC for off host card emulation, false otherwise.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC)
+ public boolean isEuiccSupported() {
+ return callServiceReturn(() -> sService.isEuiccSupported(), false);
+ }
+
+ /**
* Returns the value of {@link Settings.Secure#NFC_PAYMENT_DEFAULT_COMPONENT}.
*
* @param context A context
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index 0ade4db..cc9a97c 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -148,4 +148,12 @@
namespace: "nfc"
description: "Enable watchdog for the NFC system process"
bug: "362937338"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "enable_card_emulation_euicc"
+ is_exported: true
+ namespace: "nfc"
+ description: "Enable EUICC card emulation"
+ bug: "321314635"
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
index d9f82e8..15def72 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
@@ -40,3 +40,5 @@
}
}
}
+
+const val isSpaExpressiveEnabled = false
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
index 2fac576..8619f31 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
@@ -17,11 +17,18 @@
package com.android.settingslib.spa.widget.ui
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material3.Icon
import androidx.compose.material3.Switch
+import androidx.compose.material3.SwitchDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.android.settingslib.spa.framework.compose.contentDescription
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog
@Composable
@@ -39,13 +46,42 @@
modifier = Modifier.contentDescription(contentDescription),
enabled = changeable(),
interactionSource = interactionSource,
- )
+ thumbContent =
+ if (isSpaExpressiveEnabled) {
+ if (checked) {
+ {
+ Icon(
+ imageVector = Icons.Filled.Check,
+ contentDescription = null,
+ modifier = Modifier.size(SwitchDefaults.IconSize),
+ )
+ }
+ } else {
+ {
+ Icon(
+ imageVector = Icons.Filled.Close,
+ contentDescription = null,
+ modifier = Modifier.size(SwitchDefaults.IconSize),
+ )
+ }
+ }
+ } else null)
} else {
Switch(
checked = false,
onCheckedChange = null,
enabled = false,
interactionSource = interactionSource,
+ thumbContent =
+ if (isSpaExpressiveEnabled) {
+ {
+ Icon(
+ imageVector = Icons.Filled.Close,
+ contentDescription = null,
+ modifier = Modifier.size(SwitchDefaults.IconSize),
+ )
+ }
+ } else null
)
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 0ffb763..616ab07 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -104,6 +104,22 @@
/**
* @param context to access resources from
* @param cachedDevice to get class from
+ * @return pair containing the drawable and the description of the type of the device. The type
+ * could either derived from metadata or CoD.
+ */
+ public static Pair<Drawable, String> getDerivedBtClassDrawableWithDescription(
+ Context context, CachedBluetoothDevice cachedDevice) {
+ return BluetoothUtils.isAdvancedUntetheredDevice(cachedDevice.getDevice())
+ ? new Pair<>(
+ getBluetoothDrawable(
+ context, com.android.internal.R.drawable.ic_bt_headphones_a2dp),
+ context.getString(R.string.bluetooth_talkback_headphone))
+ : BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice);
+ }
+
+ /**
+ * @param context to access resources from
+ * @param cachedDevice to get class from
* @return pair containing the drawable and the description of the Bluetooth class of the
* device.
*/
@@ -731,9 +747,7 @@
int broadcastId = broadcast.getLatestBroadcastId();
return !sourceList.isEmpty()
&& broadcastId != UNKNOWN_VALUE_PLACEHOLDER
- && sourceList.stream()
- .anyMatch(
- source -> isSourceMatched(source, broadcastId));
+ && sourceList.stream().anyMatch(source -> isSourceMatched(source, broadcastId));
}
/** Checks the connectivity status based on the provided broadcast receive state. */
@@ -1030,8 +1044,7 @@
cachedDevice.getAddress());
break;
case BluetoothProfile.LE_AUDIO:
- if (audioDeviceCategory
- == AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER) {
+ if (audioDeviceCategory == AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER) {
saDevice =
new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
diff --git a/packages/SettingsLib/src/com/android/settingslib/view/accessibility/domain/interactor/CaptioningInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/view/accessibility/domain/interactor/CaptioningInteractor.kt
deleted file mode 100644
index 858c8b3..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/view/accessibility/domain/interactor/CaptioningInteractor.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2024 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.settingslib.view.accessibility.domain.interactor
-
-import com.android.settingslib.view.accessibility.data.repository.CaptioningRepository
-import kotlinx.coroutines.flow.StateFlow
-
-class CaptioningInteractor(private val repository: CaptioningRepository) {
-
- val isSystemAudioCaptioningEnabled: StateFlow<Boolean>
- get() = repository.isSystemAudioCaptioningEnabled
-
- val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean>
- get() = repository.isSystemAudioCaptioningUiEnabled
-
- suspend fun setIsSystemAudioCaptioningEnabled(enabled: Boolean) =
- repository.setIsSystemAudioCaptioningEnabled(enabled)
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index a0e764a..8eedb35 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -46,6 +46,7 @@
import android.provider.Settings;
import android.util.Pair;
+import com.android.internal.R;
import com.android.settingslib.widget.AdaptiveIcon;
import com.google.common.collect.ImmutableList;
@@ -118,6 +119,34 @@
}
@Test
+ public void
+ getDerivedBtClassDrawableWithDescription_isAdvancedUntetheredDevice_returnHeadset() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+ .thenReturn(BOOL_METADATA.getBytes());
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ Pair<Drawable, String> pair =
+ BluetoothUtils.getDerivedBtClassDrawableWithDescription(
+ mContext, mCachedBluetoothDevice);
+
+ verify(mContext).getDrawable(R.drawable.ic_bt_headphones_a2dp);
+ }
+
+ @Test
+ public void
+ getDerivedBtClassDrawableWithDescription_notAdvancedUntetheredDevice_returnPhone() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+ .thenReturn("false".getBytes());
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass())
+ .thenReturn(BluetoothClass.Device.Major.PHONE);
+ Pair<Drawable, String> pair =
+ BluetoothUtils.getDerivedBtClassDrawableWithDescription(
+ mContext, mCachedBluetoothDevice);
+
+ verify(mContext).getDrawable(R.drawable.ic_phone);
+ }
+
+ @Test
public void getBtClassDrawableWithDescription_typePhone_returnPhoneDrawable() {
when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass())
.thenReturn(BluetoothClass.Device.Major.PHONE);
@@ -681,8 +710,8 @@
when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList);
assertThat(
- BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(
- mBluetoothDevice, mLocalBluetoothManager))
+ BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(
+ mBluetoothDevice, mLocalBluetoothManager))
.isTrue();
}
@@ -694,12 +723,11 @@
when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList);
assertThat(
- BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(
- mBluetoothDevice, mLocalBluetoothManager))
+ BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(
+ mBluetoothDevice, mLocalBluetoothManager))
.isFalse();
}
-
@Test
public void isAvailableHearingDevice_isConnectedHearingAid_returnTure() {
when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true);
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index be748da..d1a59af 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1340,15 +1340,6 @@
}
flag {
- name: "lockscreen_preview_renderer_create_on_main_thread"
- namespace: "systemui"
- description: "Force preview renderer to be created on the main thread"
- bug: "343732179"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-flag {
name: "classic_flags_multi_user"
namespace: "systemui"
description: "Make the classic feature flag loading multi user aware."
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index be6a0f9..201c419 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -196,7 +196,12 @@
val gridState =
rememberLazyGridState(viewModel.savedFirstScrollIndex, viewModel.savedFirstScrollOffset)
- viewModel.clearPersistedScrollPosition()
+
+ LaunchedEffect(Unit) {
+ if (!viewModel.isEditMode) {
+ viewModel.clearPersistedScrollPosition()
+ }
+ }
val contentListState = rememberContentListState(widgetConfigurator, communalContent, viewModel)
val reorderingWidgets by viewModel.reorderingWidgets.collectAsStateWithLifecycle()
@@ -219,7 +224,6 @@
val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
val screenWidth = windowMetrics.bounds.width()
val layoutDirection = LocalLayoutDirection.current
-
if (viewModel.isEditMode) {
ObserveNewWidgetAddedEffect(communalContent, gridState, viewModel)
} else {
@@ -543,7 +547,6 @@
communalContent: List<CommunalContentModel>,
gridState: LazyGridState,
) {
- val coroutineScope = rememberCoroutineScope()
val liveContentKeys = remember { mutableListOf<String>() }
var communalContentPending by remember { mutableStateOf(true) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
index c60e11e..ecb3d8c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
@@ -16,7 +16,6 @@
package com.android.systemui.keyguard.ui.composable
-import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween
@@ -73,8 +72,6 @@
initialValue = null
)
- BackHandler(enabled = isVisible) { alternateBouncerDependencies.viewModel.onBackRequested() }
-
AnimatedVisibility(
visible = isVisible,
enter = fadeIn(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 3e73057..8d5189e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -73,9 +73,7 @@
val isShadeLayoutWide by viewModel.isShadeLayoutWide.collectAsStateWithLifecycle()
val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
val areNotificationsVisible by
- viewModel
- .areNotificationsVisible(contentKey)
- .collectAsStateWithLifecycle(initialValue = false)
+ viewModel.areNotificationsVisible().collectAsStateWithLifecycle(initialValue = false)
val isBypassEnabled by viewModel.isBypassEnabled.collectAsStateWithLifecycle()
if (isBypassEnabled) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index a7e41ce..3cb0d8a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -84,7 +84,6 @@
enableInterruptions = false,
)
}
- val currentSceneKey = state.transitionState.currentScene
DisposableEffect(state) {
val dataSource = SceneTransitionLayoutDataSource(state, coroutineScope)
@@ -97,19 +96,26 @@
onDispose { viewModel.setTransitionState(null) }
}
+ val actionableContentKey =
+ viewModel.getActionableContentKey(state.currentScene, state.currentOverlays, overlayByKey)
val userActionsByContentKey: MutableMap<ContentKey, Map<UserAction, UserActionResult>> =
remember {
mutableStateMapOf()
}
- // TODO(b/359173565): Add overlay user actions when the API is final.
- LaunchedEffect(currentSceneKey) {
+ LaunchedEffect(actionableContentKey) {
try {
- sceneByKey[currentSceneKey]?.userActions?.collectLatest { userActions ->
- userActionsByContentKey[currentSceneKey] =
+ val actionableContent: ActionableContent =
+ checkNotNull(
+ overlayByKey[actionableContentKey] ?: sceneByKey[actionableContentKey]
+ ) {
+ "invalid ContentKey: $actionableContentKey"
+ }
+ actionableContent.userActions.collectLatest { userActions ->
+ userActionsByContentKey[actionableContentKey] =
viewModel.resolveSceneFamilies(userActions)
}
} finally {
- userActionsByContentKey[currentSceneKey] = emptyMap()
+ userActionsByContentKey[actionableContentKey] = emptyMap()
}
}
@@ -139,13 +145,16 @@
}
}
}
- overlayByKey.forEach { (overlayKey, composableOverlay) ->
+ overlayByKey.forEach { (overlayKey, overlay) ->
overlay(
key = overlayKey,
userActions = userActionsByContentKey.getOrDefault(overlayKey, emptyMap())
) {
+ // Activate the overlay.
+ LaunchedEffect(overlay) { overlay.activate() }
+
// Render the overlay.
- with(composableOverlay) { this@overlay.Content(Modifier) }
+ with(overlay) { this@overlay.Content(Modifier) }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index a0ebca2..5a350a6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -2,6 +2,7 @@
import androidx.compose.foundation.gestures.Orientation
import com.android.compose.animation.scene.ProgressConverter
+import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.transitions
import com.android.systemui.bouncer.ui.composable.Bouncer
import com.android.systemui.notifications.ui.composable.Notifications
@@ -9,6 +10,7 @@
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
import com.android.systemui.scene.ui.composable.transitions.bouncerToGoneTransition
+import com.android.systemui.scene.ui.composable.transitions.bouncerToLockscreenPreview
import com.android.systemui.scene.ui.composable.transitions.goneToNotificationsShadeTransition
import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsShadeTransition
import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsTransition
@@ -59,6 +61,14 @@
}
from(Scenes.Lockscreen, to = Scenes.Bouncer) { lockscreenToBouncerTransition() }
+ from(
+ Scenes.Lockscreen,
+ to = Scenes.Bouncer,
+ key = TransitionKey.PredictiveBack,
+ reversePreview = { bouncerToLockscreenPreview() }
+ ) {
+ lockscreenToBouncerTransition()
+ }
from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() }
from(Scenes.Lockscreen, to = Scenes.NotificationsShade) {
lockscreenToNotificationsShadeTransition()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt
index 022eb1f..ac54896 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt
@@ -1,5 +1,6 @@
package com.android.systemui.scene.ui.composable.transitions
+import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.tween
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.TransitionBuilder
@@ -18,3 +19,9 @@
fade(Bouncer.Elements.Content)
}
}
+
+fun TransitionBuilder.bouncerToLockscreenPreview() {
+ fractionRange(easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)) {
+ scaleDraw(Bouncer.Elements.Content, scaleY = 0.8f, scaleX = 0.8f)
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index a03bf43..df22264 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -303,6 +303,8 @@
viewModel.qsSceneAdapter,
)
}
+ val shadeHorizontalPadding =
+ dimensionResource(id = R.dimen.notification_panel_margin_horizontal)
val shadeMeasurePolicy =
remember(mediaInRow) {
SingleShadeMeasurePolicy(
@@ -354,6 +356,7 @@
Box(
Modifier.element(QuickSettings.Elements.QuickQuickSettings)
.layoutId(SingleShadeMeasurePolicy.LayoutId.QuickSettings)
+ .padding(horizontal = shadeHorizontalPadding)
) {
QuickSettings(
viewModel.qsSceneAdapter,
@@ -381,7 +384,9 @@
shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
onEmptySpaceClick =
viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
- modifier = Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.Notifications),
+ modifier =
+ Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.Notifications)
+ .padding(horizontal = shadeHorizontalPadding),
)
},
measurePolicy = shadeMeasurePolicy,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 56c08b9..a076f22 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -154,11 +154,11 @@
* An element associated to [ElementNode]. Note that this element does not support updates as its
* arguments should always be the same.
*/
-private data class ElementModifier(
- private val layoutImpl: SceneTransitionLayoutImpl,
+internal data class ElementModifier(
+ internal val layoutImpl: SceneTransitionLayoutImpl,
private val currentTransitionStates: List<TransitionState>,
- private val content: Content,
- private val key: ElementKey,
+ internal val content: Content,
+ internal val key: ElementKey,
) : ModifierNodeElement<ElementNode>() {
override fun create(): ElementNode =
ElementNode(layoutImpl, currentTransitionStates, content, key)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index 5780c08..0d05f4e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -221,19 +221,59 @@
private suspend fun PointerInputScope.pointerTracker() {
val currentContext = currentCoroutineContext()
awaitPointerEventScope {
+ var velocityPointerId: PointerId? = null
// Intercepts pointer inputs and exposes [PointersInfo], via
// [requireAncestorPointersInfoOwner], to our descendants.
while (currentContext.isActive) {
// During the Initial pass, we receive the event after our ancestors.
- val pointers = awaitPointerEvent(PointerEventPass.Initial).changes
- pointersDown = pointers.countDown()
- if (pointersDown == 0) {
- // There are no more pointers down
- startedPosition = null
- } else if (startedPosition == null) {
- startedPosition = pointers.first().position
- if (enabled()) {
- onFirstPointerDown()
+ val changes = awaitPointerEvent(PointerEventPass.Initial).changes
+ pointersDown = changes.countDown()
+
+ when {
+ // There are no more pointers down.
+ pointersDown == 0 -> {
+ startedPosition = null
+
+ // This is the last pointer up
+ velocityTracker.addPointerInputChange(changes.single())
+ }
+
+ // The first pointer down, startedPosition was not set.
+ startedPosition == null -> {
+ val firstPointerDown = changes.single()
+ velocityPointerId = firstPointerDown.id
+ velocityTracker.resetTracking()
+ velocityTracker.addPointerInputChange(firstPointerDown)
+ startedPosition = firstPointerDown.position
+ if (enabled()) {
+ onFirstPointerDown()
+ }
+ }
+
+ // Changes with at least one pointer
+ else -> {
+ val pointerChange = changes.first()
+
+ // Assuming that the list of changes doesn't have two changes with the same
+ // id (PointerId), we can check:
+ // - If the first change has `id` equals to `velocityPointerId` (this should
+ // always be true unless the pointer has been removed).
+ // - If it does, we've found our change event (assuming there aren't any
+ // others changes with the same id in this PointerEvent - not checked).
+ // - If it doesn't, we can check that the change with that id isn't in first
+ // place (which should never happen - this will crash).
+ check(
+ pointerChange.id == velocityPointerId ||
+ !changes.fastAny { it.id == velocityPointerId }
+ ) {
+ "$velocityPointerId is present, but not the first: $changes"
+ }
+
+ // If the previous pointer has been removed, we use the first available
+ // change to keep tracking the velocity.
+ velocityPointerId = pointerChange.id
+
+ velocityTracker.addPointerInputChange(pointerChange)
}
}
}
@@ -253,11 +293,9 @@
orientation = orientation,
startDragImmediately = startDragImmediately,
onDragStart = { startedPosition, overSlop, pointersDown ->
- velocityTracker.resetTracking()
onDragStarted(startedPosition, overSlop, pointersDown)
},
- onDrag = { controller, change, amount ->
- velocityTracker.addPointerInputChange(change)
+ onDrag = { controller, amount ->
dispatchScrollEvents(
availableOnPreScroll = amount,
onScroll = { controller.onDrag(it) },
@@ -403,7 +441,7 @@
startDragImmediately: (startedPosition: Offset) -> Boolean,
onDragStart:
(startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
- onDrag: (controller: DragController, change: PointerInputChange, dragAmount: Float) -> Unit,
+ onDrag: (controller: DragController, dragAmount: Float) -> Unit,
onDragEnd: (controller: DragController) -> Unit,
onDragCancel: (controller: DragController) -> Unit,
swipeDetector: SwipeDetector,
@@ -482,14 +520,14 @@
val successful: Boolean
try {
- onDrag(controller, drag, overSlop)
+ onDrag(controller, overSlop)
successful =
drag(
initialPointerId = drag.id,
hasDragged = { it.positionChangeIgnoreConsumed().toFloat() != 0f },
onDrag = {
- onDrag(controller, it, it.positionChange().toFloat())
+ onDrag(controller, it.positionChange().toFloat())
it.consume()
},
onIgnoredEvent = {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index bd21a69..8ae3a11 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -46,8 +46,21 @@
}
}
+ /** The current overlays. */
+ fun currentOverlays(): Flow<Set<OverlayKey>> {
+ return when (this) {
+ is Idle -> flowOf(currentOverlays)
+ is Transition -> currentOverlays
+ }
+ }
+
/** No transition/animation is currently running. */
- data class Idle(val currentScene: SceneKey) : ObservableTransitionState
+ data class Idle
+ @JvmOverloads
+ constructor(
+ val currentScene: SceneKey,
+ val currentOverlays: Set<OverlayKey> = emptySet(),
+ ) : ObservableTransitionState
/** There is a transition animating between two scenes. */
sealed class Transition(
@@ -94,7 +107,7 @@
val fromScene: SceneKey,
val toScene: SceneKey,
val currentScene: Flow<SceneKey>,
- currentOverlays: Flow<Set<OverlayKey>>,
+ currentOverlays: Set<OverlayKey>,
progress: Flow<Float>,
isInitiatedByUserInput: Boolean,
isUserInputOngoing: Flow<Boolean>,
@@ -104,7 +117,7 @@
Transition(
fromScene,
toScene,
- currentOverlays,
+ flowOf(currentOverlays),
progress,
isInitiatedByUserInput,
isUserInputOngoing,
@@ -169,7 +182,7 @@
isUserInputOngoing: Flow<Boolean>,
previewProgress: Flow<Float> = flowOf(0f),
isInPreviewStage: Flow<Boolean> = flowOf(false),
- currentOverlays: Flow<Set<OverlayKey>> = flowOf(emptySet()),
+ currentOverlays: Set<OverlayKey> = emptySet(),
): ChangeScene {
return ChangeScene(
fromScene,
@@ -205,13 +218,17 @@
fun SceneTransitionLayoutState.observableTransitionState(): Flow<ObservableTransitionState> {
return snapshotFlow {
when (val state = transitionState) {
- is TransitionState.Idle -> ObservableTransitionState.Idle(state.currentScene)
+ is TransitionState.Idle ->
+ ObservableTransitionState.Idle(
+ state.currentScene,
+ state.currentOverlays,
+ )
is TransitionState.Transition.ChangeScene -> {
ObservableTransitionState.Transition.ChangeScene(
fromScene = state.fromScene,
toScene = state.toScene,
currentScene = snapshotFlow { state.currentScene },
- currentOverlays = flowOf(state.currentOverlays),
+ currentOverlays = state.currentOverlays,
progress = snapshotFlow { state.progress },
isInitiatedByUserInput = state.isInitiatedByUserInput,
isUserInputOngoing = snapshotFlow { state.isUserInputOngoing },
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/testing/ElementStateAccess.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/testing/ElementStateAccess.kt
new file mode 100644
index 0000000..cade9bf
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/testing/ElementStateAccess.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 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.compose.animation.scene.testing
+
+import androidx.compose.ui.semantics.SemanticsNode
+import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.Element.Companion.AlphaUnspecified
+import com.android.compose.animation.scene.ElementModifier
+import com.android.compose.animation.scene.Scale
+
+val SemanticsNode.lastAlphaForTesting: Float?
+ get() = elementState.lastAlpha.takeIf { it != AlphaUnspecified }
+
+val SemanticsNode.lastScaleForTesting: Scale?
+ get() = elementState.lastScale.takeIf { it != Scale.Unspecified }
+
+private val SemanticsNode.elementState: Element.State
+ get() {
+ val elementModifier =
+ layoutInfo
+ .getModifierInfo()
+ .map { it.modifier }
+ .filterIsInstance<ElementModifier>()
+ .firstOrNull()
+ requireNotNull(elementModifier) {
+ "No ElementModifier found. Did you use the Modifier.element(...)?"
+ }
+ return with(elementModifier) {
+ val element =
+ requireNotNull(layoutImpl.elements[key]) {
+ "No element found in STL layout with key: ${key.testTag}"
+ }
+ requireNotNull(element.stateByContent[content.key]) {
+ "No state found for given content key: ${content.key.testTag}"
+ }
+ }
+ }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index d742592..bf192e7 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -38,12 +38,15 @@
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.test.TouchInjectionScope
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Velocity
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
+import kotlin.properties.Delegates
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.isActive
import org.junit.Rule
@@ -719,4 +722,88 @@
assertThat(availableOnPreFling).isEqualTo(consumedOnDragStop)
assertThat(availableOnPostFling).isEqualTo(0f)
}
+
+ @Test
+ fun multiPointerOnStopVelocity() {
+ val size = 200f
+ val middle = Offset(size / 2f, size / 2f)
+
+ var stopped = false
+ var lastVelocity = -1f
+ var touchSlop = 0f
+ var density: Density by Delegates.notNull()
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ density = LocalDensity.current
+ Box(
+ Modifier.size(with(density) { Size(size, size).toDpSize() })
+ .nestedScrollDispatcher()
+ .multiPointerDraggable(
+ orientation = Orientation.Vertical,
+ enabled = { true },
+ startDragImmediately = { false },
+ onDragStarted = { _, _, _ ->
+ SimpleDragController(
+ onDrag = { /* do nothing */ },
+ onStop = {
+ stopped = true
+ lastVelocity = it
+ },
+ )
+ },
+ dispatcher = defaultDispatcher,
+ )
+ )
+ }
+
+ var eventMillis: Long by Delegates.notNull()
+ rule.onRoot().performTouchInput { eventMillis = eventPeriodMillis }
+
+ fun swipeGesture(block: TouchInjectionScope.() -> Unit) {
+ stopped = false
+ rule.onRoot().performTouchInput {
+ down(middle)
+ block()
+ up()
+ }
+ assertThat(stopped).isEqualTo(true)
+ }
+
+ val shortDistance = touchSlop / 2f
+ swipeGesture {
+ moveBy(delta = Offset(0f, shortDistance), delayMillis = eventMillis)
+ moveBy(delta = Offset(0f, shortDistance), delayMillis = eventMillis)
+ }
+ assertThat(lastVelocity).isGreaterThan(0f)
+ assertThat(lastVelocity).isWithin(1f).of((shortDistance / eventMillis) * 1000f)
+
+ val longDistance = touchSlop * 4f
+ swipeGesture {
+ moveBy(delta = Offset(0f, longDistance), delayMillis = eventMillis)
+ moveBy(delta = Offset(0f, longDistance), delayMillis = eventMillis)
+ }
+ assertThat(lastVelocity).isGreaterThan(0f)
+ assertThat(lastVelocity).isWithin(1f).of((longDistance / eventMillis) * 1000f)
+
+ rule.onRoot().performTouchInput {
+ down(pointerId = 0, position = middle)
+ down(pointerId = 1, position = middle)
+ moveBy(pointerId = 0, delta = Offset(0f, longDistance), delayMillis = eventMillis)
+ moveBy(pointerId = 0, delta = Offset(0f, longDistance), delayMillis = eventMillis)
+ // The velocity should be:
+ // (longDistance / eventMillis) pixels/ms
+
+ // 1 pointer left, the second one
+ up(pointerId = 0)
+
+ // After a few events the velocity should be:
+ // (shortDistance / eventMillis) pixels/ms
+ repeat(10) {
+ moveBy(pointerId = 1, delta = Offset(0f, shortDistance), delayMillis = eventMillis)
+ }
+ up(pointerId = 1)
+ }
+ assertThat(lastVelocity).isGreaterThan(0f)
+ assertThat(lastVelocity).isWithin(1f).of((shortDistance / eventMillis) * 1000f)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
new file mode 100644
index 0000000..e4a87ba
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 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.compose.animation.scene.testing
+
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.Scale
+import com.android.compose.animation.scene.TestElements
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.android.compose.animation.scene.TestScenes.SceneB
+import com.android.compose.animation.scene.testTransition
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ElementStateAccessTest {
+
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun testElementStateAccess() {
+ rule.testTransition(
+ fromSceneContent = { Box(Modifier.element(TestElements.Foo).size(50.dp)) },
+ toSceneContent = { Box(Modifier) },
+ transition = {
+ spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
+ fade(TestElements.Foo)
+ scaleDraw(TestElements.Foo, scaleX = 0f, scaleY = 0f)
+ },
+ fromScene = SceneA,
+ toScene = SceneB,
+ ) {
+ before {
+ val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
+ assertThat(semanticNode.lastAlphaForTesting).isEqualTo(1f)
+ assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(1f, 1f))
+ }
+
+ at(32) {
+ val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
+ assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0.5f)
+ assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0.5f, 0.5f))
+ }
+
+ at(64) {
+ val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
+ assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0f)
+ assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0f, 0f))
+ }
+ after { onElement(TestElements.Foo).assertDoesNotExist() }
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryTest.kt
similarity index 95%
rename from packages/SettingsLib/tests/integ/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryTest.kt
index a5233e7..dd85d9b 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryTest.kt
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.settingslib.view.accessibility.data.repository
+package com.android.systemui.accessibility.data.repository
import android.view.accessibility.CaptioningManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -40,7 +41,7 @@
@SmallTest
@Suppress("UnspecifiedRegisterReceiverFlag")
@RunWith(AndroidJUnit4::class)
-class CaptioningRepositoryTest {
+class CaptioningRepositoryTest : SysuiTestCase() {
@Captor
private lateinit var listenerCaptor: ArgumentCaptor<CaptioningManager.CaptioningChangeListener>
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index 7426574..7dd7174 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -1172,6 +1172,43 @@
}
@Test
+ fun testDreamActivityGesturesNotBlockedDreamEndedBeforeKeyguardStateChanged() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*isPreview*/,
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+
+ val matcherCaptor = argumentCaptor<TaskMatcher>()
+ verify(gestureInteractor)
+ .addGestureBlockedMatcher(matcherCaptor.capture(), eq(GestureInteractor.Scope.Global))
+ val matcher = matcherCaptor.firstValue
+
+ val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM)
+ assertThat(matcher.matches(dreamTaskInfo)).isTrue()
+
+ client.endDream()
+ mMainExecutor.runAllReady()
+ clearInvocations(gestureInteractor)
+
+ val callbackCaptor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
+ verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture())
+
+ // Notification shade opens.
+ callbackCaptor.value.onShadeExpandedChanged(true)
+ mMainExecutor.runAllReady()
+
+ verify(gestureInteractor)
+ .removeGestureBlockedMatcher(eq(matcher), eq(GestureInteractor.Scope.Global))
+ }
+
+ @Test
fun testComponentsRecreatedBetweenDreams() {
clearInvocations(
mDreamComplicationComponentFactory,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index 8236eec..6f7e9d3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -128,8 +128,7 @@
fun areNotificationsVisible_splitShadeTrue_true() =
with(kosmos) {
testScope.runTest {
- val areNotificationsVisible by
- collectLastValue(underTest.areNotificationsVisible(Scenes.Lockscreen))
+ val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
shadeRepository.setShadeLayoutWide(true)
fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)
@@ -142,36 +141,7 @@
fun areNotificationsVisible_dualShadeWideOnLockscreen_true() =
with(kosmos) {
testScope.runTest {
- val areNotificationsVisible by
- collectLastValue(underTest.areNotificationsVisible(Scenes.Lockscreen))
- shadeRepository.setShadeLayoutWide(true)
- fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)
-
- assertThat(areNotificationsVisible).isTrue()
- }
- }
-
- @Test
- @EnableFlags(DualShade.FLAG_NAME)
- fun areNotificationsVisible_dualShadeWideOnNotificationsShade_false() =
- with(kosmos) {
- testScope.runTest {
- val areNotificationsVisible by
- collectLastValue(underTest.areNotificationsVisible(Scenes.NotificationsShade))
- shadeRepository.setShadeLayoutWide(true)
- fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)
-
- assertThat(areNotificationsVisible).isFalse()
- }
- }
-
- @Test
- @EnableFlags(DualShade.FLAG_NAME)
- fun areNotificationsVisible_dualShadeWideOnQuickSettingsShade_true() =
- with(kosmos) {
- testScope.runTest {
- val areNotificationsVisible by
- collectLastValue(underTest.areNotificationsVisible(Scenes.QuickSettingsShade))
+ val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
shadeRepository.setShadeLayoutWide(true)
fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)
@@ -184,8 +154,7 @@
fun areNotificationsVisible_withSmallClock_true() =
with(kosmos) {
testScope.runTest {
- val areNotificationsVisible by
- collectLastValue(underTest.areNotificationsVisible(Scenes.Lockscreen))
+ val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
fakeKeyguardClockRepository.setClockSize(ClockSize.SMALL)
assertThat(areNotificationsVisible).isTrue()
}
@@ -196,8 +165,7 @@
fun areNotificationsVisible_withLargeClock_false() =
with(kosmos) {
testScope.runTest {
- val areNotificationsVisible by
- collectLastValue(underTest.areNotificationsVisible(Scenes.Lockscreen))
+ val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)
assertThat(areNotificationsVisible).isFalse()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 4f7c013..2d42c42 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -18,7 +18,6 @@
package com.android.systemui.scene
-import android.telecom.TelecomManager
import android.telephony.TelephonyManager
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -28,54 +27,43 @@
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserActionResult
import com.android.internal.R
-import com.android.internal.util.EmergencyAffordanceManager
import com.android.internal.util.emergencyAffordanceManager
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
-import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
-import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.bouncerSceneContentViewModel
-import com.android.systemui.classifier.domain.interactor.falsingInteractor
-import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenUserActionsViewModel
+import com.android.systemui.keyguard.ui.viewmodel.lockscreenUserActionsViewModel
+import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
-import com.android.systemui.qs.ui.adapter.fakeQSSceneAdapter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.domain.startable.sceneContainerStartable
-import com.android.systemui.scene.shared.logger.sceneLogger
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.ui.viewmodel.ShadeSceneContentViewModel
-import com.android.systemui.shade.ui.viewmodel.ShadeUserActionsViewModel
import com.android.systemui.shade.ui.viewmodel.shadeSceneContentViewModel
import com.android.systemui.shade.ui.viewmodel.shadeUserActionsViewModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
-import com.android.telecom.telecomManager
+import com.android.telecom.mockTelecomManager
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -83,14 +71,12 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
/**
* Integration test cases for the Scene Framework.
@@ -116,195 +102,131 @@
@RunWithLooper
@EnableSceneContainer
class SceneFrameworkIntegrationTest : SysuiTestCase() {
-
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val sceneContainerConfig by lazy { kosmos.sceneContainerConfig }
- private val sceneInteractor by lazy { kosmos.sceneInteractor }
- private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
- private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
- private val communalInteractor by lazy { kosmos.communalInteractor }
-
- private val transitionState by lazy {
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(sceneContainerConfig.initialSceneKey)
- )
- }
- private val sceneContainerViewModel by lazy {
- SceneContainerViewModel(
- sceneInteractor = sceneInteractor,
- falsingInteractor = kosmos.falsingInteractor,
- powerInteractor = kosmos.powerInteractor,
- logger = kosmos.sceneLogger,
- motionEventHandlerReceiver = {},
- )
- .apply { setTransitionState(transitionState) }
- }
-
- private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
- private lateinit var bouncerActionButtonInteractor: BouncerActionButtonInteractor
- private lateinit var bouncerSceneContentViewModel: BouncerSceneContentViewModel
-
- private val mLockscreenUserActionsViewModel by lazy {
- LockscreenUserActionsViewModel(
- deviceEntryInteractor = deviceEntryInteractor,
- communalInteractor = communalInteractor,
- shadeInteractor = kosmos.shadeInteractor,
- )
- }
-
- private lateinit var shadeSceneContentViewModel: ShadeSceneContentViewModel
- private lateinit var mShadeUserActionsViewModel: ShadeUserActionsViewModel
-
- private val powerInteractor by lazy { kosmos.powerInteractor }
-
private var bouncerSceneJob: Job? = null
- private val qsFlexiglassAdapter = kosmos.fakeQSSceneAdapter
-
- private lateinit var emergencyAffordanceManager: EmergencyAffordanceManager
- private lateinit var telecomManager: TelecomManager
- private val fakeSceneDataSource = kosmos.fakeSceneDataSource
-
@Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
+ fun setUp() =
+ kosmos.run {
+ overrideResource(R.bool.config_enable_emergency_call_while_sim_locked, true)
+ whenever(mockTelecomManager.isInCall).thenReturn(false)
+ whenever(emergencyAffordanceManager.needsEmergencyAffordance()).thenReturn(true)
- overrideResource(R.bool.config_enable_emergency_call_while_sim_locked, true)
- telecomManager = checkNotNull(kosmos.telecomManager)
- whenever(telecomManager.isInCall).thenReturn(false)
- emergencyAffordanceManager = kosmos.emergencyAffordanceManager
- whenever(emergencyAffordanceManager.needsEmergencyAffordance()).thenReturn(true)
+ fakeFeatureFlagsClassic.apply { set(Flags.NEW_NETWORK_SLICE_UI, false) }
- kosmos.fakeFeatureFlagsClassic.apply { set(Flags.NEW_NETWORK_SLICE_UI, false) }
+ fakeMobileConnectionsRepository.isAnySimSecure.value = false
- mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
- mobileConnectionsRepository.isAnySimSecure.value = false
+ fakeTelephonyRepository.apply {
+ setHasTelephonyRadio(true)
+ setCallState(TelephonyManager.CALL_STATE_IDLE)
+ setIsInCall(false)
+ }
- kosmos.fakeTelephonyRepository.apply {
- setHasTelephonyRadio(true)
- setCallState(TelephonyManager.CALL_STATE_IDLE)
- setIsInCall(false)
+ sceneContainerStartable.start()
+
+ lockscreenUserActionsViewModel.activateIn(testScope)
+ shadeSceneContentViewModel.activateIn(testScope)
+ shadeUserActionsViewModel.activateIn(testScope)
+ bouncerSceneContentViewModel.activateIn(testScope)
+ sceneContainerViewModel.activateIn(testScope)
+
+ assertWithMessage("Initial scene key mismatch!")
+ .that(sceneContainerViewModel.currentScene.value)
+ .isEqualTo(sceneContainerConfig.initialSceneKey)
+ assertWithMessage("Initial scene container visibility mismatch!")
+ .that(sceneContainerViewModel.isVisible)
+ .isTrue()
}
- bouncerActionButtonInteractor = kosmos.bouncerActionButtonInteractor
- bouncerSceneContentViewModel = kosmos.bouncerSceneContentViewModel
-
- shadeSceneContentViewModel = kosmos.shadeSceneContentViewModel
- mShadeUserActionsViewModel = kosmos.shadeUserActionsViewModel
-
- val startable = kosmos.sceneContainerStartable
- startable.start()
-
- mLockscreenUserActionsViewModel.activateIn(testScope)
- shadeSceneContentViewModel.activateIn(testScope)
- mShadeUserActionsViewModel.activateIn(testScope)
- bouncerSceneContentViewModel.activateIn(testScope)
- sceneContainerViewModel.activateIn(testScope)
-
- assertWithMessage("Initial scene key mismatch!")
- .that(sceneContainerViewModel.currentScene.value)
- .isEqualTo(sceneContainerConfig.initialSceneKey)
- assertWithMessage("Initial scene container visibility mismatch!")
- .that(sceneContainerViewModel.isVisible)
- .isTrue()
- }
-
@Test
- fun startsInLockscreenScene() = testScope.runTest { assertCurrentScene(Scenes.Lockscreen) }
+ fun startsInLockscreenScene() =
+ testScope.runTest { kosmos.assertCurrentScene(Scenes.Lockscreen) }
@Test
fun clickLockButtonAndEnterCorrectPin_unlocksDevice() =
testScope.runTest {
- emulateUserDrivenTransition(Scenes.Bouncer)
+ kosmos.emulateUserDrivenTransition(Scenes.Bouncer)
- fakeSceneDataSource.pause()
- enterPin()
- emulatePendingTransitionProgress(
- expectedVisible = false,
- )
- assertCurrentScene(Scenes.Gone)
+ kosmos.fakeSceneDataSource.pause()
+ kosmos.enterPin()
+ kosmos.emulatePendingTransitionProgress(expectedVisible = false)
+ kosmos.assertCurrentScene(Scenes.Gone)
}
@Test
fun swipeUpOnLockscreen_enterCorrectPin_unlocksDevice() =
testScope.runTest {
- val actions by collectLastValue(mLockscreenUserActionsViewModel.actions)
+ val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
- emulateUserDrivenTransition(
+ kosmos.emulateUserDrivenTransition(
to = upDestinationSceneKey,
)
- fakeSceneDataSource.pause()
- enterPin()
- emulatePendingTransitionProgress(
- expectedVisible = false,
- )
- assertCurrentScene(Scenes.Gone)
+ kosmos.fakeSceneDataSource.pause()
+ kosmos.enterPin()
+ kosmos.emulatePendingTransitionProgress(expectedVisible = false)
+ kosmos.assertCurrentScene(Scenes.Gone)
}
@Test
fun swipeUpOnLockscreen_withAuthMethodSwipe_dismissesLockscreen() =
testScope.runTest {
- setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+ kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
- val actions by collectLastValue(mLockscreenUserActionsViewModel.actions)
+ val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
- emulateUserDrivenTransition(
- to = upDestinationSceneKey,
- )
+ kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
}
@Test
fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
- val actions by collectLastValue(mShadeUserActionsViewModel.actions)
+ val actions by collectLastValue(kosmos.shadeUserActionsViewModel.actions)
val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
- setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
- assertCurrentScene(Scenes.Lockscreen)
+ kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
// Emulate a user swipe to the shade scene.
- emulateUserDrivenTransition(to = Scenes.Shade)
- assertCurrentScene(Scenes.Shade)
+ kosmos.emulateUserDrivenTransition(to = Scenes.Shade)
+ kosmos.assertCurrentScene(Scenes.Shade)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(SceneFamilies.Home)
assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
- emulateUserDrivenTransition(
- to = homeScene,
- )
+ kosmos.emulateUserDrivenTransition(to = homeScene)
}
@Test
fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenDismissed_goesToGone() =
testScope.runTest {
- val actions by collectLastValue(mShadeUserActionsViewModel.actions)
- val canSwipeToEnter by collectLastValue(deviceEntryInteractor.canSwipeToEnter)
+ val actions by collectLastValue(kosmos.shadeUserActionsViewModel.actions)
+ val canSwipeToEnter by collectLastValue(kosmos.deviceEntryInteractor.canSwipeToEnter)
val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
- setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+ kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
assertThat(canSwipeToEnter).isTrue()
- assertCurrentScene(Scenes.Lockscreen)
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
// Emulate a user swipe to dismiss the lockscreen.
- emulateUserDrivenTransition(to = Scenes.Gone)
- assertCurrentScene(Scenes.Gone)
+ kosmos.emulateUserDrivenTransition(to = Scenes.Gone)
+ kosmos.assertCurrentScene(Scenes.Gone)
// Emulate a user swipe to the shade scene.
- emulateUserDrivenTransition(to = Scenes.Shade)
- assertCurrentScene(Scenes.Shade)
+ kosmos.emulateUserDrivenTransition(to = Scenes.Shade)
+ kosmos.assertCurrentScene(Scenes.Shade)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(SceneFamilies.Home)
assertThat(homeScene).isEqualTo(Scenes.Gone)
- emulateUserDrivenTransition(
+ kosmos.emulateUserDrivenTransition(
to = homeScene,
)
}
@@ -312,64 +234,64 @@
@Test
fun withAuthMethodNone_deviceWakeUp_skipsLockscreen() =
testScope.runTest {
- setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = false)
- putDeviceToSleep(instantlyLockDevice = false)
- assertCurrentScene(Scenes.Lockscreen)
+ kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = false)
+ kosmos.putDeviceToSleep(instantlyLockDevice = false)
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
- wakeUpDevice()
- assertCurrentScene(Scenes.Gone)
+ kosmos.wakeUpDevice()
+ kosmos.assertCurrentScene(Scenes.Gone)
}
@Test
fun withAuthMethodSwipe_deviceWakeUp_doesNotSkipLockscreen() =
testScope.runTest {
- setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
- putDeviceToSleep(instantlyLockDevice = false)
- assertCurrentScene(Scenes.Lockscreen)
+ kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+ kosmos.putDeviceToSleep(instantlyLockDevice = false)
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
- wakeUpDevice()
- assertCurrentScene(Scenes.Lockscreen)
+ kosmos.wakeUpDevice()
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
}
@Test
fun lockDeviceLocksDevice() =
testScope.runTest {
- unlockDevice()
- assertCurrentScene(Scenes.Gone)
+ kosmos.unlockDevice()
+ kosmos.assertCurrentScene(Scenes.Gone)
- lockDevice()
- assertCurrentScene(Scenes.Lockscreen)
+ kosmos.lockDevice()
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
}
@Test
fun deviceGoesToSleep_switchesToLockscreen() =
testScope.runTest {
- unlockDevice()
- assertCurrentScene(Scenes.Gone)
+ kosmos.unlockDevice()
+ kosmos.assertCurrentScene(Scenes.Gone)
- putDeviceToSleep()
- assertCurrentScene(Scenes.Lockscreen)
+ kosmos.putDeviceToSleep()
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
}
@Test
fun deviceGoesToSleep_wakeUp_unlock() =
testScope.runTest {
- unlockDevice()
- assertCurrentScene(Scenes.Gone)
- putDeviceToSleep()
- assertCurrentScene(Scenes.Lockscreen)
- wakeUpDevice()
- assertCurrentScene(Scenes.Lockscreen)
+ kosmos.unlockDevice()
+ kosmos.assertCurrentScene(Scenes.Gone)
+ kosmos.putDeviceToSleep()
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
+ kosmos.wakeUpDevice()
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
- unlockDevice()
- assertCurrentScene(Scenes.Gone)
+ kosmos.unlockDevice()
+ kosmos.assertCurrentScene(Scenes.Gone)
}
@Test
fun swipeUpOnLockscreenWhileUnlocked_dismissesLockscreen() =
testScope.runTest {
- unlockDevice()
- val actions by collectLastValue(mLockscreenUserActionsViewModel.actions)
+ kosmos.unlockDevice()
+ val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
@@ -378,46 +300,47 @@
@Test
fun deviceGoesToSleep_withLockTimeout_staysOnLockscreen() =
testScope.runTest {
- unlockDevice()
- assertCurrentScene(Scenes.Gone)
- putDeviceToSleep(instantlyLockDevice = false)
- assertCurrentScene(Scenes.Lockscreen)
+ kosmos.unlockDevice()
+ kosmos.assertCurrentScene(Scenes.Gone)
+ kosmos.putDeviceToSleep(instantlyLockDevice = false)
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
// Pretend like the timeout elapsed and now lock the device.
- lockDevice()
- assertCurrentScene(Scenes.Lockscreen)
+ kosmos.lockDevice()
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
}
@Test
fun dismissingIme_whileOnPasswordBouncer_navigatesToLockscreen() =
testScope.runTest {
- setAuthMethod(AuthenticationMethodModel.Password)
- val actions by collectLastValue(mLockscreenUserActionsViewModel.actions)
+ kosmos.setAuthMethod(AuthenticationMethodModel.Password)
+ val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
- emulateUserDrivenTransition(
+ kosmos.emulateUserDrivenTransition(
to = upDestinationSceneKey,
)
- fakeSceneDataSource.pause()
- dismissIme()
+ kosmos.fakeSceneDataSource.pause()
+ kosmos.dismissIme()
- emulatePendingTransitionProgress()
- assertCurrentScene(Scenes.Lockscreen)
+ kosmos.emulatePendingTransitionProgress()
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
}
@Test
fun bouncerActionButtonClick_opensEmergencyServicesDialer() =
testScope.runTest {
- setAuthMethod(AuthenticationMethodModel.Password)
- val actions by collectLastValue(mLockscreenUserActionsViewModel.actions)
+ kosmos.setAuthMethod(AuthenticationMethodModel.Password)
+ val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
- emulateUserDrivenTransition(to = upDestinationSceneKey)
+ kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
- val bouncerActionButton by collectLastValue(bouncerSceneContentViewModel.actionButton)
+ val bouncerActionButton by
+ collectLastValue(kosmos.bouncerSceneContentViewModel.actionButton)
assertWithMessage("Bouncer action button not visible")
.that(bouncerActionButton)
.isNotNull()
@@ -430,54 +353,55 @@
@Test
fun bouncerActionButtonClick_duringCall_returnsToCall() =
testScope.runTest {
- setAuthMethod(AuthenticationMethodModel.Password)
- startPhoneCall()
- val actions by collectLastValue(mLockscreenUserActionsViewModel.actions)
+ kosmos.setAuthMethod(AuthenticationMethodModel.Password)
+ kosmos.startPhoneCall()
+ val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
- emulateUserDrivenTransition(to = upDestinationSceneKey)
+ kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
- val bouncerActionButton by collectLastValue(bouncerSceneContentViewModel.actionButton)
+ val bouncerActionButton by
+ collectLastValue(kosmos.bouncerSceneContentViewModel.actionButton)
assertWithMessage("Bouncer action button not visible during call")
.that(bouncerActionButton)
.isNotNull()
bouncerActionButton?.onClick?.invoke()
runCurrent()
- verify(telecomManager).showInCallScreen(any())
+ verify(kosmos.mockTelecomManager).showInCallScreen(any())
}
@Test
fun showBouncer_whenLockedSimIntroduced() =
testScope.runTest {
- setAuthMethod(AuthenticationMethodModel.None)
- introduceLockedSim()
- assertCurrentScene(Scenes.Bouncer)
+ kosmos.setAuthMethod(AuthenticationMethodModel.None)
+ kosmos.introduceLockedSim()
+ kosmos.assertCurrentScene(Scenes.Bouncer)
}
@Test
fun goesToGone_whenSimUnlocked_whileDeviceUnlocked() =
testScope.runTest {
- fakeSceneDataSource.pause()
- introduceLockedSim()
- emulatePendingTransitionProgress(expectedVisible = true)
- enterSimPin(
+ kosmos.fakeSceneDataSource.pause()
+ kosmos.introduceLockedSim()
+ kosmos.emulatePendingTransitionProgress(expectedVisible = true)
+ kosmos.enterSimPin(
authMethodAfterSimUnlock = AuthenticationMethodModel.None,
enableLockscreen = false
)
- assertCurrentScene(Scenes.Gone)
+ kosmos.assertCurrentScene(Scenes.Gone)
}
@Test
fun showLockscreen_whenSimUnlocked_whileDeviceLocked() =
testScope.runTest {
- fakeSceneDataSource.pause()
- introduceLockedSim()
- emulatePendingTransitionProgress(expectedVisible = true)
- enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.Pin)
- assertCurrentScene(Scenes.Lockscreen)
+ kosmos.fakeSceneDataSource.pause()
+ kosmos.introduceLockedSim()
+ kosmos.emulatePendingTransitionProgress(expectedVisible = true)
+ kosmos.enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.Pin)
+ kosmos.assertCurrentScene(Scenes.Lockscreen)
}
/**
@@ -485,8 +409,8 @@
*
* Note that this doesn't assert what the current scene is in the UI.
*/
- private fun TestScope.assertCurrentScene(expected: SceneKey) {
- runCurrent()
+ private fun Kosmos.assertCurrentScene(expected: SceneKey) {
+ testScope.runCurrent()
assertWithMessage("Current scene mismatch!")
.that(sceneContainerViewModel.currentScene.value)
.isEqualTo(expected)
@@ -498,7 +422,7 @@
* This can be different than the value in [SceneContainerViewModel.currentScene], by design, as
* the UI must gradually transition between scenes.
*/
- private fun getCurrentSceneInUi(): SceneKey {
+ private fun Kosmos.getCurrentSceneInUi(): SceneKey {
return when (val state = transitionState.value) {
is ObservableTransitionState.Idle -> state.currentScene
is ObservableTransitionState.Transition.ChangeScene -> state.fromScene
@@ -508,7 +432,7 @@
}
/** Updates the current authentication method and related states in the data layer. */
- private fun TestScope.setAuthMethod(
+ private fun Kosmos.setAuthMethod(
authMethod: AuthenticationMethodModel,
enableLockscreen: Boolean = true
) {
@@ -520,20 +444,20 @@
// Set the lockscreen enabled bit _before_ set the auth method as the code picks up on the
// lockscreen enabled bit _after_ the auth method is changed and the lockscreen enabled bit
// is not an observable that can trigger a new evaluation.
- kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(enableLockscreen)
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
- runCurrent()
+ fakeDeviceEntryRepository.setLockscreenEnabled(enableLockscreen)
+ fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
+ testScope.runCurrent()
}
/** Emulates a phone call in progress. */
- private fun TestScope.startPhoneCall() {
- whenever(telecomManager.isInCall).thenReturn(true)
- kosmos.fakeTelephonyRepository.apply {
+ private fun Kosmos.startPhoneCall() {
+ whenever(mockTelecomManager.isInCall).thenReturn(true)
+ fakeTelephonyRepository.apply {
setHasTelephonyRadio(true)
setIsInCall(true)
setCallState(TelephonyManager.CALL_STATE_OFFHOOK)
}
- runCurrent()
+ testScope.runCurrent()
}
/**
@@ -543,14 +467,12 @@
*
* In order to use this, the [fakeSceneDataSource] must be paused before this method is called.
*/
- private fun TestScope.emulatePendingTransitionProgress(
- expectedVisible: Boolean = true,
- ) {
+ private fun Kosmos.emulatePendingTransitionProgress(expectedVisible: Boolean = true) {
assertWithMessage("The FakeSceneDataSource has to be paused for this to do anything.")
- .that(fakeSceneDataSource.isPaused)
+ .that(kosmos.fakeSceneDataSource.isPaused)
.isTrue()
- val to = fakeSceneDataSource.pendingScene ?: return
+ val to = kosmos.fakeSceneDataSource.pendingScene ?: return
val from = getCurrentSceneInUi()
if (to == from) {
@@ -568,19 +490,19 @@
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
)
- runCurrent()
+ testScope.runCurrent()
// Report progress of transition.
while (progressFlow.value < 1f) {
progressFlow.value += 0.2f
- runCurrent()
+ testScope.runCurrent()
}
// End the transition and report the change.
transitionState.value = ObservableTransitionState.Idle(to)
- fakeSceneDataSource.unpause(force = true)
- runCurrent()
+ kosmos.fakeSceneDataSource.unpause(force = true)
+ testScope.runCurrent()
assertWithMessage("Visibility mismatch after scene transition from $from to $to!")
.that(sceneContainerViewModel.isVisible)
@@ -598,7 +520,7 @@
bouncerSceneJob?.cancel()
null
}
- runCurrent()
+ testScope.runCurrent()
}
/**
@@ -610,12 +532,10 @@
*
* @param to The scene to transition to.
*/
- private fun TestScope.emulateUserDrivenTransition(
- to: SceneKey?,
- ) {
+ private fun Kosmos.emulateUserDrivenTransition(to: SceneKey?) {
checkNotNull(to)
- fakeSceneDataSource.pause()
+ kosmos.fakeSceneDataSource.pause()
sceneInteractor.changeScene(to, "reason")
emulatePendingTransitionProgress(
@@ -630,17 +550,17 @@
*
* Not to be confused with [putDeviceToSleep], which may also instantly lock the device.
*/
- private suspend fun TestScope.lockDevice() {
+ private suspend fun Kosmos.lockDevice() {
val authMethod = authenticationInteractor.getAuthenticationMethod()
assertWithMessage("The authentication method of $authMethod is not secure, cannot lock!")
.that(authMethod.isSecure)
.isTrue()
- kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "")
- runCurrent()
+ sceneInteractor.changeScene(Scenes.Lockscreen, "")
+ testScope.runCurrent()
}
/** Unlocks the device by entering the correct PIN. Ends up in the Gone scene. */
- private fun TestScope.unlockDevice() {
+ private fun Kosmos.unlockDevice() {
assertWithMessage("Cannot unlock a device that's already unlocked!")
.that(deviceEntryInteractor.isUnlocked.value)
.isFalse()
@@ -662,12 +582,12 @@
*
* Does not assert that the device is locked or unlocked.
*/
- private fun TestScope.enterPin() {
+ private fun Kosmos.enterPin() {
assertWithMessage("Cannot enter PIN when not on the Bouncer scene!")
.that(getCurrentSceneInUi())
.isEqualTo(Scenes.Bouncer)
val authMethodViewModel by
- collectLastValue(bouncerSceneContentViewModel.authMethodViewModel)
+ testScope.collectLastValue(bouncerSceneContentViewModel.authMethodViewModel)
assertWithMessage("Cannot enter PIN when not using a PIN authentication method!")
.that(authMethodViewModel)
.isInstanceOf(PinBouncerViewModel::class.java)
@@ -677,7 +597,7 @@
pinBouncerViewModel.onPinButtonClicked(digit)
}
pinBouncerViewModel.onAuthenticateButtonClicked()
- runCurrent()
+ testScope.runCurrent()
}
/**
@@ -688,7 +608,7 @@
*
* Does not assert that the device is locked or unlocked.
*/
- private fun TestScope.enterSimPin(
+ private fun Kosmos.enterSimPin(
authMethodAfterSimUnlock: AuthenticationMethodModel = AuthenticationMethodModel.None,
enableLockscreen: Boolean = true,
) {
@@ -696,7 +616,7 @@
.that(getCurrentSceneInUi())
.isEqualTo(Scenes.Bouncer)
val authMethodViewModel by
- collectLastValue(bouncerSceneContentViewModel.authMethodViewModel)
+ testScope.collectLastValue(bouncerSceneContentViewModel.authMethodViewModel)
assertWithMessage("Cannot enter PIN when not using a PIN authentication method!")
.that(authMethodViewModel)
.isInstanceOf(PinBouncerViewModel::class.java)
@@ -706,26 +626,26 @@
pinBouncerViewModel.onPinButtonClicked(digit)
}
pinBouncerViewModel.onAuthenticateButtonClicked()
- kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = false
- runCurrent()
+ fakeMobileConnectionsRepository.isAnySimSecure.value = false
+ testScope.runCurrent()
setAuthMethod(authMethodAfterSimUnlock, enableLockscreen)
- runCurrent()
+ testScope.runCurrent()
}
/** Changes device wakefulness state from asleep to awake, going through intermediary states. */
- private fun TestScope.wakeUpDevice() {
+ private fun Kosmos.wakeUpDevice() {
val wakefulnessModel = powerInteractor.detailedWakefulness.value
assertWithMessage("Cannot wake up device as it's already awake!")
.that(wakefulnessModel.isAwake())
.isFalse()
powerInteractor.setAwakeForTest()
- runCurrent()
+ testScope.runCurrent()
}
/** Changes device wakefulness state from awake to asleep, going through intermediary states. */
- private suspend fun TestScope.putDeviceToSleep(
+ private suspend fun Kosmos.putDeviceToSleep(
instantlyLockDevice: Boolean = true,
) {
val wakefulnessModel = powerInteractor.detailedWakefulness.value
@@ -734,7 +654,7 @@
.isTrue()
powerInteractor.setAsleepForTest()
- runCurrent()
+ testScope.runCurrent()
if (instantlyLockDevice) {
lockDevice()
@@ -742,16 +662,16 @@
}
/** Emulates the dismissal of the IME (soft keyboard). */
- private fun TestScope.dismissIme() {
+ private fun Kosmos.dismissIme() {
(bouncerSceneContentViewModel.authMethodViewModel.value as? PasswordBouncerViewModel)?.let {
it.onImeDismissed()
- runCurrent()
+ testScope.runCurrent()
}
}
- private fun TestScope.introduceLockedSim() {
+ private fun Kosmos.introduceLockedSim() {
setAuthMethod(AuthenticationMethodModel.Sim)
- kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = true
- runCurrent()
+ fakeMobileConnectionsRepository.isAnySimSecure.value = true
+ testScope.runCurrent()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index 3558f17..4b132c4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -31,8 +31,10 @@
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.fakeOverlaysByKeys
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.shared.logger.sceneLogger
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
@@ -243,4 +245,46 @@
assertThat(underTest.isVisible).isFalse()
}
+
+ @Test
+ fun getActionableContentKey_noOverlays_returnsCurrentScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays).isEmpty()
+
+ val actionableContentKey =
+ underTest.getActionableContentKey(
+ currentScene = checkNotNull(currentScene),
+ currentOverlays = checkNotNull(currentOverlays),
+ overlayByKey = kosmos.fakeOverlaysByKeys,
+ )
+
+ assertThat(actionableContentKey).isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun getActionableContentKey_multipleOverlays_returnsTopOverlay() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ fakeSceneDataSource.showOverlay(Overlays.QuickSettingsShade)
+ fakeSceneDataSource.showOverlay(Overlays.NotificationsShade)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(currentOverlays)
+ .containsExactly(
+ Overlays.QuickSettingsShade,
+ Overlays.NotificationsShade,
+ )
+
+ val actionableContentKey =
+ underTest.getActionableContentKey(
+ currentScene = checkNotNull(currentScene),
+ currentOverlays = checkNotNull(currentOverlays),
+ overlayByKey = kosmos.fakeOverlaysByKeys,
+ )
+
+ assertThat(actionableContentKey).isEqualTo(Overlays.QuickSettingsShade)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
index 4cf924a..cb6dc19 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
@@ -20,11 +20,12 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.uiEventLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.data.repository.captioningRepository
+import com.android.systemui.accessibility.domain.interactor.captioningInteractor
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
-import com.android.systemui.view.accessibility.data.repository.captioningInteractor
-import com.android.systemui.view.accessibility.data.repository.captioningRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -49,7 +50,7 @@
CaptioningViewModel(
context,
captioningInteractor,
- testScope.backgroundScope,
+ applicationCoroutineScope,
uiEventLogger,
)
}
diff --git a/packages/SystemUI/res/layout/app_clips_screenshot.xml b/packages/SystemUI/res/layout/app_clips_screenshot.xml
index d7b94ec..7b7c96cb 100644
--- a/packages/SystemUI/res/layout/app_clips_screenshot.xml
+++ b/packages/SystemUI/res/layout/app_clips_screenshot.xml
@@ -82,6 +82,23 @@
app:layout_constraintStart_toEndOf="@id/backlinks_include_data"
app:layout_constraintTop_toTopOf="parent" />
+ <TextView
+ android:id="@+id/backlinks_cross_profile_error"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginStart="8dp"
+ android:drawablePadding="4dp"
+ android:drawableStart="@drawable/ic_info_outline"
+ android:drawableTint="?androidprv:attr/materialColorOnBackground"
+ android:gravity="center"
+ android:paddingHorizontal="8dp"
+ android:text="@string/backlinks_cross_profile_error"
+ android:textColor="?androidprv:attr/materialColorOnBackground"
+ android:visibility="gone"
+ app:layout_constraintBottom_toTopOf="@id/preview"
+ app:layout_constraintStart_toEndOf="@id/backlinks_data"
+ app:layout_constraintTop_toTopOf="parent" />
+
<ImageView
android:id="@+id/preview"
android:layout_width="0px"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index ee75b31..541aebe 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -66,7 +66,7 @@
<FrameLayout
android:id="@+id/status_bar_start_side_content"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:clipChildren="false">
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index be74291..1fb1dad 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -269,8 +269,12 @@
<string name="screenshot_detected_multiple_template"><xliff:g id="appName" example="Google Chrome">%1$s</xliff:g> and other open apps detected this screenshot.</string>
<!-- Add to note button used in App Clips flow to return the saved screenshot image to notes app. [CHAR LIMIT=NONE] -->
<string name="app_clips_save_add_to_note">Add to note</string>
+ <!-- A check box used in App Clips flow to return the captured backlink of the screenshotted app to notes app. [CHAR LIMIT=NONE] -->
<string name="backlinks_include_link">Include link</string>
+ <!-- A label for backlinks app that is used if there are multiple backlinks with same app name. [CHAR LIMIT=NONE] -->
<string name="backlinks_duplicate_label_format"><xliff:g id="appName" example="Google Chrome">%1$s</xliff:g> <xliff:g id="frequencyCount" example="(1)">(%2$d)</xliff:g></string>
+ <!-- An error message to inform user that capturing backlink from cross profile apps is not possible. [CHAR LIMIT=NONE] -->
+ <string name="backlinks_cross_profile_error">Links can\'t be added from other profiles</string>
<!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
<string name="screenrecord_title">Screen Recorder</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardQuickAffordancesLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardQuickAffordancesLogger.kt
index c11cf55..7dbf013 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardQuickAffordancesLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardQuickAffordancesLogger.kt
@@ -16,6 +16,7 @@
package com.android.keyguard.logging
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.KeyguardQuickAffordancesLog
@@ -63,6 +64,15 @@
)
}
+ fun logUpdate(viewModel: KeyguardQuickAffordanceViewModel) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = viewModel.toString() },
+ { "QuickAffordance updated: $str1" }
+ )
+ }
+
private fun String.decode(): Pair<String, String> {
val splitUp = this.split(DELIMITER)
return Pair(splitUp[0], splitUp[1])
diff --git a/packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt
similarity index 85%
rename from packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt
rename to packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt
index 0b71d25..bf749d4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt
@@ -1,20 +1,20 @@
/*
* Copyright (C) 2024 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
+ * 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
+ * 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.
+ * 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.settingslib.view.accessibility.data.repository
+package com.android.systemui.accessibility.data.repository
import android.view.accessibility.CaptioningManager
import kotlin.coroutines.CoroutineContext
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractor.kt b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractor.kt
new file mode 100644
index 0000000..1d493c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractor.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.domain.interactor
+
+import com.android.systemui.accessibility.data.repository.CaptioningRepository
+import kotlinx.coroutines.flow.StateFlow
+
+class CaptioningInteractor(private val repository: CaptioningRepository) {
+
+ val isSystemAudioCaptioningEnabled: StateFlow<Boolean>
+ get() = repository.isSystemAudioCaptioningEnabled
+
+ val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean>
+ get() = repository.isSystemAudioCaptioningUiEnabled
+
+ suspend fun setIsSystemAudioCaptioningEnabled(enabled: Boolean) =
+ repository.setIsSystemAudioCaptioningEnabled(enabled)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index ae2d401..113e001 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -552,7 +552,8 @@
}
private void updateGestureBlockingLocked() {
- final boolean shouldBlock = !isDreamInPreviewMode() && !mShadeExpanded && !mBouncerShowing;
+ final boolean shouldBlock = mStarted && !mShadeExpanded && !mBouncerShowing
+ && !isDreamInPreviewMode();
if (shouldBlock) {
mGestureInteractor.addGestureBlockedMatcher(DREAM_TYPE_MATCHER,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index a43bfd3..8a3d017 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -63,6 +63,7 @@
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransitionModule;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModelModule;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.process.ProcessWrapper;
@@ -111,6 +112,7 @@
DeviceEntryIconTransitionModule.class,
FalsingModule.class,
KeyguardDataQuickAffordanceModule.class,
+ KeyguardQuickAffordancesCombinedViewModelModule.class,
KeyguardRepositoryModule.class,
DeviceEntryFaceAuthModule.class,
KeyguardDisplayModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 28a17ef..27dd18d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -93,7 +93,7 @@
val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
val disposableHandle =
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
viewModel.collect { buttonModel ->
updateButton(
@@ -141,6 +141,7 @@
viewModel: KeyguardQuickAffordanceViewModel,
messageDisplayer: (Int) -> Unit,
) {
+ logger.logUpdate(viewModel)
if (!viewModel.isVisible) {
view.isInvisible = true
return
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
index a6108c4..075a1d2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
@@ -26,7 +26,6 @@
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.coroutines.runBlocking
-import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -67,11 +66,7 @@
var observer: PreviewLifecycleObserver? = null
return try {
val renderer =
- if (Flags.lockscreenPreviewRendererCreateOnMainThread()) {
- runBlocking("$TAG#previewRendererFactory.create", mainDispatcher) {
- previewRendererFactory.create(request)
- }
- } else {
+ runBlocking("$TAG#previewRendererFactory.create", mainDispatcher) {
previewRendererFactory.create(request)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutSection.kt
index a4137ac..8861c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutSection.kt
@@ -4,10 +4,10 @@
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.res.ResourcesCompat
-import com.android.systemui.res.R
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
+import com.android.systemui.res.R
abstract class BaseShortcutSection : KeyguardSection() {
protected var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
@@ -15,7 +15,9 @@
override fun removeViews(constraintLayout: ConstraintLayout) {
leftShortcutHandle?.destroy()
+ leftShortcutHandle = null
rightShortcutHandle?.destroy()
+ rightShortcutHandle = null
constraintLayout.removeView(R.id.start_button)
constraintLayout.removeView(R.id.end_button)
}
@@ -75,6 +77,7 @@
}
constraintLayout.addView(view)
}
+
/**
* Defines equality as same class.
*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index e558033..6c6e14c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -33,16 +33,19 @@
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModelModule.Companion.LOCKSCREEN_INSTANCE
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.res.R
import com.android.systemui.statusbar.KeyguardIndicationController
import dagger.Lazy
import javax.inject.Inject
+import javax.inject.Named
class DefaultShortcutsSection
@Inject
constructor(
@Main private val resources: Resources,
+ @Named(LOCKSCREEN_INSTANCE)
private val keyguardQuickAffordancesCombinedViewModel:
KeyguardQuickAffordancesCombinedViewModel,
private val keyguardRootViewModel: KeyguardRootViewModel,
@@ -76,6 +79,7 @@
override fun bindData(constraintLayout: ConstraintLayout) {
if (KeyguardBottomAreaRefactor.isEnabled) {
+ leftShortcutHandle?.destroy()
leftShortcutHandle =
keyguardQuickAffordanceViewBinder.bind(
constraintLayout.requireViewById(R.id.start_button),
@@ -84,6 +88,7 @@
) {
indicationController.showTransientIndication(it)
}
+ rightShortcutHandle?.destroy()
rightShortcutHandle =
keyguardQuickAffordanceViewBinder.bind(
constraintLayout.requireViewById(R.id.end_button),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
index 609b571d..ceae1b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -33,6 +33,7 @@
import com.android.systemui.res.R
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import javax.inject.Inject
+import javax.inject.Named
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -50,6 +51,7 @@
keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
private val burnInHelperWrapper: BurnInHelperWrapper,
burnInInteractor: BurnInInteractor,
+ @Named(KeyguardQuickAffordancesCombinedViewModelModule.Companion.LOCKSCREEN_INSTANCE)
shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
configurationInteractor: ConfigurationInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index 72740d5..7e13d22 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -177,10 +177,16 @@
)
}
} else {
- button(
- KeyguardQuickAffordancePosition.BOTTOM_START,
- )
+ button(KeyguardQuickAffordancePosition.BOTTOM_START)
}
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue =
+ KeyguardQuickAffordanceViewModel(
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()
+ ),
+ )
/** An observable for the view-model of the "end button" quick affordance. */
val endButton: Flow<KeyguardQuickAffordanceViewModel> =
@@ -194,6 +200,14 @@
} else {
button(KeyguardQuickAffordancePosition.BOTTOM_END)
}
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue =
+ KeyguardQuickAffordanceViewModel(
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId()
+ ),
+ )
/**
* Notifies that a slot with the given ID has been selected in the preview experience that is
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelModule.kt
new file mode 100644
index 0000000..fceacc9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelModule.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Named
+
+@Module
+interface KeyguardQuickAffordancesCombinedViewModelModule {
+ companion object {
+ const val LOCKSCREEN_INSTANCE = "lockscreen_instance"
+ }
+
+ @SysUISingleton
+ @Binds
+ @Named(LOCKSCREEN_INSTANCE)
+ fun provideKeyguardQuickAffordancesCombinedViewModel(
+ model: KeyguardQuickAffordancesCombinedViewModel
+ ): KeyguardQuickAffordancesCombinedViewModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index adb63b7..75b1b04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.res.Resources
-import com.android.compose.animation.scene.ContentKey
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.biometrics.AuthController
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -27,7 +26,6 @@
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
-import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import dagger.assisted.AssistedFactory
@@ -42,7 +40,6 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -103,17 +100,8 @@
}
}
- /**
- * Returns a flow that indicates whether lockscreen notifications should be rendered in the
- * given [contentKey].
- */
- fun areNotificationsVisible(contentKey: ContentKey): Flow<Boolean> {
- // `Scenes.NotificationsShade` renders its own separate notifications stack, so when it's
- // open we avoid rendering the lockscreen notifications stack.
- if (contentKey == Scenes.NotificationsShade) {
- return flowOf(false)
- }
-
+ /** Returns a flow that indicates whether lockscreen notifications should be rendered. */
+ fun areNotificationsVisible(): Flow<Boolean> {
return combine(
clockSize,
shadeInteractor.isShadeLayoutWide,
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index ed76646..19906fd 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -382,16 +382,6 @@
return factory.create("MediaLog", 20);
}
- /**
- * Provides a buffer for media device changes
- */
- @Provides
- @SysUISingleton
- @MediaDeviceLog
- public static LogBuffer providesMediaDeviceLogBuffer(LogBufferFactory factory) {
- return factory.create("MediaDeviceLog", 50);
- }
-
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
@Provides
@SysUISingleton
@@ -584,7 +574,7 @@
@SysUISingleton
@KeyguardQuickAffordancesLog
public static LogBuffer provideKeyguardQuickAffordancesLogBuffer(LogBufferFactory factory) {
- return factory.create("KeyguardQuickAffordancesLog", 25);
+ return factory.create("KeyguardQuickAffordancesLog", 100);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaDeviceLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaDeviceLog.kt
deleted file mode 100644
index 06bd269..0000000
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaDeviceLog.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.log.dagger
-
-import com.android.systemui.log.LogBuffer
-import javax.inject.Qualifier
-
-/** A [LogBuffer] for [com.android.systemui.media.controls.domain.pipeline.MediaDeviceLogger] */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class MediaDeviceLog
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLogger.kt
deleted file mode 100644
index f886166..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLogger.kt
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media.controls.domain.pipeline
-
-import android.media.session.MediaController
-import com.android.settingslib.media.MediaDevice
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.core.LogLevel
-import com.android.systemui.log.dagger.MediaDeviceLog
-import com.android.systemui.media.controls.shared.model.MediaDeviceData
-import javax.inject.Inject
-
-/** A [LogBuffer] for media device changes */
-class MediaDeviceLogger @Inject constructor(@MediaDeviceLog private val buffer: LogBuffer) {
-
- fun logBroadcastEvent(event: String, reason: Int, broadcastId: Int) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- {
- str1 = event
- int1 = reason
- int2 = broadcastId
- },
- { "$str1, reason = $int1, broadcastId = $int2" }
- )
- }
-
- fun logBroadcastEvent(event: String, reason: Int) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- {
- str1 = event
- int1 = reason
- },
- { "$str1, reason = $int1" }
- )
- }
-
- fun logBroadcastMetadataChanged(broadcastId: Int, metadata: String) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- {
- int1 = broadcastId
- str1 = metadata
- },
- { "onBroadcastMetadataChanged, broadcastId = $int1, metadata = $str1" }
- )
- }
-
- fun logNewDeviceName(name: String?) {
- buffer.log(TAG, LogLevel.DEBUG, { str1 = name }, { "New device name $str1" })
- }
-
- fun logLocalDevice(sassDevice: MediaDeviceData?, connectedDevice: MediaDeviceData?) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- {
- str1 = sassDevice?.name?.toString()
- str2 = connectedDevice?.name?.toString()
- },
- { "Local device: $str1 or $str2" }
- )
- }
-
- fun logRemoteDevice(routingSessionName: CharSequence?, connectedDevice: MediaDeviceData?) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- {
- str1 = routingSessionName?.toString()
- str2 = connectedDevice?.name?.toString()
- },
- { "Remote device: $str1 or $str2 or unknown" }
- )
- }
-
- fun logDeviceName(
- device: MediaDevice?,
- controller: MediaController?,
- routingSessionName: CharSequence?,
- selectedRouteName: CharSequence?
- ) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- {
- str1 = "device $device, controller: $controller"
- str2 = routingSessionName?.toString()
- str3 = selectedRouteName?.toString()
- },
- { "$str1, routingSession $str2 or selected route $str3" }
- )
- }
-
- companion object {
- private const val TAG = "MediaDeviceLog"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index 49b53c2..a193f7f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -71,7 +71,6 @@
private val localBluetoothManager: Lazy<LocalBluetoothManager?>,
@Main private val fgExecutor: Executor,
@Background private val bgExecutor: Executor,
- private val logger: MediaDeviceLogger,
) : MediaDataManager.Listener {
private val listeners: MutableSet<Listener> = mutableSetOf()
@@ -282,38 +281,59 @@
}
override fun onBroadcastStarted(reason: Int, broadcastId: Int) {
- logger.logBroadcastEvent("onBroadcastStarted", reason, broadcastId)
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStarted(), reason = $reason , broadcastId = $broadcastId")
+ }
updateCurrent()
}
override fun onBroadcastStartFailed(reason: Int) {
- logger.logBroadcastEvent("onBroadcastStartFailed", reason)
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStartFailed(), reason = $reason")
+ }
}
override fun onBroadcastMetadataChanged(
broadcastId: Int,
metadata: BluetoothLeBroadcastMetadata
) {
- logger.logBroadcastMetadataChanged(broadcastId, metadata.toString())
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onBroadcastMetadataChanged(), broadcastId = $broadcastId , " +
+ "metadata = $metadata"
+ )
+ }
updateCurrent()
}
override fun onBroadcastStopped(reason: Int, broadcastId: Int) {
- logger.logBroadcastEvent("onBroadcastStopped", reason, broadcastId)
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStopped(), reason = $reason , broadcastId = $broadcastId")
+ }
updateCurrent()
}
override fun onBroadcastStopFailed(reason: Int) {
- logger.logBroadcastEvent("onBroadcastStopFailed", reason)
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStopFailed(), reason = $reason")
+ }
}
override fun onBroadcastUpdated(reason: Int, broadcastId: Int) {
- logger.logBroadcastEvent("onBroadcastUpdated", reason, broadcastId)
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastUpdated(), reason = $reason , broadcastId = $broadcastId")
+ }
updateCurrent()
}
override fun onBroadcastUpdateFailed(reason: Int, broadcastId: Int) {
- logger.logBroadcastEvent("onBroadcastUpdateFailed", reason, broadcastId)
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onBroadcastUpdateFailed(), reason = $reason , " + "broadcastId = $broadcastId"
+ )
+ }
}
override fun onPlaybackStarted(reason: Int, broadcastId: Int) {}
@@ -361,16 +381,12 @@
name = context.getString(R.string.media_seamless_other_device),
showBroadcastButton = false
)
- logger.logRemoteDevice(routingSession?.name, connectedDevice)
} else {
// Prefer SASS if available when playback is local.
- val sassDevice = getSassDevice()
- activeDevice = sassDevice ?: connectedDevice
- logger.logLocalDevice(sassDevice, connectedDevice)
+ activeDevice = getSassDevice() ?: connectedDevice
}
current = activeDevice ?: EMPTY_AND_DISABLED_MEDIA_DEVICE_DATA
- logger.logNewDeviceName(current?.name?.toString())
} else {
val aboutToConnect = aboutToConnectDeviceOverride
if (
@@ -391,7 +407,9 @@
val enabled = device != null && (controller == null || routingSession != null)
val name = getDeviceName(device, routingSession)
- logger.logNewDeviceName(name)
+ if (DEBUG) {
+ Log.d(TAG, "new device name $name")
+ }
current =
MediaDeviceData(
enabled,
@@ -445,12 +463,14 @@
): String? {
val selectedRoutes = routingSession?.let { mr2manager.get().getSelectedRoutes(it) }
- logger.logDeviceName(
- device,
- controller,
- routingSession?.name,
- selectedRoutes?.firstOrNull()?.name
- )
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "device is $device, controller $controller," +
+ " routingSession ${routingSession?.name}" +
+ " or ${selectedRoutes?.firstOrNull()?.name}"
+ )
+ }
if (controller == null) {
// In resume state, we don't have a controller - just use the device name
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
index e51a8bc..738b184 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
@@ -38,6 +38,8 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapConcat
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -73,22 +75,34 @@
sceneInteractorProvider
.get()
.transitionState
- .map { state ->
+ .flatMapConcat { state ->
when (state) {
is ObservableTransitionState.Idle ->
- state.currentScene == Scenes.Shade ||
- state.currentScene == Scenes.NotificationsShade ||
- state.currentScene == Scenes.QuickSettingsShade ||
- state.currentScene == Scenes.Lockscreen
+ flowOf(
+ state.currentScene == Scenes.Shade ||
+ state.currentScene == Scenes.NotificationsShade ||
+ state.currentScene == Scenes.QuickSettingsShade ||
+ state.currentScene == Scenes.Lockscreen
+ )
is ObservableTransitionState.Transition ->
- state.toContent == Scenes.Shade ||
- state.toContent == Scenes.NotificationsShade ||
- state.toContent == Scenes.QuickSettingsShade ||
- state.toContent == Scenes.Lockscreen ||
- state.fromContent == Scenes.Shade ||
- state.fromContent == Scenes.NotificationsShade ||
- state.fromContent == Scenes.QuickSettingsShade ||
- state.fromContent == Scenes.Lockscreen
+ if (
+ state.fromContent == Scenes.Bouncer &&
+ state.toContent == Scenes.Lockscreen
+ ) {
+ // Lockscreen is not visible during preview stage of predictive back
+ state.isInPreviewStage.map { !it }
+ } else {
+ flowOf(
+ state.toContent == Scenes.Shade ||
+ state.toContent == Scenes.NotificationsShade ||
+ state.toContent == Scenes.QuickSettingsShade ||
+ state.toContent == Scenes.Lockscreen ||
+ state.fromContent == Scenes.Shade ||
+ state.fromContent == Scenes.NotificationsShade ||
+ state.fromContent == Scenes.QuickSettingsShade ||
+ state.fromContent == Scenes.Lockscreen
+ )
+ }
}
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index 075599b..7f35d73 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -77,29 +77,31 @@
alternateBouncerDependencies: AlternateBouncerDependencies,
) {
val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key }
- val sortedSceneByKey: Map<SceneKey, Scene> = buildMap {
- containerConfig.sceneKeys.forEach { sceneKey ->
- val scene =
- checkNotNull(unsortedSceneByKey[sceneKey]) {
- "Scene not found for key \"$sceneKey\"!"
- }
+ val sortedSceneByKey: Map<SceneKey, Scene> =
+ LinkedHashMap<SceneKey, Scene>(containerConfig.sceneKeys.size).apply {
+ containerConfig.sceneKeys.forEach { sceneKey ->
+ val scene =
+ checkNotNull(unsortedSceneByKey[sceneKey]) {
+ "Scene not found for key \"$sceneKey\"!"
+ }
- put(sceneKey, scene)
+ put(sceneKey, scene)
+ }
}
- }
val unsortedOverlayByKey: Map<OverlayKey, Overlay> =
overlays.associateBy { overlay -> overlay.key }
- val sortedOverlayByKey: Map<OverlayKey, Overlay> = buildMap {
- containerConfig.overlayKeys.forEach { overlayKey ->
- val overlay =
- checkNotNull(unsortedOverlayByKey[overlayKey]) {
- "Overlay not found for key \"$overlayKey\"!"
- }
+ val sortedOverlayByKey: Map<OverlayKey, Overlay> =
+ LinkedHashMap<OverlayKey, Overlay>(containerConfig.overlayKeys.size).apply {
+ containerConfig.overlayKeys.forEach { overlayKey ->
+ val overlay =
+ checkNotNull(unsortedOverlayByKey[overlayKey]) {
+ "Overlay not found for key \"$overlayKey\"!"
+ }
- put(overlayKey, overlay)
+ put(overlayKey, overlay)
+ }
}
- }
view.repeatWhenAttached {
view.viewModel(
@@ -148,7 +150,7 @@
)
view.addView(sharedNotificationContainer)
- // TODO (b/358354906): use an overlay for the alternate bouncer
+ // TODO(b/358354906): use an overlay for the alternate bouncer
view.addView(
createAlternateBouncerView(
context = view.context,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index a73c39d..4c6341b 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -18,7 +18,9 @@
import android.view.MotionEvent
import androidx.compose.runtime.getValue
+import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
@@ -30,6 +32,7 @@
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.ui.composable.Overlay
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -83,7 +86,7 @@
/**
* Binds the given flow so the system remembers it.
*
- * Note that you must call is with `null` when the UI is done or risk a memory leak.
+ * Note that you must call this with `null` when the UI is done or risk a memory leak.
*/
fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
sceneInteractor.setTransitionState(transitionState)
@@ -197,6 +200,29 @@
}
}
+ /**
+ * Returns the [ContentKey] whose user actions should be active.
+ *
+ * @param overlayByKey Mapping of [Overlay] by [OverlayKey], ordered by z-order such that the
+ * last overlay is rendered on top of all other overlays.
+ */
+ fun getActionableContentKey(
+ currentScene: SceneKey,
+ currentOverlays: Set<OverlayKey>,
+ overlayByKey: Map<OverlayKey, Overlay>,
+ ): ContentKey {
+ // Overlay actions take precedence over scene actions.
+ return when (currentOverlays.size) {
+ // No overlays, the scene is actionable.
+ 0 -> currentScene
+ // Small optimization for the most common case.
+ 1 -> currentOverlays.first()
+ // Find the top-most overlay by z-index.
+ else ->
+ checkNotNull(overlayByKey.asSequence().findLast { it.key in currentOverlays }?.key)
+ }
+ }
+
/** Defines interface for classes that can handle externally-reported [MotionEvent]s. */
interface MotionEventHandler {
/** Notifies that a [MotionEvent] has occurred. */
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index ad5e772..15a1d1d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -68,6 +68,8 @@
import com.android.systemui.Flags;
import com.android.systemui.log.DebugLogger;
import com.android.systemui.res.R;
+import com.android.systemui.screenshot.appclips.InternalBacklinksData.BacklinksData;
+import com.android.systemui.screenshot.appclips.InternalBacklinksData.CrossProfileError;
import com.android.systemui.screenshot.scroll.CropView;
import com.android.systemui.settings.UserTracker;
@@ -100,6 +102,7 @@
private static final String TAG = AppClipsActivity.class.getSimpleName();
private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0);
private static final int DRAWABLE_END = 2;
+ private static final float DISABLE_ALPHA = 0.5f;
private final AppClipsViewModel.Factory mViewModelFactory;
private final PackageManager mPackageManager;
@@ -116,6 +119,7 @@
private Button mCancel;
private CheckBox mBacklinksIncludeDataCheckBox;
private TextView mBacklinksDataTextView;
+ private TextView mBacklinksCrossProfileError;
private AppClipsViewModel mViewModel;
private ResultReceiver mResultReceiver;
@@ -192,8 +196,8 @@
mBacklinksDataTextView = mLayout.findViewById(R.id.backlinks_data);
mBacklinksIncludeDataCheckBox = mLayout.findViewById(R.id.backlinks_include_data);
mBacklinksIncludeDataCheckBox.setOnCheckedChangeListener(
- (buttonView, isChecked) ->
- mBacklinksDataTextView.setVisibility(isChecked ? View.VISIBLE : View.GONE));
+ this::backlinksIncludeDataCheckBoxCheckedChangeListener);
+ mBacklinksCrossProfileError = mLayout.findViewById(R.id.backlinks_cross_profile_error);
mViewModel = new ViewModelProvider(this, mViewModelFactory).get(AppClipsViewModel.class);
mViewModel.getScreenshot().observe(this, this::setScreenshot);
@@ -312,10 +316,11 @@
Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS);
data.putParcelable(EXTRA_SCREENSHOT_URI, uri);
+ InternalBacklinksData selectedBacklink = mViewModel.mSelectedBacklinksLiveData.getValue();
if (mBacklinksIncludeDataCheckBox.getVisibility() == View.VISIBLE
&& mBacklinksIncludeDataCheckBox.isChecked()
- && mViewModel.mSelectedBacklinksLiveData.getValue() != null) {
- ClipData backlinksData = mViewModel.mSelectedBacklinksLiveData.getValue().getClipData();
+ && selectedBacklink instanceof BacklinksData) {
+ ClipData backlinksData = ((BacklinksData) selectedBacklink).getClipData();
data.putParcelable(EXTRA_CLIP_DATA, backlinksData);
DebugLogger.INSTANCE.logcatMessage(this,
@@ -459,6 +464,38 @@
mBacklinksDataTextView.setCompoundDrawablesRelative(/* start= */ appIcon, /* top= */
null, /* end= */ dropDownIcon, /* bottom= */ null);
+
+ updateViewsToShowOrHideBacklinkError(backlinksData);
+ }
+
+ /** Updates views to show or hide error with backlink. */
+ private void updateViewsToShowOrHideBacklinkError(InternalBacklinksData backlinksData) {
+ // Remove the check box change listener before updating it to avoid updating backlink text
+ // view visibility.
+ mBacklinksIncludeDataCheckBox.setOnCheckedChangeListener(null);
+ if (backlinksData instanceof CrossProfileError) {
+ // There's error with the backlink, unselect the checkbox and disable it.
+ mBacklinksIncludeDataCheckBox.setEnabled(false);
+ mBacklinksIncludeDataCheckBox.setChecked(false);
+ mBacklinksIncludeDataCheckBox.setAlpha(DISABLE_ALPHA);
+
+ mBacklinksCrossProfileError.setVisibility(View.VISIBLE);
+ } else {
+ // When there is no error, ensure the check box is enabled and checked.
+ mBacklinksIncludeDataCheckBox.setEnabled(true);
+ mBacklinksIncludeDataCheckBox.setChecked(true);
+ mBacklinksIncludeDataCheckBox.setAlpha(1.0f);
+
+ mBacklinksCrossProfileError.setVisibility(View.GONE);
+ }
+
+ // (Re)Set the check box change listener as we're done making changes to the check box.
+ mBacklinksIncludeDataCheckBox.setOnCheckedChangeListener(
+ this::backlinksIncludeDataCheckBoxCheckedChangeListener);
+ }
+
+ private void backlinksIncludeDataCheckBoxCheckedChangeListener(View unused, boolean isChecked) {
+ mBacklinksDataTextView.setVisibility(isChecked ? View.VISIBLE : View.GONE);
}
private Rect createBacklinksTextViewDrawableBounds() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
index 3530b3f..9a38358 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
@@ -23,13 +23,13 @@
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.IActivityTaskManager;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.app.assist.AssistContent;
import android.content.ClipData;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -51,11 +51,14 @@
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
+import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.log.DebugLogger;
import com.android.systemui.screenshot.AssistContentRequester;
import com.android.systemui.screenshot.ImageExporter;
+import com.android.systemui.screenshot.appclips.InternalBacklinksData.BacklinksData;
+import com.android.systemui.screenshot.appclips.InternalBacklinksData.CrossProfileError;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
@@ -82,7 +85,7 @@
private final ImageExporter mImageExporter;
private final IActivityTaskManager mAtmService;
private final AssistContentRequester mAssistContentRequester;
- private final PackageManager mPackageManager;
+ @Application private final Context mContext;
@Main
private final Executor mMainExecutor;
@@ -97,13 +100,13 @@
private AppClipsViewModel(AppClipsCrossProcessHelper appClipsCrossProcessHelper,
ImageExporter imageExporter, IActivityTaskManager atmService,
- AssistContentRequester assistContentRequester, PackageManager packageManager,
+ AssistContentRequester assistContentRequester, @Application Context context,
@Main Executor mainExecutor, @Background Executor bgExecutor) {
mAppClipsCrossProcessHelper = appClipsCrossProcessHelper;
mImageExporter = imageExporter;
mAtmService = atmService;
mAssistContentRequester = assistContentRequester;
- mPackageManager = packageManager;
+ mContext = context;
mMainExecutor = mainExecutor;
mBgExecutor = bgExecutor;
@@ -243,6 +246,10 @@
allTasksOnDisplay
.stream()
.filter(taskInfo -> shouldIncludeTask(taskInfo, taskIdsToIgnore))
+ .map(taskInfo -> new InternalTaskInfo(taskInfo.topActivityInfo,
+ taskInfo.taskId, taskInfo.userId,
+ getPackageManagerForUser(taskInfo.userId)))
+ .filter(this::canAppStartThroughLauncher)
.map(this::getBacklinksDataForTaskInfo)
.toList(),
mBgExecutor);
@@ -284,33 +291,34 @@
/**
* Returns whether the app represented by the provided {@link TaskInfo} should be included for
* querying for {@link AssistContent}.
+ *
+ * <p>This does not check whether the task has a launcher icon.
*/
private boolean shouldIncludeTask(TaskInfo taskInfo, Set<Integer> taskIdsToIgnore) {
DebugLogger.INSTANCE.logcatMessage(this,
() -> String.format("shouldIncludeTask taskId %d; topActivity %s", taskInfo.taskId,
taskInfo.topActivity));
- // Only consider tasks that shouldn't be ignored, are visible, running, and have a launcher
- // icon. Furthermore, types such as launcher/home/dock/assistant are ignored.
+ // Only consider tasks that shouldn't be ignored, are visible, and running. Furthermore,
+ // types such as launcher/home/dock/assistant are ignored.
return !taskIdsToIgnore.contains(taskInfo.taskId)
&& taskInfo.isVisible
&& taskInfo.isRunning
&& taskInfo.numActivities > 0
&& taskInfo.topActivity != null
&& taskInfo.topActivityInfo != null
- && taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_STANDARD
- && canAppStartThroughLauncher(taskInfo.topActivity.getPackageName());
+ && taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_STANDARD;
}
/**
- * Returns whether the app represented by the provided {@code packageName} can be launched
- * through the all apps tray by a user.
+ * Returns whether the app represented by the {@link InternalTaskInfo} can be launched through
+ * the all apps tray by a user.
*/
- private boolean canAppStartThroughLauncher(String packageName) {
+ private boolean canAppStartThroughLauncher(InternalTaskInfo internalTaskInfo) {
// Use Intent.resolveActivity API to check if the intent resolves as that is what Android
// uses internally when apps use Context.startActivity.
- return getMainLauncherIntentForPackage(packageName).resolveActivity(mPackageManager)
- != null;
+ return getMainLauncherIntentForTask(internalTaskInfo)
+ .resolveActivity(internalTaskInfo.getPackageManager()) != null;
}
/**
@@ -318,18 +326,36 @@
* is captured by querying the system using {@link TaskInfo#taskId}.
*/
private ListenableFuture<InternalBacklinksData> getBacklinksDataForTaskInfo(
- TaskInfo taskInfo) {
+ InternalTaskInfo internalTaskInfo) {
DebugLogger.INSTANCE.logcatMessage(this,
() -> String.format("getBacklinksDataForTaskId for taskId %d; topActivity %s",
- taskInfo.taskId, taskInfo.topActivity));
+ internalTaskInfo.getTaskId(),
+ internalTaskInfo.getTopActivityNameForDebugLogging()));
+
+ // Unlike other SysUI components, App Clips is started by the notes app so it runs as the
+ // same user as the notes app. That is, if the notes app was running as work profile user
+ // then App Clips also runs as work profile user. This is why while checking for user of the
+ // screenshotted app the check is performed using UserHandle.myUserId instead of using the
+ // more complex UserTracker.
+ if (internalTaskInfo.getUserId() != UserHandle.myUserId()) {
+ return getCrossProfileErrorBacklinkForTask(internalTaskInfo);
+ }
SettableFuture<InternalBacklinksData> backlinksData = SettableFuture.create();
- int taskId = taskInfo.taskId;
- mAssistContentRequester.requestAssistContent(taskId, assistContent ->
- backlinksData.set(getBacklinksDataFromAssistContent(taskInfo, assistContent)));
+ int taskId = internalTaskInfo.getTaskId();
+ mAssistContentRequester.requestAssistContent(taskId, assistContent -> backlinksData.set(
+ getBacklinksDataFromAssistContent(internalTaskInfo, assistContent)));
return withTimeout(backlinksData);
}
+ private ListenableFuture<InternalBacklinksData> getCrossProfileErrorBacklinkForTask(
+ InternalTaskInfo internalTaskInfo) {
+ String appName = internalTaskInfo.getTopActivityAppName();
+ Drawable appIcon = internalTaskInfo.getTopActivityAppIcon();
+ InternalBacklinksData errorData = new CrossProfileError(appIcon, appName);
+ return Futures.immediateFuture(errorData);
+ }
+
/** Returns the same {@link ListenableFuture} but with a 5 {@link TimeUnit#SECONDS} timeout. */
private static <V> ListenableFuture<V> withTimeout(ListenableFuture<V> future) {
return Futures.withTimeout(future, 5L, TimeUnit.SECONDS,
@@ -351,22 +377,24 @@
* {@link Intent#ACTION_MAIN} and {@link Intent#CATEGORY_LAUNCHER}.
* </ul>
*
- * @param taskInfo {@link RootTaskInfo} of the task which provided the {@link AssistContent}.
+ * @param internalTaskInfo {@link InternalTaskInfo} of the task which provided the
+ * {@link AssistContent}.
* @param content the {@link AssistContent} to map into Backlinks {@link ClipData}.
* @return {@link InternalBacklinksData} that represents the Backlinks data along with app icon.
*/
- private InternalBacklinksData getBacklinksDataFromAssistContent(TaskInfo taskInfo,
+ private InternalBacklinksData getBacklinksDataFromAssistContent(
+ InternalTaskInfo internalTaskInfo,
@Nullable AssistContent content) {
DebugLogger.INSTANCE.logcatMessage(this,
() -> String.format("getBacklinksDataFromAssistContent taskId %d; topActivity %s",
- taskInfo.taskId, taskInfo.topActivity));
+ internalTaskInfo.getTaskId(),
+ internalTaskInfo.getTopActivityNameForDebugLogging()));
- String appName = getAppNameOfTask(taskInfo);
- String packageName = taskInfo.topActivity.getPackageName();
- Drawable appIcon = taskInfo.topActivityInfo.loadIcon(mPackageManager);
+ String appName = internalTaskInfo.getTopActivityAppName();
+ Drawable appIcon = internalTaskInfo.getTopActivityAppIcon();
ClipData mainLauncherIntent = ClipData.newIntent(appName,
- getMainLauncherIntentForPackage(packageName));
- InternalBacklinksData fallback = new InternalBacklinksData(mainLauncherIntent, appIcon);
+ getMainLauncherIntentForTask(internalTaskInfo));
+ InternalBacklinksData fallback = new BacklinksData(mainLauncherIntent, appIcon);
if (content == null) {
return fallback;
}
@@ -378,10 +406,10 @@
Uri uri = content.getWebUri();
Intent backlinksIntent = new Intent(ACTION_VIEW).setData(uri);
- if (doesIntentResolveToSamePackage(backlinksIntent, packageName)) {
+ if (doesIntentResolveToSameTask(backlinksIntent, internalTaskInfo)) {
DebugLogger.INSTANCE.logcatMessage(this,
() -> "getBacklinksDataFromAssistContent: using app provided uri");
- return new InternalBacklinksData(ClipData.newRawUri(appName, uri), appIcon);
+ return new BacklinksData(ClipData.newRawUri(appName, uri), appIcon);
}
}
@@ -391,11 +419,10 @@
() -> "getBacklinksDataFromAssistContent: app has provided an intent");
Intent backlinksIntent = content.getIntent();
- if (doesIntentResolveToSamePackage(backlinksIntent, packageName)) {
+ if (doesIntentResolveToSameTask(backlinksIntent, internalTaskInfo)) {
DebugLogger.INSTANCE.logcatMessage(this,
() -> "getBacklinksDataFromAssistContent: using app provided intent");
- return new InternalBacklinksData(ClipData.newIntent(appName, backlinksIntent),
- appIcon);
+ return new BacklinksData(ClipData.newIntent(appName, backlinksIntent), appIcon);
}
}
@@ -404,28 +431,28 @@
return fallback;
}
- private boolean doesIntentResolveToSamePackage(Intent intentToResolve,
- String requiredPackageName) {
- ComponentName resolvedComponent = intentToResolve.resolveActivity(mPackageManager);
+ private boolean doesIntentResolveToSameTask(Intent intentToResolve,
+ InternalTaskInfo requiredTaskInfo) {
+ PackageManager packageManager = requiredTaskInfo.getPackageManager();
+ ComponentName resolvedComponent = intentToResolve.resolveActivity(packageManager);
if (resolvedComponent == null) {
return false;
}
+ String requiredPackageName = requiredTaskInfo.getTopActivityPackageName();
return resolvedComponent.getPackageName().equals(requiredPackageName);
}
- private String getAppNameOfTask(TaskInfo taskInfo) {
- return taskInfo.topActivityInfo.loadLabel(mPackageManager).toString();
- }
-
- private Intent getMainLauncherIntentForPackage(String pkgName) {
+ private Intent getMainLauncherIntentForTask(InternalTaskInfo internalTaskInfo) {
+ String pkgName = internalTaskInfo.getTopActivityPackageName();
Intent intent = new Intent(ACTION_MAIN).addCategory(CATEGORY_LAUNCHER).setPackage(pkgName);
// Not all apps use DEFAULT_CATEGORY for their main launcher activity so the exact component
// needs to be queried and set on the Intent in order for note-taking apps to be able to
// start this intent. When starting an activity with an implicit intent, Android adds the
// DEFAULT_CATEGORY flag otherwise it fails to resolve the intent.
- ResolveInfo resolvedActivity = mPackageManager.resolveActivity(intent, /* flags= */ 0);
+ PackageManager packageManager = internalTaskInfo.getPackageManager();
+ ResolveInfo resolvedActivity = packageManager.resolveActivity(intent, /* flags= */ 0);
if (resolvedActivity != null) {
intent.setComponent(resolvedActivity.getComponentInfo().getComponentName());
}
@@ -433,6 +460,17 @@
return intent;
}
+ private PackageManager getPackageManagerForUser(int userId) {
+ // If app clips was launched as the same user, then reuse the available PM from mContext.
+ if (mContext.getUserId() == userId) {
+ return mContext.getPackageManager();
+ }
+
+ // PackageManager required for a different user, create its context and return its PM.
+ UserHandle userHandle = UserHandle.of(userId);
+ return mContext.createContextAsUser(userHandle, /* flags= */ 0).getPackageManager();
+ }
+
/** Helper factory to help with injecting {@link AppClipsViewModel}. */
static final class Factory implements ViewModelProvider.Factory {
@@ -440,7 +478,7 @@
private final ImageExporter mImageExporter;
private final IActivityTaskManager mAtmService;
private final AssistContentRequester mAssistContentRequester;
- private final PackageManager mPackageManager;
+ @Application private final Context mContext;
@Main
private final Executor mMainExecutor;
@Background
@@ -449,13 +487,13 @@
@Inject
Factory(AppClipsCrossProcessHelper appClipsCrossProcessHelper, ImageExporter imageExporter,
IActivityTaskManager atmService, AssistContentRequester assistContentRequester,
- PackageManager packageManager, @Main Executor mainExecutor,
+ @Application Context context, @Main Executor mainExecutor,
@Background Executor bgExecutor) {
mAppClipsCrossProcessHelper = appClipsCrossProcessHelper;
mImageExporter = imageExporter;
mAtmService = atmService;
mAssistContentRequester = assistContentRequester;
- mPackageManager = packageManager;
+ mContext = context;
mMainExecutor = mainExecutor;
mBgExecutor = bgExecutor;
}
@@ -469,7 +507,7 @@
//noinspection unchecked
return (T) new AppClipsViewModel(mAppClipsCrossProcessHelper, mImageExporter,
- mAtmService, mAssistContentRequester, mPackageManager, mMainExecutor,
+ mAtmService, mAssistContentRequester, mContext, mMainExecutor,
mBgExecutor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
index 30c33c5..234692e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
@@ -16,10 +16,49 @@
package com.android.systemui.screenshot.appclips
+import android.app.TaskInfo
import android.content.ClipData
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
-/** A class to hold the [ClipData] for backlinks and the corresponding app's [Drawable] icon. */
-internal data class InternalBacklinksData(val clipData: ClipData, val appIcon: Drawable) {
- var displayLabel: String = clipData.description.label.toString()
+/**
+ * A class to hold the [ClipData] for backlinks, the corresponding app's [Drawable] icon, and
+ * represent error when necessary.
+ */
+internal sealed class InternalBacklinksData(
+ open val appIcon: Drawable,
+ open var displayLabel: String
+) {
+ data class BacklinksData(val clipData: ClipData, override val appIcon: Drawable) :
+ InternalBacklinksData(appIcon, clipData.description.label.toString())
+
+ data class CrossProfileError(
+ override val appIcon: Drawable,
+ override var displayLabel: String
+ ) : InternalBacklinksData(appIcon, displayLabel)
+}
+
+/**
+ * A class to hold important members of [TaskInfo] and its associated user's [PackageManager] for
+ * ease of querying.
+ *
+ * @note A task can have a different app running on top. For example, an app "A" can use camera app
+ * to capture an image. In this case the top app will be the camera app even though the task
+ * belongs to app A. This is expected behaviour because user will be taking a screenshot of the
+ * content rendered by the camera (top) app.
+ */
+internal data class InternalTaskInfo(
+ private val topActivityInfo: ActivityInfo,
+ val taskId: Int,
+ val userId: Int,
+ val packageManager: PackageManager
+) {
+ fun getTopActivityNameForDebugLogging(): String = topActivityInfo.name
+
+ fun getTopActivityPackageName(): String = topActivityInfo.packageName
+
+ fun getTopActivityAppName(): String = topActivityInfo.loadLabel(packageManager).toString()
+
+ fun getTopActivityAppIcon(): Drawable = topActivityInfo.loadIcon(packageManager)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 48e69893..7c01784 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1389,6 +1389,10 @@
}
private void clampScrollPosition() {
+ // NSSL doesn't control scrolling with SceneContainer enabled
+ if (SceneContainerFlag.isEnabled()) {
+ return;
+ }
int scrollRange = getScrollRange();
if (scrollRange < mOwnScrollY && !mAmbientState.isClearAllInProgress()) {
// if the scroll boundary updates the position of the stack,
@@ -1399,12 +1403,8 @@
}
public int getTopPadding() {
- // TODO(b/332574413) replace all usages of getTopPadding()
- if (SceneContainerFlag.isEnabled()) {
- return (int) mAmbientState.getStackTop();
- } else {
- return mAmbientState.getTopPadding();
- }
+ SceneContainerFlag.assertInLegacyMode();
+ return mAmbientState.getTopPadding();
}
private void onTopPaddingChanged(boolean animate) {
@@ -2446,6 +2446,11 @@
}
private int getScrollRange() {
+ // TODO(b/360091533) Disable the usages of #getScrollRange() with SceneContainer enabled,
+ // because NSSL shouldn't control its own scrolling.
+ if (SceneContainerFlag.isEnabled()) {
+ return 0;
+ }
// In current design, it only use the top HUN to treat all of HUNs
// although there are more than one HUNs
int contentHeight = mContentHeight;
@@ -2881,7 +2886,9 @@
NotificationEntry entry = ((ExpandableNotificationRow) child).getEntry();
entry.removeOnSensitivityChangedListener(mOnChildSensitivityChangedListener);
}
- updateScrollStateForRemovedChild(child);
+ if (!SceneContainerFlag.isEnabled()) {
+ updateScrollStateForRemovedChild(child);
+ }
boolean animationGenerated = container != null && generateRemoveAnimation(child);
if (animationGenerated) {
if (!mSwipedOutViews.contains(child) || !isFullySwipedOut(child)) {
@@ -3060,6 +3067,7 @@
* @param removedChild the removed child
*/
private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
+ SceneContainerFlag.assertInLegacyMode();
final int startingPosition = getPositionInLinearLayout(removedChild);
final int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements;
final int endPosition = startingPosition + childHeight;
@@ -3081,6 +3089,7 @@
* @return the amount of scrolling needed to start clipping notifications.
*/
private int getScrollAmountToScrollBoundary() {
+ SceneContainerFlag.assertInLegacyMode();
if (mShouldUseSplitNotificationShade) {
return mSidePaddings;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 456265b..bef552c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -78,7 +78,27 @@
private lateinit var battery: BatteryMeterView
private lateinit var clock: Clock
- private lateinit var statusContainer: View
+ private lateinit var startSideContainer: View
+ private lateinit var endSideContainer: View
+
+ private val iconsOnTouchListener =
+ object : View.OnTouchListener {
+ override fun onTouch(v: View, event: MotionEvent): Boolean {
+ // We want to handle only mouse events here to avoid stealing finger touches
+ // from status bar which expands shade when swiped down on. See b/326097469.
+ // We're using onTouchListener instead of onClickListener as the later will lead
+ // to isClickable being set to true and hence ALL touches always being
+ // intercepted. See [View.OnTouchEvent]
+ if (event.source == InputDevice.SOURCE_MOUSE) {
+ if (event.action == MotionEvent.ACTION_UP) {
+ v.performClick()
+ shadeController.animateExpandShade()
+ }
+ return true
+ }
+ return false
+ }
+ }
private val configurationListener =
object : ConfigurationController.ConfigurationListener {
@@ -88,34 +108,10 @@
}
override fun onViewAttached() {
- statusContainer = mView.requireViewById(R.id.system_icons)
clock = mView.requireViewById(R.id.clock)
battery = mView.requireViewById(R.id.battery)
-
addDarkReceivers()
-
- statusContainer.setOnHoverListener(
- statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer)
- )
- statusContainer.setOnTouchListener(
- object : View.OnTouchListener {
- override fun onTouch(v: View, event: MotionEvent): Boolean {
- // We want to handle only mouse events here to avoid stealing finger touches
- // from status bar which expands shade when swiped down on. See b/326097469.
- // We're using onTouchListener instead of onClickListener as the later will lead
- // to isClickable being set to true and hence ALL touches always being
- // intercepted. See [View.OnTouchEvent]
- if (event.source == InputDevice.SOURCE_MOUSE) {
- if (event.action == MotionEvent.ACTION_UP) {
- v.performClick()
- shadeController.animateExpandShade()
- }
- return true
- }
- return false
- }
- }
- )
+ addCursorSupportToIconContainers()
progressProvider?.setReadyToHandleTransition(true)
configurationController.addCallback(configurationListener)
@@ -146,10 +142,25 @@
}
}
+ private fun addCursorSupportToIconContainers() {
+ endSideContainer = mView.requireViewById(R.id.system_icons)
+ endSideContainer.setOnHoverListener(
+ statusOverlayHoverListenerFactory.createDarkAwareListener(endSideContainer)
+ )
+ endSideContainer.setOnTouchListener(iconsOnTouchListener)
+
+ startSideContainer = mView.requireViewById(R.id.status_bar_start_side_content)
+ startSideContainer.setOnHoverListener(
+ statusOverlayHoverListenerFactory.createDarkAwareListener(startSideContainer)
+ )
+ startSideContainer.setOnTouchListener(iconsOnTouchListener)
+ }
+
@VisibleForTesting
public override fun onViewDetached() {
removeDarkReceivers()
- statusContainer.setOnHoverListener(null)
+ startSideContainer.setOnHoverListener(null)
+ endSideContainer.setOnHoverListener(null)
progressProvider?.setReadyToHandleTransition(false)
moveFromCenterAnimationController?.onViewDetached()
configurationController.removeCallback(configurationListener)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt
index 73f5237..9715772 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt
@@ -17,9 +17,9 @@
package com.android.systemui.volume.dagger
import android.view.accessibility.CaptioningManager
-import com.android.settingslib.view.accessibility.data.repository.CaptioningRepository
-import com.android.settingslib.view.accessibility.data.repository.CaptioningRepositoryImpl
-import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
+import com.android.systemui.accessibility.data.repository.CaptioningRepository
+import com.android.systemui.accessibility.data.repository.CaptioningRepositoryImpl
+import com.android.systemui.accessibility.domain.interactor.CaptioningInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt
index 85da1d0..52f2ce6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt
@@ -17,7 +17,7 @@
package com.android.systemui.volume.panel.component.captioning.domain
import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
+import com.android.systemui.accessibility.domain.interactor.CaptioningInteractor
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
index ca5aef8..9e70843 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
@@ -18,7 +18,7 @@
import android.content.Context
import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
+import com.android.systemui.accessibility.domain.interactor.CaptioningInteractor
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.res.R
import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index 0c8d880..6a66c40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -54,7 +54,6 @@
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManagerFactory
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -126,8 +125,6 @@
private lateinit var mediaData: MediaData
@JvmField @Rule val mockito = MockitoJUnit.rule()
- private val kosmos = testKosmos()
-
@Before
fun setUp() {
fakeFgExecutor = FakeExecutor(FakeSystemClock())
@@ -144,7 +141,6 @@
{ localBluetoothManager },
fakeFgExecutor,
fakeBgExecutor,
- kosmos.mediaDeviceLogger,
)
manager.addListener(listener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
index eb1a04d..e1cd5e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
@@ -30,12 +30,15 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.IActivityTaskManager;
import android.app.assist.AssistContent;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -51,6 +54,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
@@ -120,6 +124,8 @@
@Mock
private AssistContentRequester mAssistContentRequester;
@Mock
+ private Context mMockedContext;
+ @Mock
private PackageManager mPackageManager;
@Mock
private UserTracker mUserTracker;
@@ -127,6 +133,9 @@
private UiEventLogger mUiEventLogger;
private AppClipsActivity mActivity;
+ private TextView mBacklinksDataTextView;
+ private CheckBox mBacklinksIncludeDataCheckBox;
+ private TextView mBacklinksCrossProfileErrorTextView;
// Using the deprecated ActivityTestRule and SingleActivityFactory to help with injecting mocks.
private final SingleActivityFactory<AppClipsActivityTestable> mFactory =
@@ -136,7 +145,7 @@
return new AppClipsActivityTestable(
new AppClipsViewModel.Factory(mAppClipsCrossProcessHelper,
mImageExporter, mAtmService, mAssistContentRequester,
- mPackageManager, getContext().getMainExecutor(),
+ mMockedContext, getContext().getMainExecutor(),
directExecutor()),
mPackageManager, mUserTracker, mUiEventLogger);
}
@@ -162,6 +171,9 @@
when(mImageExporter.export(any(Executor.class), any(UUID.class), any(Bitmap.class),
eq(Process.myUserHandle()), eq(Display.DEFAULT_DISPLAY)))
.thenReturn(Futures.immediateFuture(result));
+
+ when(mMockedContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mMockedContext.createContextAsUser(any(), anyInt())).thenReturn(mMockedContext);
}
@After
@@ -175,10 +187,9 @@
launchActivity();
assertThat(((ImageView) mActivity.findViewById(R.id.preview)).getDrawable()).isNotNull();
- assertThat(mActivity.findViewById(R.id.backlinks_data).getVisibility())
- .isEqualTo(View.GONE);
- assertThat(mActivity.findViewById(R.id.backlinks_include_data).getVisibility())
- .isEqualTo(View.GONE);
+ assertThat(mBacklinksDataTextView.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mBacklinksIncludeDataCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mBacklinksCrossProfileErrorTextView.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -228,20 +239,23 @@
waitForIdleSync();
assertThat(mDisplayIdCaptor.getValue()).isEqualTo(mActivity.getDisplayId());
- TextView backlinksData = mActivity.findViewById(R.id.backlinks_data);
- assertThat(backlinksData.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(backlinksData.getText().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
- assertThat(backlinksData.getCompoundDrawablesRelative()[0]).isEqualTo(FAKE_DRAWABLE);
+ assertThat(mBacklinksDataTextView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBacklinksDataTextView.getText().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
+ assertThat(mBacklinksDataTextView.getCompoundDrawablesRelative()[0])
+ .isEqualTo(FAKE_DRAWABLE);
// Verify dropdown icon is not shown and there are no click listeners on text view.
- assertThat(backlinksData.getCompoundDrawablesRelative()[2]).isNull();
- assertThat(backlinksData.hasOnClickListeners()).isFalse();
+ assertThat(mBacklinksDataTextView.getCompoundDrawablesRelative()[2]).isNull();
+ assertThat(mBacklinksDataTextView.hasOnClickListeners()).isFalse();
- CheckBox backlinksIncludeData = mActivity.findViewById(R.id.backlinks_include_data);
- assertThat(backlinksIncludeData.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(backlinksIncludeData.getText().toString())
+ assertThat(mBacklinksIncludeDataCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBacklinksIncludeDataCheckBox.getText().toString())
.isEqualTo(mActivity.getString(R.string.backlinks_include_link));
- assertThat(backlinksIncludeData.isChecked()).isTrue();
+ assertThat(mBacklinksIncludeDataCheckBox.isChecked()).isTrue();
+
+ assertThat(mBacklinksIncludeDataCheckBox.getAlpha()).isEqualTo(1.0f);
+ assertThat(mBacklinksIncludeDataCheckBox.isEnabled()).isTrue();
+ assertThat(mBacklinksCrossProfileErrorTextView.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -258,8 +272,7 @@
assertThat(backlinksIncludeData.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(backlinksIncludeData.isChecked()).isFalse();
- TextView backlinksData = mActivity.findViewById(R.id.backlinks_data);
- assertThat(backlinksData.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mBacklinksDataTextView.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -300,12 +313,11 @@
waitForIdleSync();
// Verify default backlink shown to user and text view has on click listener.
- TextView backlinksData = mActivity.findViewById(R.id.backlinks_data);
- assertThat(backlinksData.getText().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
- assertThat(backlinksData.hasOnClickListeners()).isTrue();
+ assertThat(mBacklinksDataTextView.getText().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
+ assertThat(mBacklinksDataTextView.hasOnClickListeners()).isTrue();
// Verify dropdown icon is not null.
- assertThat(backlinksData.getCompoundDrawablesRelative()[2]).isNotNull();
+ assertThat(mBacklinksDataTextView.getCompoundDrawablesRelative()[2]).isNotNull();
}
@Test
@@ -336,12 +348,35 @@
waitForIdleSync();
// Verify default backlink shown to user has the numerical suffix.
- TextView backlinksData = mActivity.findViewById(R.id.backlinks_data);
- assertThat(backlinksData.getText().toString()).isEqualTo(
- mContext.getString(R.string.backlinks_duplicate_label_format,
+ assertThat(mBacklinksDataTextView.getText().toString()).isEqualTo(
+ getContext().getString(R.string.backlinks_duplicate_label_format,
BACKLINKS_TASK_APP_NAME, 1));
}
+ @Test
+ @EnableFlags(Flags.FLAG_APP_CLIPS_BACKLINKS)
+ public void appClipsLaunched_backlinks_singleBacklink_crossProfileError()
+ throws RemoteException {
+ // Set up mocking for cross profile backlink.
+ setUpMocksForBacklinks();
+ ActivityManager.RunningTaskInfo crossProfileTaskInfo = createTaskInfoForBacklinksTask();
+ crossProfileTaskInfo.userId = UserHandle.myUserId() + 1;
+ reset(mAtmService);
+ when(mAtmService.getTasks(eq(Integer.MAX_VALUE), eq(false), eq(false),
+ mDisplayIdCaptor.capture())).thenReturn(List.of(crossProfileTaskInfo));
+
+ // Trigger backlinks.
+ launchActivity();
+ waitForIdleSync();
+
+ // Verify views for cross profile backlinks error.
+ assertThat(mBacklinksIncludeDataCheckBox.getAlpha()).isLessThan(1.0f);
+ assertThat(mBacklinksIncludeDataCheckBox.isEnabled()).isFalse();
+ assertThat(mBacklinksIncludeDataCheckBox.isChecked()).isFalse();
+
+ assertThat(mBacklinksCrossProfileErrorTextView.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
private void setUpMocksForBacklinks() throws RemoteException {
when(mAtmService.getTasks(eq(Integer.MAX_VALUE), eq(false), eq(false),
mDisplayIdCaptor.capture()))
@@ -373,11 +408,15 @@
mActivity = mActivityRule.launchActivity(intent);
waitForIdleSync();
+ mBacklinksDataTextView = mActivity.findViewById(R.id.backlinks_data);
+ mBacklinksIncludeDataCheckBox = mActivity.findViewById(R.id.backlinks_include_data);
+ mBacklinksCrossProfileErrorTextView = mActivity.findViewById(
+ R.id.backlinks_cross_profile_error);
}
private ResultReceiver createResultReceiver(
BiConsumer<Integer, Bundle> resultReceiverConsumer) {
- ResultReceiver testReceiver = new ResultReceiver(mContext.getMainThreadHandler()) {
+ ResultReceiver testReceiver = new ResultReceiver(getContext().getMainThreadHandler()) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
resultReceiverConsumer.accept(resultCode, resultData);
@@ -394,7 +433,7 @@
}
private void runOnMainThread(Runnable runnable) {
- mContext.getMainExecutor().execute(runnable);
+ getContext().getMainExecutor().execute(runnable);
}
private static ResolveInfo createBacklinksTaskResolveInfo() {
@@ -418,6 +457,7 @@
taskInfo.topActivityInfo = createBacklinksTaskResolveInfo().activityInfo;
taskInfo.baseIntent = new Intent().setComponent(taskInfo.topActivity);
taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+ taskInfo.userId = UserHandle.myUserId();
return taskInfo;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
index 178547e..5d71c05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
@@ -30,6 +30,7 @@
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
@@ -43,6 +44,7 @@
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -62,6 +64,8 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.screenshot.AssistContentRequester;
import com.android.systemui.screenshot.ImageExporter;
+import com.android.systemui.screenshot.appclips.InternalBacklinksData.BacklinksData;
+import com.android.systemui.screenshot.appclips.InternalBacklinksData.CrossProfileError;
import com.google.common.util.concurrent.Futures;
@@ -92,10 +96,16 @@
private static final String BACKLINKS_TASK_PACKAGE_NAME = "backlinksTaskPackageName";
private static final AssistContent EMPTY_ASSIST_CONTENT = new AssistContent();
- @Mock private AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
- @Mock private ImageExporter mImageExporter;
- @Mock private IActivityTaskManager mAtmService;
- @Mock private AssistContentRequester mAssistContentRequester;
+ @Mock
+ private AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
+ @Mock
+ private ImageExporter mImageExporter;
+ @Mock
+ private IActivityTaskManager mAtmService;
+ @Mock
+ private AssistContentRequester mAssistContentRequester;
+ @Mock
+ Context mMockedContext;
@Mock
private PackageManager mPackageManager;
private ArgumentCaptor<Intent> mPackageManagerIntentCaptor;
@@ -112,10 +122,14 @@
when(mPackageManager.resolveActivity(mPackageManagerIntentCaptor.capture(), anyInt()))
.thenReturn(createBacklinksTaskResolveInfo());
when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
+ when(mMockedContext.getPackageManager()).thenReturn(mPackageManager);
mViewModel = new AppClipsViewModel.Factory(mAppClipsCrossProcessHelper, mImageExporter,
- mAtmService, mAssistContentRequester, mPackageManager,
+ mAtmService, mAssistContentRequester, mMockedContext,
getContext().getMainExecutor(), directExecutor()).create(AppClipsViewModel.class);
+
+ when(mMockedContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mMockedContext.createContextAsUser(any(), anyInt())).thenReturn(mMockedContext);
}
@Test
@@ -199,7 +213,7 @@
assertThat(queriedIntent.getData()).isEqualTo(expectedUri);
assertThat(queriedIntent.getAction()).isEqualTo(ACTION_VIEW);
- InternalBacklinksData result = mViewModel.mSelectedBacklinksLiveData.getValue();
+ BacklinksData result = (BacklinksData) mViewModel.mSelectedBacklinksLiveData.getValue();
assertThat(result.getAppIcon()).isEqualTo(FAKE_DRAWABLE);
ClipData clipData = result.getClipData();
ClipDescription resultDescription = clipData.getDescription();
@@ -238,7 +252,7 @@
Intent queriedIntent = mPackageManagerIntentCaptor.getValue();
assertThat(queriedIntent.getPackage()).isEqualTo(expectedIntent.getPackage());
- InternalBacklinksData result = mViewModel.mSelectedBacklinksLiveData.getValue();
+ BacklinksData result = (BacklinksData) mViewModel.mSelectedBacklinksLiveData.getValue();
assertThat(result.getAppIcon()).isEqualTo(FAKE_DRAWABLE);
ClipData clipData = result.getClipData();
ClipDescription resultDescription = clipData.getDescription();
@@ -307,6 +321,8 @@
@Test
public void triggerBacklinks_taskIdsToIgnoreConsidered_noBacklinkAvailable() {
+ mockForAssistContent(EMPTY_ASSIST_CONTENT, BACKLINKS_TASK_ID);
+
mViewModel.triggerBacklinks(Set.of(BACKLINKS_TASK_ID), DEFAULT_DISPLAY);
waitForIdleSync();
@@ -362,16 +378,58 @@
waitForIdleSync();
// Verify two backlinks are received and the first backlink is set as default selected.
- assertThat(mViewModel.mSelectedBacklinksLiveData.getValue().getClipData().getItemAt(
- 0).getUri()).isEqualTo(expectedUri);
+ assertThat(
+ ((BacklinksData) mViewModel.mSelectedBacklinksLiveData.getValue())
+ .getClipData().getItemAt(0).getUri())
+ .isEqualTo(expectedUri);
List<InternalBacklinksData> actualBacklinks = mViewModel.getBacklinksLiveData().getValue();
assertThat(actualBacklinks).hasSize(2);
- assertThat(actualBacklinks.get(0).getClipData().getItemAt(0).getUri())
+ assertThat(((BacklinksData) actualBacklinks.get(0)).getClipData().getItemAt(0).getUri())
.isEqualTo(expectedUri);
- assertThat(actualBacklinks.get(1).getClipData().getItemAt(0).getIntent())
+ assertThat(((BacklinksData) actualBacklinks.get(1)).getClipData().getItemAt(0).getIntent())
.isEqualTo(expectedIntent);
}
+ @Test
+ public void triggerBacklinks_singleCrossProfileApp_shouldIndicateError()
+ throws RemoteException {
+ reset(mAtmService);
+ RunningTaskInfo taskInfo = createTaskInfoForBacklinksTask();
+ taskInfo.userId = UserHandle.myUserId() + 1;
+ when(mAtmService.getTasks(Integer.MAX_VALUE, false, false, DEFAULT_DISPLAY))
+ .thenReturn(List.of(taskInfo));
+
+ mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+ waitForIdleSync();
+
+ assertThat(mViewModel.mSelectedBacklinksLiveData.getValue())
+ .isInstanceOf(CrossProfileError.class);
+ }
+
+ @Test
+ public void triggerBacklinks_multipleBacklinks_includesCrossProfileError()
+ throws RemoteException {
+ // Set up mocking for multiple backlinks.
+ mockForAssistContent(EMPTY_ASSIST_CONTENT, BACKLINKS_TASK_ID);
+ reset(mAtmService);
+ RunningTaskInfo runningTaskInfo1 = createTaskInfoForBacklinksTask();
+ RunningTaskInfo runningTaskInfo2 = createTaskInfoForBacklinksTask();
+ runningTaskInfo2.userId = UserHandle.myUserId() + 1;
+
+ when(mAtmService.getTasks(anyInt(), anyBoolean(), anyBoolean(), anyInt()))
+ .thenReturn(List.of(runningTaskInfo1, runningTaskInfo2));
+
+ // Set up complete, trigger the backlinks action.
+ mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+ waitForIdleSync();
+
+ // Verify two backlinks are received and only second has error.
+ List<InternalBacklinksData> actualBacklinks = mViewModel.getBacklinksLiveData().getValue();
+ assertThat(actualBacklinks).hasSize(2);
+ assertThat(actualBacklinks.get(0)).isInstanceOf(BacklinksData.class);
+ assertThat(actualBacklinks.get(1)).isInstanceOf(CrossProfileError.class);
+ }
+
private void resetPackageManagerMockingForUsingFallbackBacklinks() {
ResolveInfo backlinksTaskResolveInfo = createBacklinksTaskResolveInfo();
reset(mPackageManager);
@@ -389,7 +447,7 @@
}
private void verifyMainLauncherBacklinksIntent() {
- InternalBacklinksData result = mViewModel.mSelectedBacklinksLiveData.getValue();
+ BacklinksData result = (BacklinksData) mViewModel.mSelectedBacklinksLiveData.getValue();
assertThat(result.getAppIcon()).isEqualTo(FAKE_DRAWABLE);
ClipData clipData = result.getClipData();
@@ -436,6 +494,7 @@
taskInfo.topActivityInfo = createBacklinksTaskResolveInfo().activityInfo;
taskInfo.baseIntent = new Intent().setComponent(taskInfo.topActivity);
taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+ taskInfo.userId = UserHandle.myUserId();
return taskInfo;
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryKosmos.kt
similarity index 74%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLoggerKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryKosmos.kt
index 76d71dd..5c39e32 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLoggerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryKosmos.kt
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.domain.pipeline
+package com.android.systemui.accessibility.data.repository
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.log.logcatLogBuffer
-var Kosmos.mediaDeviceLogger by
- Kosmos.Fixture { MediaDeviceLogger(logcatLogBuffer("MediaDeviceLoggerKosmos")) }
+var Kosmos.fakeCaptioningRepository by Kosmos.Fixture { FakeCaptioningRepository() }
+val Kosmos.captioningRepository by Kosmos.Fixture { fakeCaptioningRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/FakeCaptioningRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeCaptioningRepository.kt
similarity index 91%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/FakeCaptioningRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeCaptioningRepository.kt
index 663aaf2..2a0e764 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/FakeCaptioningRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeCaptioningRepository.kt
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.view.accessibility.data.repository
+package com.android.systemui.accessibility.data.repository
-import com.android.settingslib.view.accessibility.data.repository.CaptioningRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/CaptioningKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractorKosmos.kt
similarity index 76%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/CaptioningKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractorKosmos.kt
index 0e978f2..2125e95 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/CaptioningKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractorKosmos.kt
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.view.accessibility.data.repository
+package com.android.systemui.accessibility.domain.interactor
-import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
+import com.android.systemui.accessibility.data.repository.captioningRepository
import com.android.systemui.kosmos.Kosmos
-val Kosmos.captioningRepository by Kosmos.Fixture { FakeCaptioningRepository() }
val Kosmos.captioningInteractor by Kosmos.Fixture { CaptioningInteractor(captioningRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.kt
new file mode 100644
index 0000000..a25b29fd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+
+val Kosmos.lockscreenUserActionsViewModel by Fixture {
+ LockscreenUserActionsViewModel(
+ deviceEntryInteractor = deviceEntryInteractor,
+ communalInteractor = communalInteractor,
+ shadeInteractor = shadeInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerKosmos.kt
index 11408d8..c479ce6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerKosmos.kt
@@ -41,6 +41,5 @@
},
fgExecutor = fakeExecutor,
bgExecutor = fakeExecutor,
- logger = mediaDeviceLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
index b3664e1..55f3ed7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
@@ -1,11 +1,18 @@
package com.android.systemui.scene
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.logger.sceneLogger
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.FakeOverlay
+import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
var Kosmos.sceneKeys by Fixture {
listOf(
@@ -27,7 +34,9 @@
)
}
-val Kosmos.fakeOverlays by Fixture { overlayKeys.map { key -> FakeOverlay(key) }.toSet() }
+val Kosmos.fakeOverlaysByKeys by Fixture { overlayKeys.associateWith { FakeOverlay(it) } }
+
+val Kosmos.fakeOverlays by Fixture { fakeOverlaysByKeys.values.toSet() }
val Kosmos.overlays by Fixture { fakeOverlays }
@@ -49,3 +58,20 @@
navigationDistances = navigationDistances,
)
}
+
+val Kosmos.transitionState by Fixture {
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(sceneContainerConfig.initialSceneKey)
+ )
+}
+
+val Kosmos.sceneContainerViewModel by Fixture {
+ SceneContainerViewModel(
+ sceneInteractor = sceneInteractor,
+ falsingInteractor = falsingInteractor,
+ powerInteractor = powerInteractor,
+ motionEventHandlerReceiver = {},
+ logger = sceneLogger
+ )
+ .apply { setTransitionState(transitionState) }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt
index e7162d2..6d30c68 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt
@@ -17,9 +17,9 @@
package com.android.systemui.volume.panel.component.captioning
import com.android.internal.logging.uiEventLogger
+import com.android.systemui.accessibility.domain.interactor.captioningInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
-import com.android.systemui.view.accessibility.data.repository.captioningInteractor
import com.android.systemui.volume.panel.component.button.ui.composable.ToggleButtonComponent
import com.android.systemui.volume.panel.component.captioning.domain.CaptioningAvailabilityCriteria
import com.android.systemui.volume.panel.component.captioning.ui.viewmodel.captioningViewModel
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt
index 0edd9e0..e23a21a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt
@@ -18,9 +18,9 @@
import android.content.applicationContext
import com.android.internal.logging.uiEventLogger
+import com.android.systemui.accessibility.domain.interactor.captioningInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
-import com.android.systemui.view.accessibility.data.repository.captioningInteractor
val Kosmos.captioningViewModel by
Kosmos.Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/telecom/TelecomManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/telecom/TelecomManagerKosmos.kt
index 4e0c0883..d1bee61 100644
--- a/packages/SystemUI/tests/utils/src/com/android/telecom/TelecomManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/telecom/TelecomManagerKosmos.kt
@@ -21,4 +21,5 @@
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.util.mockito.mock
-var Kosmos.telecomManager by Fixture<TelecomManager?> { mock() }
+val Kosmos.mockTelecomManager by Fixture<TelecomManager> { mock() }
+var Kosmos.telecomManager by Fixture<TelecomManager?> { mockTelecomManager }
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
index a2d467c..02800cb 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
@@ -21,9 +21,7 @@
import com.android.server.SystemService;
-/**
- * Service that manages app functions.
- */
+/** Service that manages app functions. */
public class AppFunctionManagerService extends SystemService {
private final AppFunctionManagerServiceImpl mServiceImpl;
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 32b8d6b..f04bd9f 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -39,9 +39,7 @@
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-/**
- * Implementation of the AppFunctionManagerService.
- */
+/** Implementation of the AppFunctionManagerService. */
public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
private static final String TAG = AppFunctionManagerServiceImpl.class.getSimpleName();
@@ -50,30 +48,31 @@
private final ServiceHelper mInternalServiceHelper;
private final ServiceConfig mServiceConfig;
-
public AppFunctionManagerServiceImpl(@NonNull Context context) {
- this(new RemoteServiceCallerImpl<>(
+ this(
+ new RemoteServiceCallerImpl<>(
context,
- IAppFunctionService.Stub::asInterface, new ThreadPoolExecutor(
- /*corePoolSize=*/ Runtime.getRuntime().availableProcessors(),
- /*maxConcurrency=*/ Runtime.getRuntime().availableProcessors(),
- /*keepAliveTime=*/ 0L,
- /*unit=*/ TimeUnit.SECONDS,
- /*workQueue=*/ new LinkedBlockingQueue<>())),
+ IAppFunctionService.Stub::asInterface,
+ new ThreadPoolExecutor(
+ /* corePoolSize= */ Runtime.getRuntime().availableProcessors(),
+ /* maxConcurrency= */ Runtime.getRuntime().availableProcessors(),
+ /* keepAliveTime= */ 0L,
+ /* unit= */ TimeUnit.SECONDS,
+ /* workQueue= */ new LinkedBlockingQueue<>())),
new CallerValidatorImpl(context),
new ServiceHelperImpl(context),
new ServiceConfigImpl());
}
@VisibleForTesting
- AppFunctionManagerServiceImpl(RemoteServiceCaller<IAppFunctionService> remoteServiceCaller,
- CallerValidator callerValidator,
- ServiceHelper appFunctionInternalServiceHelper,
- ServiceConfig serviceConfig) {
+ AppFunctionManagerServiceImpl(
+ RemoteServiceCaller<IAppFunctionService> remoteServiceCaller,
+ CallerValidator callerValidator,
+ ServiceHelper appFunctionInternalServiceHelper,
+ ServiceConfig serviceConfig) {
mRemoteServiceCaller = Objects.requireNonNull(remoteServiceCaller);
mCallerValidator = Objects.requireNonNull(callerValidator);
- mInternalServiceHelper =
- Objects.requireNonNull(appFunctionInternalServiceHelper);
+ mInternalServiceHelper = Objects.requireNonNull(appFunctionInternalServiceHelper);
mServiceConfig = serviceConfig;
}
@@ -90,65 +89,71 @@
String validatedCallingPackage;
UserHandle targetUser;
try {
- validatedCallingPackage = mCallerValidator
- .validateCallingPackage(requestInternal.getCallingPackage());
- targetUser = mCallerValidator.verifyTargetUserHandle(
- requestInternal.getUserHandle(), validatedCallingPackage);
+ validatedCallingPackage =
+ mCallerValidator.validateCallingPackage(requestInternal.getCallingPackage());
+ targetUser =
+ mCallerValidator.verifyTargetUserHandle(
+ requestInternal.getUserHandle(), validatedCallingPackage);
} catch (SecurityException exception) {
- safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse
- .newFailure(ExecuteAppFunctionResponse.RESULT_DENIED,
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_DENIED,
exception.getMessage(),
- /*extras=*/ null));
+ /* extras= */ null));
return;
}
// TODO(b/354956319): Add and honor the new enterprise policies.
if (mCallerValidator.isUserOrganizationManaged(targetUser)) {
- safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
- "Cannot run on a device with a device owner or from the managed profile.",
- /*extras=*/ null
- ));
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+ "Cannot run on a device with a device owner or from the managed"
+ + " profile.",
+ /* extras= */ null));
return;
}
String targetPackageName = requestInternal.getClientRequest().getTargetPackageName();
if (TextUtils.isEmpty(targetPackageName)) {
- safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT,
- "Target package name cannot be empty.",
- /*extras=*/ null
- ));
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT,
+ "Target package name cannot be empty.",
+ /* extras= */ null));
return;
}
if (!mCallerValidator.verifyCallerCanExecuteAppFunction(
validatedCallingPackage, targetPackageName)) {
- safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse
- .newFailure(ExecuteAppFunctionResponse.RESULT_DENIED,
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_DENIED,
"Caller does not have permission to execute the appfunction",
- /*extras=*/ null));
+ /* extras= */ null));
return;
}
- Intent serviceIntent = mInternalServiceHelper.resolveAppFunctionService(
- targetPackageName,
- targetUser);
+ Intent serviceIntent =
+ mInternalServiceHelper.resolveAppFunctionService(targetPackageName, targetUser);
if (serviceIntent == null) {
- safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
- "Cannot find the target service.",
- /*extras=*/ null
- ));
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+ "Cannot find the target service.",
+ /* extras= */ null));
return;
}
final long token = Binder.clearCallingIdentity();
try {
- bindAppFunctionServiceUnchecked(requestInternal, serviceIntent, targetUser,
- safeExecuteAppFunctionCallback,
- /*bindFlags=*/ Context.BIND_AUTO_CREATE,
- /*timeoutInMillis=*/ mServiceConfig.getExecuteAppFunctionTimeoutMillis());
+ bindAppFunctionServiceUnchecked(
+ requestInternal,
+ serviceIntent,
+ targetUser,
+ safeExecuteAppFunctionCallback,
+ /* bindFlags= */ Context.BIND_AUTO_CREATE,
+ /* timeoutInMillis= */ mServiceConfig.getExecuteAppFunctionTimeoutMillis());
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -156,69 +161,75 @@
private void bindAppFunctionServiceUnchecked(
@NonNull ExecuteAppFunctionAidlRequest requestInternal,
- @NonNull Intent serviceIntent, @NonNull UserHandle targetUser,
- @NonNull SafeOneTimeExecuteAppFunctionCallback
- safeExecuteAppFunctionCallback,
- int bindFlags, long timeoutInMillis) {
- boolean bindServiceResult = mRemoteServiceCaller.runServiceCall(
- serviceIntent,
- bindFlags,
- timeoutInMillis,
- targetUser,
- new RunServiceCallCallback<IAppFunctionService>() {
- @Override
- public void onServiceConnected(@NonNull IAppFunctionService service,
- @NonNull ServiceUsageCompleteListener
- serviceUsageCompleteListener) {
- try {
- service.executeAppFunction(
- requestInternal.getClientRequest(),
- new IExecuteAppFunctionCallback.Stub() {
- @Override
- public void onResult(ExecuteAppFunctionResponse response) {
- safeExecuteAppFunctionCallback.onResult(response);
- serviceUsageCompleteListener.onCompleted();
- }
- }
- );
- } catch (Exception e) {
- safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse
- .newFailure(ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR,
- e.getMessage(),
- /*extras=*/ null));
- serviceUsageCompleteListener.onCompleted();
- }
- }
+ @NonNull Intent serviceIntent,
+ @NonNull UserHandle targetUser,
+ @NonNull SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback,
+ int bindFlags,
+ long timeoutInMillis) {
+ boolean bindServiceResult =
+ mRemoteServiceCaller.runServiceCall(
+ serviceIntent,
+ bindFlags,
+ timeoutInMillis,
+ targetUser,
+ new RunServiceCallCallback<IAppFunctionService>() {
+ @Override
+ public void onServiceConnected(
+ @NonNull IAppFunctionService service,
+ @NonNull
+ ServiceUsageCompleteListener
+ serviceUsageCompleteListener) {
+ try {
+ service.executeAppFunction(
+ requestInternal.getClientRequest(),
+ new IExecuteAppFunctionCallback.Stub() {
+ @Override
+ public void onResult(
+ ExecuteAppFunctionResponse response) {
+ safeExecuteAppFunctionCallback.onResult(
+ response);
+ serviceUsageCompleteListener.onCompleted();
+ }
+ });
+ } catch (Exception e) {
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse
+ .RESULT_APP_UNKNOWN_ERROR,
+ e.getMessage(),
+ /* extras= */ null));
+ serviceUsageCompleteListener.onCompleted();
+ }
+ }
- @Override
- public void onFailedToConnect() {
- Slog.e(TAG, "Failed to connect to service");
- safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse
- .newFailure(ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR,
- "Failed to connect to AppFunctionService",
- /*extras=*/ null));
- }
+ @Override
+ public void onFailedToConnect() {
+ Slog.e(TAG, "Failed to connect to service");
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR,
+ "Failed to connect to AppFunctionService",
+ /* extras= */ null));
+ }
- @Override
- public void onTimedOut() {
- Slog.e(TAG, "Timed out");
- safeExecuteAppFunctionCallback.onResult(
- ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_TIMED_OUT,
- "Binding to AppFunctionService timed out.",
- /*extras=*/ null
- ));
- }
- }
- );
+ @Override
+ public void onTimedOut() {
+ Slog.e(TAG, "Timed out");
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_TIMED_OUT,
+ "Binding to AppFunctionService timed out.",
+ /* extras= */ null));
+ }
+ });
if (!bindServiceResult) {
Slog.e(TAG, "Failed to bind to the AppFunctionService");
- safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_TIMED_OUT,
- "Failed to bind the AppFunctionService.",
- /*extras=*/ null
- ));
+ safeExecuteAppFunctionCallback.onResult(
+ ExecuteAppFunctionResponse.newFailure(
+ ExecuteAppFunctionResponse.RESULT_TIMED_OUT,
+ "Failed to bind the AppFunctionService.",
+ /* extras= */ null));
}
}
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
index 9bd633f..ca43dfa 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
@@ -22,7 +22,6 @@
import com.android.internal.annotations.VisibleForTesting;
-
/**
* Interface for validating that the caller has the correct privilege to call an AppFunctionManager
* API.
@@ -33,8 +32,8 @@
// TODO(b/357551503): Verify that user have been unlocked.
/**
- * This method is used to validate that the calling package reported in the request is the
- * same as the binder calling identity.
+ * This method is used to validate that the calling package reported in the request is the same
+ * as the binder calling identity.
*
* @param claimedCallingPackage The package name of the caller.
* @return The package name of the caller.
@@ -43,29 +42,29 @@
String validateCallingPackage(@NonNull String claimedCallingPackage);
/**
- * Validates that the caller can invoke an AppFunctionManager API in the provided
- * target user space.
+ * Validates that the caller can invoke an AppFunctionManager API in the provided target user
+ * space.
*
- * @param targetUserHandle The user which the caller is requesting to execute as.
+ * @param targetUserHandle The user which the caller is requesting to execute as.
* @param claimedCallingPackage The package name of the caller.
* @return The user handle that the call should run as. Will always be a concrete user.
* @throws IllegalArgumentException if the target user is a special user.
- * @throws SecurityException if caller trying to interact across users without {@link
- * Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * @throws SecurityException if caller trying to interact across users without {@link
+ * Manifest.permission#INTERACT_ACROSS_USERS_FULL}
*/
- UserHandle verifyTargetUserHandle(@NonNull UserHandle targetUserHandle,
- @NonNull String claimedCallingPackage);
+ UserHandle verifyTargetUserHandle(
+ @NonNull UserHandle targetUserHandle, @NonNull String claimedCallingPackage);
/**
* Validates that the caller can execute the specified app function.
- * <p>
- * The caller can execute if the app function's package name is the same as the caller's package
- * or the caller has either {@link Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or
- * {@link Manifest.permission.EXECUTE_APP_FUNCTIONS} granted. In some cases, app functions
- * can still opt-out of caller having {@link Manifest.permission.EXECUTE_APP_FUNCTIONS}.
*
- * @param callerPackageName The calling package (as previously validated).
- * @param targetPackageName The package that owns the app function to execute.
+ * <p>The caller can execute if the app function's package name is the same as the caller's
+ * package or the caller has either {@link Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or
+ * {@link Manifest.permission.EXECUTE_APP_FUNCTIONS} granted. In some cases, app functions can
+ * still opt-out of caller having {@link Manifest.permission.EXECUTE_APP_FUNCTIONS}.
+ *
+ * @param callerPackageName The calling package (as previously validated).
+ * @param targetPackageName The package that owns the app function to execute.
* @return Whether the caller can execute the specified app function.
*/
boolean verifyCallerCanExecuteAppFunction(
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
index 7cd660d..35905ed 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
@@ -34,7 +34,6 @@
class CallerValidatorImpl implements CallerValidator {
private final Context mContext;
-
CallerValidatorImpl(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
}
@@ -56,14 +55,14 @@
@Override
@NonNull
@BinderThread
- public UserHandle verifyTargetUserHandle(@NonNull UserHandle targetUserHandle,
- @NonNull String claimedCallingPackage) {
+ public UserHandle verifyTargetUserHandle(
+ @NonNull UserHandle targetUserHandle, @NonNull String claimedCallingPackage) {
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
final long callingIdentityToken = Binder.clearCallingIdentity();
try {
- return handleIncomingUser(claimedCallingPackage, targetUserHandle,
- callingPid, callingUid);
+ return handleIncomingUser(
+ claimedCallingPackage, targetUserHandle, callingPid, callingUid);
} finally {
Binder.restoreCallingIdentity(callingIdentityToken);
}
@@ -71,19 +70,24 @@
@Override
@BinderThread
- @RequiresPermission(anyOf = {Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
- Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional = true)
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+ Manifest.permission.EXECUTE_APP_FUNCTIONS
+ },
+ conditional = true)
// TODO(b/360864791): Add and honor apps that opt-out from EXECUTE_APP_FUNCTIONS caller.
public boolean verifyCallerCanExecuteAppFunction(
@NonNull String callerPackageName, @NonNull String targetPackageName) {
int pid = Binder.getCallingPid();
int uid = Binder.getCallingUid();
- boolean hasExecutionPermission = mContext.checkPermission(
- Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
- boolean hasTrustedExecutionPermission = mContext.checkPermission(
- Manifest.permission.EXECUTE_APP_FUNCTIONS, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
+ boolean hasExecutionPermission =
+ mContext.checkPermission(
+ Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ boolean hasTrustedExecutionPermission =
+ mContext.checkPermission(Manifest.permission.EXECUTE_APP_FUNCTIONS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
boolean isSamePackage = callerPackageName.equals(targetPackageName);
return hasExecutionPermission || hasTrustedExecutionPermission || isSamePackage;
}
@@ -111,13 +115,13 @@
* simply throw.
*
* @param callingPackageName The package name of the caller.
- * @param targetUserHandle The user which the caller is requesting to execute as.
- * @param callingPid The actual pid of the caller as determined by Binder.
- * @param callingUid The actual uid of the caller as determined by Binder.
+ * @param targetUserHandle The user which the caller is requesting to execute as.
+ * @param callingPid The actual pid of the caller as determined by Binder.
+ * @param callingUid The actual uid of the caller as determined by Binder.
* @return the user handle that the call should run as. Will always be a concrete user.
* @throws IllegalArgumentException if the target user is a special user.
- * @throws SecurityException if caller trying to interact across user without {@link
- * Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * @throws SecurityException if caller trying to interact across user without {@link
+ * Manifest.permission#INTERACT_ACROSS_USERS_FULL}
*/
@NonNull
private UserHandle handleIncomingUser(
@@ -137,7 +141,7 @@
}
if (mContext.checkPermission(
- Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingPid, callingUid)
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED) {
try {
mContext.createPackageContextAsUser(
@@ -168,10 +172,9 @@
private void validateCallingPackageInternal(
int actualCallingUid, @NonNull String claimedCallingPackage) {
UserHandle callingUserHandle = UserHandle.getUserHandleForUid(actualCallingUid);
- Context actualCallingUserContext = mContext.createContextAsUser(
- callingUserHandle, /* flags= */ 0);
- int claimedCallingUid =
- getPackageUid(actualCallingUserContext, claimedCallingPackage);
+ Context actualCallingUserContext =
+ mContext.createContextAsUser(callingUserHandle, /* flags= */ 0);
+ int claimedCallingUid = getPackageUid(actualCallingUserContext, claimedCallingPackage);
if (claimedCallingUid != actualCallingUid) {
throw new SecurityException(
"Specified calling package ["
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
index c19a027..0e18705 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
@@ -30,8 +30,8 @@
import java.util.function.Function;
/**
- * An implementation of {@link RemoteServiceCaller} that that is based on
- * {@link Context#bindService}.
+ * An implementation of {@link RemoteServiceCaller} that that is based on {@link
+ * Context#bindService}.
*
* @param <T> Class of wrapped service.
* @hide
@@ -39,18 +39,16 @@
public class RemoteServiceCallerImpl<T> implements RemoteServiceCaller<T> {
private static final String TAG = "AppFunctionsServiceCall";
- @NonNull
- private final Context mContext;
- @NonNull
- private final Function<IBinder, T> mInterfaceConverter;
+ @NonNull private final Context mContext;
+ @NonNull private final Function<IBinder, T> mInterfaceConverter;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Executor mExecutor;
/**
* @param interfaceConverter A function responsible for converting an IBinder object into the
- * desired service interface.
- * @param executor An Executor instance to dispatch callback.
- * @param context The system context.
+ * desired service interface.
+ * @param executor An Executor instance to dispatch callback.
+ * @param context The system context.
*/
public RemoteServiceCallerImpl(
@NonNull Context context,
diff --git a/services/appfunctions/java/com/android/server/appfunctions/ServiceConfig.java b/services/appfunctions/java/com/android/server/appfunctions/ServiceConfig.java
index 4bc6e70..caa4bf0 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/ServiceConfig.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/ServiceConfig.java
@@ -16,15 +16,11 @@
package com.android.server.appfunctions;
-/**
- * This interface is used to expose configs to the AppFunctionManagerService.
- */
+/** This interface is used to expose configs to the AppFunctionManagerService. */
public interface ServiceConfig {
// TODO(b/357551503): Obtain namespace from DeviceConfig.
String NAMESPACE_APP_FUNCTIONS = "appfunctions";
- /**
- * Returns the maximum time to wait for an app function execution to be complete.
- */
+ /** Returns the maximum time to wait for an app function execution to be complete. */
long getExecuteAppFunctionTimeoutMillis();
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/ServiceConfigImpl.java b/services/appfunctions/java/com/android/server/appfunctions/ServiceConfigImpl.java
index e090317..f18789b 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/ServiceConfigImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/ServiceConfigImpl.java
@@ -18,21 +18,17 @@
import android.provider.DeviceConfig;
-/**
- * Implementation of {@link ServiceConfig}
- */
+/** Implementation of {@link ServiceConfig} */
public class ServiceConfigImpl implements ServiceConfig {
static final String DEVICE_CONFIG_PROPERTY_EXECUTION_TIMEOUT =
"execute_app_function_timeout_millis";
static final long DEFAULT_EXECUTE_APP_FUNCTION_TIMEOUT_MS = 5000L;
-
@Override
public long getExecuteAppFunctionTimeoutMillis() {
return DeviceConfig.getLong(
NAMESPACE_APP_FUNCTIONS,
DEVICE_CONFIG_PROPERTY_EXECUTION_TIMEOUT,
- DEFAULT_EXECUTE_APP_FUNCTION_TIMEOUT_MS
- );
+ DEFAULT_EXECUTE_APP_FUNCTION_TIMEOUT_MS);
}
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/ServiceHelper.java b/services/appfunctions/java/com/android/server/appfunctions/ServiceHelper.java
index 6cd87d3..bc7bd2b 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/ServiceHelper.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/ServiceHelper.java
@@ -22,18 +22,16 @@
import com.android.internal.annotations.VisibleForTesting;
-/**
- * Helper interface for AppFunctionService.
- */
+/** Helper interface for AppFunctionService. */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public interface ServiceHelper {
/**
* Resolves the AppFunctionService for the target package.
*
* @param targetPackageName The package name of the target.
- * @param targetUser The user which the caller is requesting to execute as.
+ * @param targetUser The user which the caller is requesting to execute as.
* @return The intent to bind to the target service.
*/
- Intent resolveAppFunctionService(@NonNull String targetPackageName,
- @NonNull UserHandle targetUser);
+ Intent resolveAppFunctionService(
+ @NonNull String targetPackageName, @NonNull UserHandle targetUser);
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java b/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java
index e49fba5..37a3779 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java
@@ -38,23 +38,23 @@
}
@Override
- public Intent resolveAppFunctionService(@NonNull String targetPackageName,
- @NonNull UserHandle targetUser) {
+ public Intent resolveAppFunctionService(
+ @NonNull String targetPackageName, @NonNull UserHandle targetUser) {
Intent serviceIntent = new Intent(AppFunctionService.SERVICE_INTERFACE);
serviceIntent.setPackage(targetPackageName);
- ResolveInfo resolveInfo = mContext.createContextAsUser(targetUser, /* flags= */ 0)
- .getPackageManager().resolveService(serviceIntent, 0);
+ ResolveInfo resolveInfo =
+ mContext.createContextAsUser(targetUser, /* flags= */ 0)
+ .getPackageManager()
+ .resolveService(serviceIntent, 0);
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
return null;
}
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
- if (!Manifest.permission.BIND_APP_FUNCTION_SERVICE.equals(
- serviceInfo.permission)) {
+ if (!Manifest.permission.BIND_APP_FUNCTION_SERVICE.equals(serviceInfo.permission)) {
return null;
}
- serviceIntent.setComponent(
- new ComponentName(serviceInfo.packageName, serviceInfo.name));
+ serviceIntent.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name));
return serviceIntent;
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java b/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
index 5dd4c25..e56f81f0 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
@@ -20,7 +20,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
-import android.annotation.WorkerThread;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchManager.SearchContext;
import android.app.appsearch.AppSearchResult;
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index de94715..3068398 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -16,6 +16,7 @@
package com.android.server.appwidget;
+import static android.appwidget.flags.Flags.remoteAdapterConversion;
import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
import static android.appwidget.flags.Flags.supportResumeRestoreAfterReboot;
import static android.content.Context.KEYGUARD_SERVICE;
@@ -1650,6 +1651,9 @@
public boolean bindRemoteViewsService(String callingPackage, int appWidgetId, Intent intent,
IApplicationThread caller, IBinder activtiyToken, IServiceConnection connection,
long flags) {
+ if (remoteAdapterConversion()) {
+ throw new UnsupportedOperationException("bindRemoteViewsService is deprecated");
+ }
final int userId = UserHandle.getCallingUserId();
if (DEBUG) {
Slog.i(TAG, "bindRemoteViewsService() " + userId);
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index dd4239c..556fae3 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -201,6 +201,15 @@
"include-filter": "com.android.server.wm.BackgroundActivityStart*"
}
]
+ },
+ {
+ "name": "CtsOsTestCases",
+ "file_patterns": ["StorageManagerService\\.java"],
+ "options": [
+ {
+ "include-filter": "android.os.storage.cts.StorageManagerTest"
+ }
+ ]
}
]
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 79e09d7..7442277 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1732,41 +1732,47 @@
// In the future, we can remove this logic for every notification here and add a
// callback so listeners know when their PhoneStateListener's subId becomes invalid,
// but for now we use the simplest fix.
- if (validatePhoneId(phoneId) && SubscriptionManager.isValidSubscriptionId(subId)) {
+ if (validatePhoneId(phoneId)) {
mServiceState[phoneId] = state;
- for (Record r : mRecords) {
- if (VDBG) {
- log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
- + " phoneId=" + phoneId + " state=" + state);
- }
- if (r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
- && idMatch(r, subId, phoneId)) {
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
- try {
- ServiceState stateToSend;
- if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
- stateToSend = new ServiceState(state);
- } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
- stateToSend = state.createLocationInfoSanitizedCopy(false);
- } else {
- stateToSend = state.createLocationInfoSanitizedCopy(true);
+ for (Record r : mRecords) {
+ if (VDBG) {
+ log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
+ + " phoneId=" + phoneId + " state=" + state);
+ }
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+
+ try {
+ ServiceState stateToSend;
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ stateToSend = new ServiceState(state);
+ } else if(checkCoarseLocationAccess(
+ r, Build.VERSION_CODES.Q)) {
+ stateToSend = state.createLocationInfoSanitizedCopy(false);
+ } else {
+ stateToSend = state.createLocationInfoSanitizedCopy(true);
+ }
+ if (DBG) {
+ log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
+ + " subId=" + subId + " phoneId=" + phoneId
+ + " state=" + stateToSend);
+ }
+ r.callback.onServiceStateChanged(stateToSend);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
}
- if (DBG) {
- log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
- + " subId=" + subId + " phoneId=" + phoneId
- + " state=" + stateToSend);
- }
- r.callback.onServiceStateChanged(stateToSend);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
}
}
}
+ else {
+ log("notifyServiceStateForSubscriber: INVALID subId=" +subId);
+ }
} else {
- log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId
- + " or subId=" + subId);
+ log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId);
}
handleRemoveListLocked();
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 2af5316..765afef 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1773,7 +1773,8 @@
// Create a Session for the target user and pass in the bundle
completeCloningAccount(response, result, account, toAccounts, userFrom);
} else {
- super.onResult(result);
+ // Bundle format is not defined.
+ super.onResultSkipSanitization(result);
}
}
}.bind();
@@ -1860,7 +1861,8 @@
// account to avoid retries?
// TODO: what we do with the visibility?
- super.onResult(result);
+ // Bundle format is not defined.
+ super.onResultSkipSanitization(result);
}
@Override
@@ -2106,6 +2108,7 @@
@Override
public void onResult(Bundle result) {
Bundle.setDefusable(result, true);
+ result = sanitizeBundle(result);
IAccountManagerResponse response = getResponseAndClose();
if (response != null) {
try {
@@ -2456,6 +2459,7 @@
@Override
public void onResult(Bundle result) {
Bundle.setDefusable(result, true);
+ result = sanitizeBundle(result);
if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
&& !result.containsKey(AccountManager.KEY_INTENT)) {
final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
@@ -2970,6 +2974,7 @@
@Override
public void onResult(Bundle result) {
Bundle.setDefusable(result, true);
+ result = sanitizeBundle(result);
if (result != null) {
String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
Bundle bundle = new Bundle();
@@ -3147,6 +3152,7 @@
@Override
public void onResult(Bundle result) {
Bundle.setDefusable(result, true);
+ result = sanitizeBundle(result);
if (result != null) {
if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Intent intent = newGrantCredentialsPermissionIntent(
@@ -3618,6 +3624,12 @@
@Override
public void onResult(Bundle result) {
Bundle.setDefusable(result, true);
+ Bundle sessionBundle = null;
+ if (result != null) {
+ // Session bundle will be removed from result.
+ sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
+ }
+ result = sanitizeBundle(result);
mNumResults++;
Intent intent = null;
if (result != null) {
@@ -3679,7 +3691,6 @@
// bundle contains data necessary for finishing the session
// later. The session bundle will be encrypted here and
// decrypted later when trying to finish the session.
- Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
if (sessionBundle != null) {
String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
if (TextUtils.isEmpty(accountType)
@@ -4067,6 +4078,7 @@
@Override
public void onResult(Bundle result) {
Bundle.setDefusable(result, true);
+ result = sanitizeBundle(result);
IAccountManagerResponse response = getResponseAndClose();
if (response == null) {
return;
@@ -4380,6 +4392,7 @@
@Override
public void onResult(Bundle result) {
Bundle.setDefusable(result, true);
+ result = sanitizeBundle(result);
mNumResults++;
if (result == null) {
onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
@@ -4936,6 +4949,68 @@
callback, resultReceiver);
}
+
+ // All keys for Strings passed from AbstractAccountAuthenticator using Bundle.
+ private static final String[] sStringBundleKeys = new String[] {
+ AccountManager.KEY_ACCOUNT_NAME,
+ AccountManager.KEY_ACCOUNT_TYPE,
+ AccountManager.KEY_AUTHTOKEN,
+ AccountManager.KEY_AUTH_TOKEN_LABEL,
+ AccountManager.KEY_ERROR_MESSAGE,
+ AccountManager.KEY_PASSWORD,
+ AccountManager.KEY_ACCOUNT_STATUS_TOKEN};
+
+ /**
+ * Keep only documented fields in a Bundle received from AbstractAccountAuthenticator.
+ */
+ protected static Bundle sanitizeBundle(Bundle bundle) {
+ if (bundle == null) {
+ return null;
+ }
+ Bundle sanitizedBundle = new Bundle();
+ Bundle.setDefusable(sanitizedBundle, true);
+ int updatedKeysCount = 0;
+ for (String stringKey : sStringBundleKeys) {
+ if (bundle.containsKey(stringKey)) {
+ String value = bundle.getString(stringKey);
+ sanitizedBundle.putString(stringKey, value);
+ updatedKeysCount++;
+ }
+ }
+ String key = AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY;
+ if (bundle.containsKey(key)) {
+ long expiryMillis = bundle.getLong(key, 0L);
+ sanitizedBundle.putLong(key, expiryMillis);
+ updatedKeysCount++;
+ }
+ key = AccountManager.KEY_BOOLEAN_RESULT;
+ if (bundle.containsKey(key)) {
+ boolean booleanResult = bundle.getBoolean(key, false);
+ sanitizedBundle.putBoolean(key, booleanResult);
+ updatedKeysCount++;
+ }
+ key = AccountManager.KEY_ERROR_CODE;
+ if (bundle.containsKey(key)) {
+ int errorCode = bundle.getInt(key, 0);
+ sanitizedBundle.putInt(key, errorCode);
+ updatedKeysCount++;
+ }
+ key = AccountManager.KEY_INTENT;
+ if (bundle.containsKey(key)) {
+ Intent intent = bundle.getParcelable(key, Intent.class);
+ sanitizedBundle.putParcelable(key, intent);
+ updatedKeysCount++;
+ }
+ if (bundle.containsKey(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)) {
+ // The field is not copied in sanitized bundle.
+ updatedKeysCount++;
+ }
+ if (updatedKeysCount != bundle.size()) {
+ Log.w(TAG, "Size mismatch after sanitizeBundle call.");
+ }
+ return sanitizedBundle;
+ }
+
private abstract class Session extends IAccountAuthenticatorResponse.Stub
implements IBinder.DeathRecipient, ServiceConnection {
private final Object mSessionLock = new Object();
@@ -5226,10 +5301,15 @@
}
}
}
-
@Override
public void onResult(Bundle result) {
Bundle.setDefusable(result, true);
+ result = sanitizeBundle(result);
+ onResultSkipSanitization(result);
+ }
+
+ public void onResultSkipSanitization(Bundle result) {
+ Bundle.setDefusable(result, true);
mNumResults++;
Intent intent = null;
if (result != null) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 81c30dd..4b85217 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -116,11 +116,11 @@
private boolean mWasActiveSourceSetToConnectedDevice = false;
@VisibleForTesting
- protected boolean getWasActiveSourceSetToConnectedDevice() {
+ protected boolean getWasActivePathSetToConnectedDevice() {
return mWasActiveSourceSetToConnectedDevice;
}
- protected void setWasActiveSourceSetToConnectedDevice(
+ protected void setWasActivePathSetToConnectedDevice(
boolean wasActiveSourceSetToConnectedDevice) {
mWasActiveSourceSetToConnectedDevice = wasActiveSourceSetToConnectedDevice;
}
@@ -404,6 +404,15 @@
}
}
+ @Override
+ void setActivePath(int path) {
+ super.setActivePath(path);
+ if (path != Constants.INVALID_PHYSICAL_ADDRESS
+ && path != Constants.TV_PHYSICAL_ADDRESS) {
+ setWasActivePathSetToConnectedDevice(true);
+ }
+ }
+
@ServiceThreadOnly
void updateActiveInput(int path, boolean notifyInputChange) {
assertRunOnServiceThread();
@@ -512,7 +521,6 @@
|| info.getDeviceType() == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
mService.getHdmiCecNetwork().updateDevicePowerStatus(logicalAddress,
HdmiControlManager.POWER_STATUS_ON);
- setWasActiveSourceSetToConnectedDevice(true);
ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType());
} else {
@@ -528,16 +536,16 @@
protected int handleStandby(HdmiCecMessage message) {
assertRunOnServiceThread();
- // If a device has previously asserted the active source status, ignore <Standby> from
- // non-active source.
- if (getWasActiveSourceSetToConnectedDevice()
+ // If the TV has previously changed the active path, ignore <Standby> from non-active
+ // source.
+ if (getWasActivePathSetToConnectedDevice()
&& getActiveSource().logicalAddress != message.getSource()) {
Slog.d(TAG, "<Standby> was not sent by the current active source, ignoring."
+ " Current active source has logical address "
+ getActiveSource().logicalAddress);
return Constants.HANDLED;
}
- setWasActiveSourceSetToConnectedDevice(false);
+ setWasActivePathSetToConnectedDevice(false);
return super.handleStandby(message);
}
@@ -1509,7 +1517,7 @@
invokeStandbyCompletedCallback(callback);
return;
}
- setWasActiveSourceSetToConnectedDevice(false);
+ setWasActivePathSetToConnectedDevice(false);
boolean sendStandbyOnSleep =
mService.getHdmiCecConfig().getIntValue(
HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index 0c4fb26..d48957b 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -44,7 +44,8 @@
static final int STATE_WAIT_FOR_ROUTING_INFORMATION = 1;
// Time out in millseconds used for <Routing Information>
- private static final int TIMEOUT_ROUTING_INFORMATION_MS = 1000;
+ @VisibleForTesting
+ static final int TIMEOUT_ROUTING_INFORMATION_MS = 1000;
// If set to true, call {@link HdmiControlService#invokeInputChangeListener()} when
// the routing control/active source change happens. The listener should be called if
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 52bf537..84cee7e 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -26,6 +26,8 @@
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.PermissionManuallyEnforced;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.bluetooth.BluetoothAdapter;
@@ -35,6 +37,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.hardware.SensorPrivacyManager;
@@ -84,7 +87,6 @@
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
@@ -125,7 +127,6 @@
import com.android.server.input.InputManagerInternal.LidSwitchCallback;
import com.android.server.input.debug.FocusEventDebugView;
import com.android.server.input.debug.TouchpadDebugViewController;
-import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
import libcore.io.IoUtils;
@@ -175,7 +176,7 @@
private final InputManagerHandler mHandler;
private DisplayManagerInternal mDisplayManagerInternal;
- private InputMethodManagerInternal mInputMethodManagerInternal;
+ private PackageManagerInternal mPackageManagerInternal;
private final File mDoubleTouchGestureEnableFile;
@@ -546,8 +547,7 @@
}
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
- mInputMethodManagerInternal =
- LocalServices.getService(InputMethodManagerInternal.class);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mSettingsObserver.registerAndUpdate();
@@ -2740,21 +2740,48 @@
lockedModifierState);
}
+ /**
+ * Enforces the caller contains the necessary permission to manage key gestures.
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+ private void enforceManageKeyGesturePermission() {
+ // TODO(b/361567988): Use @EnforcePermission to enforce permission once flag guarding the
+ // permission is rolled out
+ String systemUIPackage = mContext.getString(R.string.config_systemUi);
+ int systemUIAppId = UserHandle.getAppId(mPackageManagerInternal
+ .getPackageUid(systemUIPackage, PackageManager.MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM));
+ if (UserHandle.getCallingAppId() == systemUIAppId) {
+ return;
+ }
+ if (mContext.checkCallingOrSelfPermission(
+ Manifest.permission.MANAGE_KEY_GESTURES) == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+
+ String message = "Managing Key Gestures requires the following permission: "
+ + Manifest.permission.MANAGE_KEY_GESTURES;
+ throw new SecurityException(message);
+ }
+
+
@Override
- @EnforcePermission(Manifest.permission.MANAGE_KEY_GESTURES)
+ @PermissionManuallyEnforced
public void registerKeyGestureEventListener(
@NonNull IKeyGestureEventListener listener) {
- super.registerKeyGestureEventListener_enforcePermission();
+ enforceManageKeyGesturePermission();
+
Objects.requireNonNull(listener);
mKeyGestureController.registerKeyGestureEventListener(listener,
Binder.getCallingPid());
}
@Override
- @EnforcePermission(Manifest.permission.MANAGE_KEY_GESTURES)
+ @PermissionManuallyEnforced
public void unregisterKeyGestureEventListener(
@NonNull IKeyGestureEventListener listener) {
- super.unregisterKeyGestureEventListener_enforcePermission();
+ enforceManageKeyGesturePermission();
+
Objects.requireNonNull(listener);
mKeyGestureController.unregisterKeyGestureEventListener(listener,
Binder.getCallingPid());
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index db4d68b..4fcf27d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -756,15 +756,9 @@
unlockIntent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- PendingIntent intent;
- if (android.app.admin.flags.Flags.hsumUnlockNotificationFix()) {
- intent = PendingIntent.getActivityAsUser(mContext, 0, unlockIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED,
- null, parent);
- } else {
- intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
- }
+ PendingIntent intent = PendingIntent.getActivityAsUser(mContext, 0, unlockIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED,
+ null, parent);
Slogf.d(TAG, "Showing encryption notification for user %d; reason: %s",
user.getIdentifier(), reason);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 48b60b2..0cc50e6 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -954,7 +954,7 @@
|| isPackageOrComponentAllowed(component.getPackageName(), userId))) {
return false;
}
- return isValidService(component, userId);
+ return componentHasBindPermission(component, userId);
}
private boolean componentHasBindPermission(ComponentName component, int userId) {
@@ -1306,12 +1306,11 @@
if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) {
final ComponentName component = ComponentName.unflattenFromString(
approvedPackageOrComponent);
- if (component != null && !isValidService(component, userId)) {
+ if (component != null && !componentHasBindPermission(component, userId)) {
approved.removeAt(j);
if (DEBUG) {
Slog.v(TAG, "Removing " + approvedPackageOrComponent
- + " from approved list; no bind permission or "
- + "service interface filter found "
+ + " from approved list; no bind permission found "
+ mConfig.bindPermission);
}
}
@@ -1330,11 +1329,6 @@
}
}
- protected boolean isValidService(ComponentName component, int userId) {
- return componentHasBindPermission(component, userId) && queryPackageForServices(
- component.getPackageName(), userId).contains(component);
- }
-
protected boolean isValidEntry(String packageOrComponent, int userId) {
return hasMatchingServices(packageOrComponent, userId);
}
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index e34bdc6..9ecc7b9 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -19,6 +19,7 @@
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
import static android.os.Trace.TRACE_TAG_DALVIK;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
@@ -739,6 +740,78 @@
}
/**
+ * Perform dexopt if needed for the installation
+ */
+ static void performDexoptIfNeeded(InstallRequest installRequest, DexManager dexManager,
+ Context context, PackageManagerTracedLock.RawLock installLock) {
+
+ // Construct the DexoptOptions early to see if we should skip running dexopt.
+ //
+ // Do not run PackageDexOptimizer through the local performDexOpt
+ // method because `pkg` may not be in `mPackages` yet.
+ //
+ // Also, don't fail application installs if the dexopt step fails.
+ DexoptOptions dexoptOptions = getDexoptOptionsByInstallRequest(installRequest, dexManager);
+ // Check whether we need to dexopt the app.
+ //
+ // NOTE: it is IMPORTANT to call dexopt:
+ // - after doRename which will sync the package data from AndroidPackage and
+ // its corresponding ApplicationInfo.
+ // - after installNewPackageLIF or replacePackageLIF which will update result with the
+ // uid of the application (pkg.applicationInfo.uid).
+ // This update happens in place!
+ //
+ // We only need to dexopt if the package meets ALL of the following conditions:
+ // 1) it is not an instant app or if it is then dexopt is enabled via gservices.
+ // 2) it is not debuggable.
+ // 3) it is not on Incremental File System.
+ //
+ // Note that we do not dexopt instant apps by default. dexopt can take some time to
+ // complete, so we skip this step during installation. Instead, we'll take extra time
+ // the first time the instant app starts. It's preferred to do it this way to provide
+ // continuous progress to the useur instead of mysteriously blocking somewhere in the
+ // middle of running an instant app. The default behaviour can be overridden
+ // via gservices.
+ //
+ // Furthermore, dexopt may be skipped, depending on the install scenario and current
+ // state of the device.
+ //
+ // TODO(b/174695087): instantApp and onIncremental should be removed and their install
+ // path moved to SCENARIO_FAST.
+
+ final boolean performDexopt = DexOptHelper.shouldPerformDexopt(installRequest,
+ dexoptOptions, context);
+ if (performDexopt) {
+ // dexopt can take long, and ArtService doesn't require installd, so we release
+ // the lock here and re-acquire the lock after dexopt is finished.
+ if (installLock != null) {
+ installLock.unlock();
+ }
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+
+ // This mirrors logic from commitReconciledScanResultLocked, where the library
+ // files needed for dexopt are assigned.
+ PackageSetting realPkgSetting = installRequest.getRealPackageSetting();
+ // Unfortunately, the updated system app flag is only tracked on this
+ // PackageSetting
+ boolean isUpdatedSystemApp =
+ installRequest.getScannedPackageSetting().isUpdatedSystemApp();
+ realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
+
+ DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
+ installRequest, dexoptOptions);
+ installRequest.onDexoptFinished(dexOptResult);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (installLock != null) {
+ installLock.lock();
+ }
+ }
+ }
+ }
+
+ /**
* Use ArtService to perform dexopt by the given InstallRequest.
*/
static DexoptResult dexoptPackageUsingArtService(InstallRequest installRequest,
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 662c792..43a285c 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -172,11 +172,9 @@
import com.android.internal.util.CollectionUtils;
import com.android.server.EventLogTags;
import com.android.server.SystemConfig;
-import com.android.server.art.model.DexoptResult;
import com.android.server.criticalevents.CriticalEventLog;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexManager;
-import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.permission.Permission;
@@ -2633,70 +2631,8 @@
pkg.getBaseApkPath(), pkg.getSplitCodePaths());
}
- // Construct the DexoptOptions early to see if we should skip running dexopt.
- //
- // Do not run PackageDexOptimizer through the local performDexOpt
- // method because `pkg` may not be in `mPackages` yet.
- //
- // Also, don't fail application installs if the dexopt step fails.
- DexoptOptions dexoptOptions = DexOptHelper.getDexoptOptionsByInstallRequest(
- installRequest, mDexManager);
- // Check whether we need to dexopt the app.
- //
- // NOTE: it is IMPORTANT to call dexopt:
- // - after doRename which will sync the package data from AndroidPackage and
- // its corresponding ApplicationInfo.
- // - after installNewPackageLIF or replacePackageLIF which will update result with the
- // uid of the application (pkg.applicationInfo.uid).
- // This update happens in place!
- //
- // We only need to dexopt if the package meets ALL of the following conditions:
- // 1) it is not an instant app or if it is then dexopt is enabled via gservices.
- // 2) it is not debuggable.
- // 3) it is not on Incremental File System.
- //
- // Note that we do not dexopt instant apps by default. dexopt can take some time to
- // complete, so we skip this step during installation. Instead, we'll take extra time
- // the first time the instant app starts. It's preferred to do it this way to provide
- // continuous progress to the useur instead of mysteriously blocking somewhere in the
- // middle of running an instant app. The default behaviour can be overridden
- // via gservices.
- //
- // Furthermore, dexopt may be skipped, depending on the install scenario and current
- // state of the device.
- //
- // TODO(b/174695087): instantApp and onIncremental should be removed and their install
- // path moved to SCENARIO_FAST.
-
- final boolean performDexopt = DexOptHelper.shouldPerformDexopt(installRequest,
- dexoptOptions, mContext);
- if (performDexopt) {
- // dexopt can take long, and ArtService doesn't require installd, so we release
- // the lock here and re-acquire the lock after dexopt is finished.
- PackageManagerTracedLock.RawLock installLock = mPm.mInstallLock.getRawLock();
- installLock.unlock();
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-
- // This mirrors logic from commitReconciledScanResultLocked, where the library
- // files needed for dexopt are assigned.
- PackageSetting realPkgSetting = installRequest.getRealPackageSetting();
-
- // Unfortunately, the updated system app flag is only tracked on this
- // PackageSetting
- boolean isUpdatedSystemApp =
- installRequest.getScannedPackageSetting().isUpdatedSystemApp();
-
- realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
-
- DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
- installRequest, dexoptOptions);
- installRequest.onDexoptFinished(dexOptResult);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- } finally {
- installLock.lock();
- }
- }
+ DexOptHelper.performDexoptIfNeeded(installRequest, mDexManager, mContext,
+ mPm.mInstallLock.getRawLock());
}
PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
incrementalStorages);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 13901c1..a683a8c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1194,11 +1194,11 @@
// Avoid marking pre-created users for removal.
return;
}
- if (ui.lastLoggedInTime == 0) {
- // Avoid marking a not-yet-logged-in ephemeral user for removal, since it doesn't have
- // any personal data in it yet due to not being logged in.
- // This will also avoid marking an auto-created not-yet-logged-in ephemeral guest user
- // for removal, which would be recreated again later in the boot anyway.
+ if (ui.lastLoggedInTime == 0 && ui.isGuest() && Resources.getSystem().getBoolean(
+ com.android.internal.R.bool.config_guestUserAutoCreated)) {
+ // Avoid marking auto-created but not-yet-logged-in guest user for removal. Because a
+ // new one will be created anyway, and this one doesn't have any personal data in it yet
+ // due to not being logged in.
return;
}
// Mark the user for removal.
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index c1e859d..e27b2be 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -864,9 +864,8 @@
if (transition != null) {
if (changed) {
// Always set as scene transition because it expects to be a jump-cut.
- transition.setOverrideAnimation(
- TransitionInfo.AnimationOptions.makeSceneTransitionAnimOptions(), r,
- null, null);
+ transition.setOverrideAnimation(TransitionInfo.AnimationOptions
+ .makeSceneTransitionAnimOptions(), null, null);
r.mTransitionController.requestStartTransition(transition,
null /*startTask */, null /* remoteTransition */,
null /* displayChange */);
@@ -911,9 +910,8 @@
&& under.returningOptions.getAnimationType()
== ANIM_SCENE_TRANSITION) {
// Pass along the scene-transition animation-type
- transition.setOverrideAnimation(TransitionInfo
- .AnimationOptions.makeSceneTransitionAnimOptions(), r,
- null, null);
+ transition.setOverrideAnimation(TransitionInfo.AnimationOptions
+ .makeSceneTransitionAnimOptions(), null, null);
}
} else {
transition.abort();
@@ -1510,7 +1508,7 @@
r.mOverrideTaskTransition);
r.mTransitionController.setOverrideAnimation(
TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName,
- enterAnim, exitAnim, backgroundColor, r.mOverrideTaskTransition), r,
+ enterAnim, exitAnim, backgroundColor, r.mOverrideTaskTransition),
null /* startCallback */, null /* finishCallback */);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e562ea8..99747e0 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -353,7 +353,6 @@
import android.window.SplashScreenView;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.TaskSnapshot;
-import android.window.TransitionInfo;
import android.window.TransitionInfo.AnimationOptions;
import android.window.WindowContainerToken;
import android.window.WindowOnBackInvokedDispatcher;
@@ -5049,8 +5048,7 @@
// controller but don't clear the animation information from the options since they
// need to be sent to the animating activity.
mTransitionController.setOverrideAnimation(
- TransitionInfo.AnimationOptions.makeSceneTransitionAnimOptions(), this,
- null, null);
+ AnimationOptions.makeSceneTransitionAnimOptions(), null, null);
return;
}
applyOptionsAnimation(mPendingOptions, intent);
@@ -5173,8 +5171,7 @@
}
if (options != null) {
- mTransitionController.setOverrideAnimation(options, this, startCallback,
- finishCallback);
+ mTransitionController.setOverrideAnimation(options, startCallback, finishCallback);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 97d35b9..e8a3951 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3225,7 +3225,8 @@
}
Slog.i(TAG_WM, "Using new display size: " + width + "x" + height);
- updateBaseDisplayMetrics(width, height, mBaseDisplayDensity,
+ updateBaseDisplayMetrics(width, height,
+ mIsDensityForced ? mBaseDisplayDensity : mInitialDisplayDensity,
xDPI != INVALID_DPI ? xDPI : mBaseDisplayPhysicalXDpi,
yDPI != INVALID_DPI ? yDPI : mBaseDisplayPhysicalYDpi);
reconfigureDisplayLocked();
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 6b916ef..43c3d05 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -104,7 +104,7 @@
mGivenInsetsReady = true;
ImeTracker.forLogging().onProgress(mStatsToken,
ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED);
- mStateController.notifyControlChanged(mControlTarget);
+ mStateController.notifyControlChanged(mControlTarget, this);
setImeShowing(true);
} else if (wasServerVisible && mServerVisible && mGivenInsetsReady
&& givenInsetsPending) {
@@ -132,15 +132,15 @@
}
@Override
- protected boolean isLeashReadyForDispatching() {
+ protected boolean isLeashReadyForDispatching(InsetsControlTarget target) {
if (android.view.inputmethod.Flags.refactorInsetsController()) {
final WindowState ws =
mWindowContainer != null ? mWindowContainer.asWindowState() : null;
final boolean isDrawn = ws != null && ws.isDrawn();
- return super.isLeashReadyForDispatching() && mServerVisible && isDrawn
- && mGivenInsetsReady;
+ return super.isLeashReadyForDispatching(target)
+ && mServerVisible && isDrawn && mGivenInsetsReady;
} else {
- return super.isLeashReadyForDispatching();
+ return super.isLeashReadyForDispatching(target);
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index b66b8bc..8f90b2d 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -411,7 +411,7 @@
changed = true;
}
if (changed) {
- mStateController.notifyControlChanged(mControlTarget);
+ mStateController.notifyControlChanged(mControlTarget, this);
}
}
@@ -556,11 +556,37 @@
}
mControl = new InsetsSourceControl(mSource.getId(), mSource.getType(), leash,
initiallyVisible, surfacePosition, getInsetsHint());
+ mStateController.notifySurfaceTransactionReady(this, getSurfaceTransactionId(leash), true);
ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
"InsetsSource Control %s for target %s", mControl, mControlTarget);
}
+ private long getSurfaceTransactionId(SurfaceControl leash) {
+ // Here returns mNativeObject (long) as the ID instead of the leash itself so that
+ // InsetsStateController won't keep referencing the leash unexpectedly.
+ return leash != null ? leash.mNativeObject : 0;
+ }
+
+ /**
+ * This is called when the surface transaction of the leash initialization has been committed.
+ *
+ * @param id Indicates which transaction is committed so that stale callbacks can be dropped.
+ */
+ void onSurfaceTransactionCommitted(long id) {
+ if (mIsLeashReadyForDispatching) {
+ return;
+ }
+ if (mControl == null) {
+ return;
+ }
+ if (id != getSurfaceTransactionId(mControl.getLeash())) {
+ return;
+ }
+ mIsLeashReadyForDispatching = true;
+ mStateController.notifySurfaceTransactionReady(this, 0, false);
+ }
+
void startSeamlessRotation() {
if (!mSeamlessRotating) {
mSeamlessRotating = true;
@@ -582,10 +608,6 @@
return true;
}
- void onSurfaceTransactionApplied() {
- mIsLeashReadyForDispatching = true;
- }
-
void setClientVisible(boolean clientVisible) {
if (mClientVisible == clientVisible) {
return;
@@ -612,8 +634,9 @@
mServerVisible, mClientVisible);
}
- protected boolean isLeashReadyForDispatching() {
- return mIsLeashReadyForDispatching;
+ protected boolean isLeashReadyForDispatching(InsetsControlTarget target) {
+ // If the target is not the control target, we are ready for dispatching a null-leash to it.
+ return target != mControlTarget || mIsLeashReadyForDispatching;
}
/**
@@ -626,7 +649,7 @@
@Nullable
InsetsSourceControl getControl(InsetsControlTarget target) {
if (target == mControlTarget) {
- if (!isLeashReadyForDispatching() && mControl != null) {
+ if (!isLeashReadyForDispatching(target) && mControl != null) {
// The surface transaction of preparing leash is not applied yet. We don't send it
// to the client in case that the client applies its transaction sooner than ours
// that we could unexpectedly overwrite the surface state.
@@ -799,6 +822,7 @@
public void onAnimationCancelled(SurfaceControl animationLeash) {
if (mAdapter == this) {
mStateController.notifyControlRevoked(mControlTarget, InsetsSourceProvider.this);
+ mStateController.notifySurfaceTransactionReady(InsetsSourceProvider.this, 0, false);
mControl = null;
mControlTarget = null;
mAdapter = null;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 098a691..0daddc0 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -34,6 +34,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
+import android.util.SparseLongArray;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
@@ -59,13 +60,14 @@
private final DisplayContent mDisplayContent;
private final SparseArray<InsetsSourceProvider> mProviders = new SparseArray<>();
+ private final SparseLongArray mSurfaceTransactionIds = new SparseLongArray();
private final ArrayMap<InsetsControlTarget, ArrayList<InsetsSourceProvider>>
mControlTargetProvidersMap = new ArrayMap<>();
+ private final ArrayMap<InsetsControlTarget, ArrayList<InsetsSourceProvider>>
+ mPendingTargetProvidersMap = new ArrayMap<>();
private final SparseArray<InsetsControlTarget> mIdControlTargetMap = new SparseArray<>();
private final SparseArray<InsetsControlTarget> mIdFakeControlTargetMap = new SparseArray<>();
- private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>();
-
private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
if (w.isReadyToDispatchInsetsState()) {
w.notifyInsetsChanged();
@@ -327,11 +329,11 @@
}
if (lastTarget != null) {
removeFromControlMaps(lastTarget, provider, fake);
- mPendingControlChanged.add(lastTarget);
+ addToPendingControlMaps(lastTarget, provider);
}
if (target != null) {
addToControlMaps(target, provider, fake);
- mPendingControlChanged.add(target);
+ addToPendingControlMaps(target, provider);
}
}
@@ -364,8 +366,15 @@
}
}
- void notifyControlChanged(InsetsControlTarget target) {
- mPendingControlChanged.add(target);
+ private void addToPendingControlMaps(@NonNull InsetsControlTarget target,
+ InsetsSourceProvider provider) {
+ final ArrayList<InsetsSourceProvider> array =
+ mPendingTargetProvidersMap.computeIfAbsent(target, key -> new ArrayList<>());
+ array.add(provider);
+ }
+
+ void notifyControlChanged(InsetsControlTarget target, InsetsSourceProvider provider) {
+ addToPendingControlMaps(target, provider);
notifyPendingInsetsControlChanged();
if (android.view.inputmethod.Flags.refactorInsetsController()) {
@@ -376,26 +385,58 @@
}
}
+ void notifySurfaceTransactionReady(InsetsSourceProvider provider, long id, boolean ready) {
+ if (ready) {
+ mSurfaceTransactionIds.put(provider.getSource().getId(), id);
+ } else {
+ mSurfaceTransactionIds.delete(provider.getSource().getId());
+ }
+ }
+
private void notifyPendingInsetsControlChanged() {
- if (mPendingControlChanged.isEmpty()) {
+ if (mPendingTargetProvidersMap.isEmpty()) {
return;
}
+ final int size = mSurfaceTransactionIds.size();
+ final SparseLongArray surfaceTransactionIds = new SparseLongArray(size);
+ for (int i = 0; i < size; i++) {
+ surfaceTransactionIds.append(
+ mSurfaceTransactionIds.keyAt(i), mSurfaceTransactionIds.valueAt(i));
+ }
mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- final InsetsSourceProvider provider = mProviders.valueAt(i);
- provider.onSurfaceTransactionApplied();
+ for (int i = 0; i < size; i++) {
+ final int sourceId = surfaceTransactionIds.keyAt(i);
+ final InsetsSourceProvider provider = mProviders.get(sourceId);
+ if (provider == null) {
+ continue;
+ }
+ provider.onSurfaceTransactionCommitted(surfaceTransactionIds.valueAt(i));
}
final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>();
int displayId = mDisplayContent.getDisplayId();
- for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
- final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i);
- controlTarget.notifyInsetsControlChanged(displayId);
- if (mControlTargetProvidersMap.containsKey(controlTarget)) {
- // We only collect targets who get controls, not lose controls.
- newControlTargets.add(controlTarget);
+ final ArrayMap<InsetsControlTarget, ArrayList<InsetsSourceProvider>> pendingControlMap =
+ mPendingTargetProvidersMap;
+ for (int i = pendingControlMap.size() - 1; i >= 0; i--) {
+ final InsetsControlTarget target = pendingControlMap.keyAt(i);
+ final ArrayList<InsetsSourceProvider> providers = pendingControlMap.valueAt(i);
+ for (int p = providers.size() - 1; p >= 0; p--) {
+ final InsetsSourceProvider provider = providers.get(p);
+ if (provider.isLeashReadyForDispatching(target)) {
+ // Stop waiting for this provider.
+ providers.remove(p);
+ }
+ }
+ if (providers.isEmpty()) {
+ pendingControlMap.removeAt(i);
+
+ // All controls of this target are ready to be dispatched.
+ target.notifyInsetsControlChanged(displayId);
+ if (mControlTargetProvidersMap.containsKey(target)) {
+ // We only collect targets who get controls, not lose controls.
+ newControlTargets.add(target);
+ }
}
}
- mPendingControlChanged.clear();
// This updates the insets visibilities AFTER sending current insets state and controls
// to the clients, so that the clients can change the current visibilities to the
@@ -424,7 +465,7 @@
* @param target the control target to check.
*/
boolean hasPendingControls(@NonNull InsetsControlTarget target) {
- return mPendingControlChanged.contains(target);
+ return mPendingTargetProvidersMap.containsKey(target);
}
void dump(String prefix, PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 7f6dc84..e6226ab 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -953,13 +953,10 @@
* Set animation options for collecting transition by ActivityRecord.
* @param options AnimationOptions captured from ActivityOptions
*/
- void setOverrideAnimation(@Nullable AnimationOptions options, @NonNull ActivityRecord r,
+ void setOverrideAnimation(@Nullable AnimationOptions options,
@Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback) {
if (!isCollecting()) return;
mOverrideOptions = options;
- if (mOverrideOptions != null) {
- mOverrideOptions.setUserId(r.mUserId);
- }
sendRemoteCallback(mClientAnimationStartCallback);
mClientAnimationStartCallback = startCallback;
mClientAnimationFinishCallback = finishCallback;
@@ -2822,7 +2819,7 @@
}
}
final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName(
- "Transition Root: " + leashReference.getName())
+ "Transition Root: " + leashReference.getName())
.setCallsite("Transition.calculateTransitionRoots").build();
rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots");
// Update layers to start transaction because we prevent assignment during collect, so
@@ -2993,8 +2990,7 @@
// Create the options based on this change's custom animations and layout
// parameters
animOptions = getOptions(activityRecord /* customAnimActivity */,
- activityRecord /* animLpActivity */);
- animOptions.setUserId(activityRecord.mUserId);
+ activityRecord /* animLpActivity */);
if (!change.hasFlags(FLAG_TRANSLUCENT)) {
// If this change is not translucent, its options are going to be
// inherited by the changes below
@@ -3004,7 +3000,6 @@
} else if (activityRecord != null && animOptionsForActivityTransition != null) {
// Use the same options from the top activity for all the activities
animOptions = animOptionsForActivityTransition;
- animOptions.setUserId(activityRecord.mUserId);
} else if (Flags.activityEmbeddingOverlayPresentationFlag()
&& isEmbeddedTaskFragment) {
final TaskFragmentAnimationParams params = taskFragment.getAnimationParams();
@@ -3017,7 +3012,6 @@
params.getOpenAnimationResId(), params.getChangeAnimationResId(),
params.getCloseAnimationResId(), 0 /* backgroundColor */,
false /* overrideTaskTransition */);
- animOptions.setUserId(taskFragment.getTask().mUserId);
}
}
if (animOptions != null) {
@@ -3081,9 +3075,6 @@
animOptions);
animOptions = addCustomActivityTransition(customAnimActivity, false /* open */,
animOptions);
- if (animOptions != null) {
- animOptions.setUserId(customAnimActivity.mUserId);
- }
}
// Layout parameters
@@ -3097,12 +3088,10 @@
// are running an app starting animation, in which case we don't want the app to be
// able to change its animation directly.
if (animOptions != null) {
- animOptions.setUserId(animLpActivity.mUserId);
animOptions.addOptionsFromLayoutParameters(animLp);
} else {
animOptions = TransitionInfo.AnimationOptions
.makeAnimOptionsFromLayoutParameters(animLp);
- animOptions.setUserId(animLpActivity.mUserId);
}
}
return animOptions;
@@ -3128,7 +3117,6 @@
if (animOptions == null) {
animOptions = TransitionInfo.AnimationOptions
.makeCommonAnimOptions(activity.packageName);
- animOptions.setUserId(activity.mUserId);
}
animOptions.addCustomActivityTransition(open, customAnim.mEnterAnim,
customAnim.mExitAnim, customAnim.mBackgroundColor);
@@ -3257,7 +3245,7 @@
// Remote animations always win, but fullscreen windows override non-fullscreen windows.
ActivityRecord result = lookForTopWindowWithFilter(sortedTargets,
w -> w.getRemoteAnimationDefinition() != null
- && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
+ && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
if (result != null) {
return result;
}
@@ -3304,7 +3292,7 @@
private void validateKeyguardOcclusion() {
if ((mFlags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) {
mController.mStateValidators.add(
- mController.mAtm.mWindowManager.mPolicy::applyKeyguardOcclusionChange);
+ mController.mAtm.mWindowManager.mPolicy::applyKeyguardOcclusionChange);
}
}
@@ -3913,9 +3901,9 @@
/** @return true if all tracked subtrees are ready. */
boolean allReady() {
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
- " allReady query: used=%b " + "override=%b defer=%d states=[%s]", mUsed,
- mReadyOverride, mDeferReadyDepth, groupsToString());
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " allReady query: used=%b "
+ + "override=%b defer=%d states=[%s]", mUsed, mReadyOverride, mDeferReadyDepth,
+ groupsToString());
// If the readiness has never been touched, mUsed will be false. We never want to
// consider a transition ready if nothing has been reported on it.
if (!mUsed) return false;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 1d2a605..50fe69c 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -52,8 +52,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.ProtoLog;
import com.android.internal.protolog.ProtoLogGroup;
+import com.android.internal.protolog.ProtoLog;
import com.android.server.FgThread;
import com.android.window.flags.Flags;
@@ -880,10 +880,10 @@
}
/** @see Transition#setOverrideAnimation */
- void setOverrideAnimation(TransitionInfo.AnimationOptions options, ActivityRecord r,
+ void setOverrideAnimation(TransitionInfo.AnimationOptions options,
@Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback) {
if (mCollectingTransition == null) return;
- mCollectingTransition.setOverrideAnimation(options, r, startCallback, finishCallback);
+ mCollectingTransition.setOverrideAnimation(options, startCallback, finishCallback);
}
void setNoAnimation(WindowContainer wc) {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 13334a5..2342de3 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -27,6 +27,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.content.Context;
+import android.os.HandlerExecutor;
import android.os.Trace;
import android.util.Slog;
import android.util.TimeUtils;
@@ -68,6 +69,8 @@
private Choreographer mChoreographer;
+ private final HandlerExecutor mExecutor;
+
/**
* Indicates whether we have an animation frame callback scheduled, which will happen at
* vsync-app and then schedule the animation tick at the right time (vsync-sf).
@@ -79,8 +82,7 @@
* A list of runnable that need to be run after {@link WindowContainer#prepareSurfaces} is
* executed and the corresponding transaction is closed and applied.
*/
- private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
- private boolean mInExecuteAfterPrepareSurfacesRunnables;
+ private ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
private final SurfaceControl.Transaction mTransaction;
@@ -91,6 +93,7 @@
mTransaction = service.mTransactionFactory.get();
service.mAnimationHandler.runWithScissors(
() -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
+ mExecutor = new HandlerExecutor(service.mAnimationHandler);
mAnimationFrameCallback = frameTimeNs -> {
synchronized (mService.mGlobalLock) {
@@ -198,6 +201,19 @@
updateRunningExpensiveAnimationsLegacy();
}
+ final ArrayList<Runnable> afterPrepareSurfacesRunnables = mAfterPrepareSurfacesRunnables;
+ if (!afterPrepareSurfacesRunnables.isEmpty()) {
+ mAfterPrepareSurfacesRunnables = new ArrayList<>();
+ mTransaction.addTransactionCommittedListener(mExecutor, () -> {
+ synchronized (mService.mGlobalLock) {
+ // Traverse in order they were added.
+ for (int i = 0, size = afterPrepareSurfacesRunnables.size(); i < size; i++) {
+ afterPrepareSurfacesRunnables.get(i).run();
+ }
+ afterPrepareSurfacesRunnables.clear();
+ }
+ });
+ }
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "applyTransaction");
mTransaction.apply();
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
@@ -205,7 +221,6 @@
ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
- executeAfterPrepareSurfacesRunnables();
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit"
@@ -287,34 +302,10 @@
/**
* Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and
- * the corresponding transaction is closed and applied.
+ * the corresponding transaction is closed, applied, and committed.
*/
void addAfterPrepareSurfacesRunnable(Runnable r) {
- // If runnables are already being handled in executeAfterPrepareSurfacesRunnable, then just
- // immediately execute the runnable passed in.
- if (mInExecuteAfterPrepareSurfacesRunnables) {
- r.run();
- return;
- }
-
mAfterPrepareSurfacesRunnables.add(r);
scheduleAnimation();
}
-
- void executeAfterPrepareSurfacesRunnables() {
-
- // Don't even think about to start recursing!
- if (mInExecuteAfterPrepareSurfacesRunnables) {
- return;
- }
- mInExecuteAfterPrepareSurfacesRunnables = true;
-
- // Traverse in order they were added.
- final int size = mAfterPrepareSurfacesRunnables.size();
- for (int i = 0; i < size; i++) {
- mAfterPrepareSurfacesRunnables.get(i).run();
- }
- mAfterPrepareSurfacesRunnables.clear();
- mInExecuteAfterPrepareSurfacesRunnables = false;
- }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 470025a..85e24c1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -21867,18 +21867,15 @@
return;
}
- if (Flags.copyAccountWithRetryEnabled()) {
- boolean copySucceeded = false;
- int retryAttemptsLeft = RETRY_COPY_ACCOUNT_ATTEMPTS;
- while (!copySucceeded && (retryAttemptsLeft > 0)) {
- Slogf.i(LOG_TAG, "Copying account. Attempts left : " + retryAttemptsLeft);
- copySucceeded =
- copyAccount(targetUser, sourceUser, accountToMigrate, callerPackage);
- retryAttemptsLeft--;
- }
- } else {
- copyAccount(targetUser, sourceUser, accountToMigrate, callerPackage);
+ boolean copySucceeded = false;
+ int retryAttemptsLeft = RETRY_COPY_ACCOUNT_ATTEMPTS;
+ while (!copySucceeded && (retryAttemptsLeft > 0)) {
+ Slogf.i(LOG_TAG, "Copying account. Attempts left : " + retryAttemptsLeft);
+ copySucceeded =
+ copyAccount(targetUser, sourceUser, accountToMigrate, callerPackage);
+ retryAttemptsLeft--;
}
+
if (!keepAccountMigrated) {
removeAccount(accountToMigrate, sourceUserId);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index 52a7845..87fd002 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -361,9 +361,7 @@
@Override
boolean shouldWrite() {
- return Flags.alwaysPersistDo()
- || (mDeviceOwner != null) || (mSystemUpdatePolicy != null)
- || (mSystemUpdateInfo != null);
+ return true;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 0c92abc..b9ce8ad 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -1163,16 +1163,6 @@
verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
Bundle result = mBundleCaptor.getValue();
- Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
- assertNotNull(sessionBundle);
- // Assert that session bundle is decrypted and hence data is visible.
- assertEquals(AccountManagerServiceTestFixtures.SESSION_DATA_VALUE_1,
- sessionBundle.getString(AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1));
- // Assert finishSessionAsUser added calling uid and pid into the sessionBundle
- assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_UID));
- assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_PID));
- assertEquals(sessionBundle.getString(
- AccountManager.KEY_ANDROID_PACKAGE_NAME), "APCT.package");
// Verify response data
assertNull(result.getString(AccountManager.KEY_AUTHTOKEN, null));
@@ -2121,12 +2111,6 @@
result.getString(AccountManager.KEY_ACCOUNT_NAME));
assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
result.getString(AccountManager.KEY_ACCOUNT_TYPE));
-
- Bundle optionBundle = result.getParcelable(
- AccountManagerServiceTestFixtures.KEY_OPTIONS_BUNDLE);
- // Assert addAccountAsUser added calling uid and pid into the option bundle
- assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_UID));
- assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_PID));
}
@SmallTest
@@ -3457,6 +3441,52 @@
+ (readTotalTime.doubleValue() / readerCount / loopSize));
}
+ @SmallTest
+ public void testSanitizeBundle_expectedFields() throws Exception {
+ Bundle bundle = new Bundle();
+ bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "name");
+ bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, "type");
+ bundle.putString(AccountManager.KEY_AUTHTOKEN, "token");
+ bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, "label");
+ bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "error message");
+ bundle.putString(AccountManager.KEY_PASSWORD, "password");
+ bundle.putString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN, "status");
+
+ bundle.putLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 123L);
+ bundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+ bundle.putInt(AccountManager.KEY_ERROR_CODE, 456);
+
+ Bundle sanitizedBundle = AccountManagerService.sanitizeBundle(bundle);
+ assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_NAME), "name");
+ assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE), "type");
+ assertEquals(sanitizedBundle.getString(AccountManager.KEY_AUTHTOKEN), "token");
+ assertEquals(sanitizedBundle.getString(AccountManager.KEY_AUTH_TOKEN_LABEL), "label");
+ assertEquals(sanitizedBundle.getString(AccountManager.KEY_ERROR_MESSAGE), "error message");
+ assertEquals(sanitizedBundle.getString(AccountManager.KEY_PASSWORD), "password");
+ assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN), "status");
+
+ assertEquals(sanitizedBundle.getLong(
+ AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0), 123L);
+ assertEquals(sanitizedBundle.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false), true);
+ assertEquals(sanitizedBundle.getInt(AccountManager.KEY_ERROR_CODE, 0), 456);
+ }
+
+ @SmallTest
+ public void testSanitizeBundle_filtersUnexpectedFields() throws Exception {
+ Bundle bundle = new Bundle();
+ bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "name");
+ bundle.putString("unknown_key", "value");
+ Bundle sessionBundle = new Bundle();
+ bundle.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle);
+
+ Bundle sanitizedBundle = AccountManagerService.sanitizeBundle(bundle);
+
+ assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_NAME), "name");
+ assertFalse(sanitizedBundle.containsKey("unknown_key"));
+ // It is a valid response from Authenticator which will be accessed using original Bundle
+ assertFalse(sanitizedBundle.containsKey(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE));
+ }
+
private void waitForCyclicBarrier(CyclicBarrier cyclicBarrier) {
try {
cyclicBarrier.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index a7e8a00..a5f1fcd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -29,6 +29,7 @@
import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
import static com.android.server.hdmi.RequestActiveSourceAction.TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS;
+import static com.android.server.hdmi.RoutingControlAction.TIMEOUT_ROUTING_INFORMATION_MS;
import static com.google.common.truth.Truth.assertThat;
@@ -77,6 +78,7 @@
public class HdmiCecLocalDeviceTvTest {
private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1;
private static final int PORT_1 = 1;
+ private static final int PORT_2 = 2;
private static final String[] SADS_NOT_TO_QUERY = new String[]{
HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1,
@@ -215,7 +217,7 @@
.setEarcSupported(false)
.build();
hdmiPortInfos[1] =
- new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, 0x2000)
+ new HdmiPortInfo.Builder(PORT_2, HdmiPortInfo.PORT_INPUT, 0x2000)
.setCecSupported(true)
.setMhlSupported(false)
.setArcSupported(true)
@@ -2021,7 +2023,7 @@
ADDR_TV);
mTestLooper.dispatchAll();
- assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+ assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
.isFalse();
mPowerManager.setInteractive(true);
mTestLooper.dispatchAll();
@@ -2031,14 +2033,14 @@
"HdmiCecLocalDeviceTvTest");
mTestLooper.dispatchAll();
- assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+ assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
.isTrue();
assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
assertThat(mPowerManager.isInteractive()).isFalse();
- assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+ assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
.isFalse();
}
@@ -2051,7 +2053,7 @@
mHdmiCecLocalDeviceTv = new MockTvDevice(mHdmiControlService);
mTestLooper.dispatchAll();
- assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+ assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
.isFalse();
mPowerManager.setInteractive(true);
@@ -2060,7 +2062,7 @@
"HdmiCecLocalDeviceTvTest");
mTestLooper.dispatchAll();
- assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+ assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
.isTrue();
assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
.isEqualTo(Constants.HANDLED);
@@ -2076,22 +2078,51 @@
mHdmiCecLocalDeviceTv = new MockTvDevice(mHdmiControlService);
mTestLooper.dispatchAll();
- assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+ assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
.isFalse();
mPowerManager.setInteractive(true);
- assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+ assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
.isFalse();
assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
assertThat(mPowerManager.isInteractive()).isFalse();
- assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+ assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
.isFalse();
}
@Test
+ public void handleStandby_fromNonActiveSource_previousActivePathSetToNonCecDevice_Standby() {
+ HdmiCecLocalDeviceTv hdmiCecLocalDeviceTv = new MockTvDevice(mHdmiControlService);
+ hdmiCecLocalDeviceTv.setDeviceInfo(mHdmiCecLocalDeviceTv.getDeviceInfo());
+ mTestLooper.dispatchAll();
+
+ assertThat(hdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
+ .isFalse();
+ mPowerManager.setInteractive(true);
+ hdmiCecLocalDeviceTv.doManualPortSwitching(PORT_2, null);
+ mTestLooper.dispatchAll();
+
+ // Timeout the action RoutingControlAction such that the active path would be updated.
+ mTestLooper.moveTimeForward(TIMEOUT_ROUTING_INFORMATION_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(hdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
+ .isTrue();
+ HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(
+ ADDR_PLAYBACK_1, ADDR_TV);
+ assertThat(hdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
+ .isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+
+ assertThat(mPowerManager.isInteractive()).isTrue();
+ assertThat(hdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
+ .isTrue();
+ }
+
+ @Test
public void handleReportPhysicalAddress_DeviceDiscoveryActionInProgress_noNewDeviceAction() {
mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
@@ -2189,7 +2220,7 @@
@Override
protected int handleActiveSource(HdmiCecMessage message) {
- setWasActiveSourceSetToConnectedDevice(true);
+ setWasActivePathSetToConnectedDevice(true);
return super.handleActiveSource(message);
}
}
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
index e27bb4c..b9ece93 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
@@ -40,12 +40,19 @@
public class StubTransaction extends SurfaceControl.Transaction {
private HashSet<Runnable> mWindowInfosReportedListeners = new HashSet<>();
+ private HashSet<SurfaceControl.TransactionCommittedListener> mTransactionCommittedListeners =
+ new HashSet<>();
@Override
public void apply() {
for (Runnable listener : mWindowInfosReportedListeners) {
listener.run();
}
+ for (SurfaceControl.TransactionCommittedListener listener
+ : mTransactionCommittedListeners) {
+ listener.onTransactionCommitted();
+ }
+ mTransactionCommittedListeners.clear();
}
@Override
@@ -239,6 +246,9 @@
@Override
public SurfaceControl.Transaction addTransactionCommittedListener(Executor executor,
SurfaceControl.TransactionCommittedListener listener) {
+ SurfaceControl.TransactionCommittedListener listenerInner =
+ () -> executor.execute(listener::onTransactionCommitted);
+ mTransactionCommittedListeners.add(listenerInner);
return this;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 4e376aa..48bc9d7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -888,7 +888,7 @@
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a", 0, true);
service.reregisterService(cn, 0);
@@ -919,7 +919,7 @@
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a", 0, false);
service.reregisterService(cn, 0);
@@ -950,7 +950,7 @@
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a/a", 0, true);
service.reregisterService(cn, 0);
@@ -981,7 +981,7 @@
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a/a", 0, false);
service.reregisterService(cn, 0);
@@ -1211,64 +1211,6 @@
}
@Test
- public void testUpgradeAppNoIntentFilterNoRebind() throws Exception {
- Context context = spy(getContext());
- doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
-
- ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles,
- mIpm, APPROVAL_BY_COMPONENT);
-
- List<String> packages = new ArrayList<>();
- packages.add("package");
- addExpectedServices(service, packages, 0);
-
- final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1");
- final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2");
-
- // Both components are approved initially
- mExpectedPrimaryComponentNames.clear();
- mExpectedPrimaryPackages.clear();
- mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
- mExpectedSecondaryComponentNames.clear();
- mExpectedSecondaryPackages.clear();
-
- loadXml(service);
-
- //Component package/C1 loses serviceInterface intent filter
- ManagedServices.Config config = service.getConfig();
- when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).
- thenAnswer(new Answer<List<ResolveInfo>>() {
- @Override
- public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
- throws Throwable {
- Object[] args = invocationOnMock.getArguments();
- Intent invocationIntent = (Intent) args[0];
- if (invocationIntent != null) {
- if (invocationIntent.getAction().equals(config.serviceInterface)
- && packages.contains(invocationIntent.getPackage())) {
- List<ResolveInfo> dummyServices = new ArrayList<>();
- ResolveInfo resolveInfo = new ResolveInfo();
- ServiceInfo serviceInfo = new ServiceInfo();
- serviceInfo.packageName = invocationIntent.getPackage();
- serviceInfo.name = approvedComponent.getClassName();
- serviceInfo.permission = service.getConfig().bindPermission;
- resolveInfo.serviceInfo = serviceInfo;
- dummyServices.add(resolveInfo);
- return dummyServices;
- }
- }
- return new ArrayList<>();
- }
- });
-
- // Trigger package update
- service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
-
- assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent));
- assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent));
- }
-
- @Test
public void testSetPackageOrComponentEnabled() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -2002,7 +1944,7 @@
metaDataAutobindAllow.putBoolean(META_DATA_DEFAULT_AUTOBIND, true);
metaDatas.put(cn_allowed, metaDataAutobindAllow);
- mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
+ mockServiceInfoWithMetaData(componentNames, service, metaDatas);
service.addApprovedList(cn_allowed.flattenToString(), 0, true);
service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -2047,7 +1989,7 @@
metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
- mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
+ mockServiceInfoWithMetaData(componentNames, service, metaDatas);
service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -2086,7 +2028,7 @@
metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
- mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
+ mockServiceInfoWithMetaData(componentNames, service, metaDatas);
service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -2157,8 +2099,8 @@
}
private void mockServiceInfoWithMetaData(List<ComponentName> componentNames,
- ManagedServices service, PackageManager packageManager,
- ArrayMap<ComponentName, Bundle> metaDatas) throws RemoteException {
+ ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas)
+ throws RemoteException {
when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
(Answer<ServiceInfo>) invocation -> {
ComponentName invocationCn = invocation.getArgument(0);
@@ -2173,39 +2115,6 @@
return null;
}
);
-
- // add components to queryIntentServicesAsUser response
- final List<String> packages = new ArrayList<>();
- for (ComponentName cn: componentNames) {
- packages.add(cn.getPackageName());
- }
- ManagedServices.Config config = service.getConfig();
- when(packageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt())).
- thenAnswer(new Answer<List<ResolveInfo>>() {
- @Override
- public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
- throws Throwable {
- Object[] args = invocationOnMock.getArguments();
- Intent invocationIntent = (Intent) args[0];
- if (invocationIntent != null) {
- if (invocationIntent.getAction().equals(config.serviceInterface)
- && packages.contains(invocationIntent.getPackage())) {
- List<ResolveInfo> dummyServices = new ArrayList<>();
- for (ComponentName cn: componentNames) {
- ResolveInfo resolveInfo = new ResolveInfo();
- ServiceInfo serviceInfo = new ServiceInfo();
- serviceInfo.packageName = invocationIntent.getPackage();
- serviceInfo.name = cn.getClassName();
- serviceInfo.permission = service.getConfig().bindPermission;
- resolveInfo.serviceInfo = serviceInfo;
- dummyServices.add(resolveInfo);
- }
- return dummyServices;
- }
- }
- return new ArrayList<>();
- }
- });
}
private void resetComponentsAndPackages() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 0c1fbf3..c294bc6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -94,6 +94,7 @@
public void setUp() throws Exception {
assumeFalse(WindowManagerService.sEnableShellTransitions);
mAppTransitionController = new AppTransitionController(mWm, mDisplayContent);
+ mWm.mAnimator.ready();
}
@Test
@@ -855,7 +856,7 @@
// Prepare and start transition.
prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation run by the remote handler.
assertTrue(remoteAnimationRunner.isAnimationStarted());
@@ -886,7 +887,7 @@
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity,
null /* changingTaskFragment */);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation is not run by the remote handler because the activity is filling the Task.
assertFalse(remoteAnimationRunner.isAnimationStarted());
@@ -921,7 +922,7 @@
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity,
null /* changingTaskFragment */);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation run by the remote handler.
assertTrue(remoteAnimationRunner.isAnimationStarted());
@@ -946,7 +947,7 @@
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation run by the remote handler.
assertTrue(remoteAnimationRunner.isAnimationStarted());
@@ -973,7 +974,7 @@
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment1);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation run by the remote handler.
assertTrue(remoteAnimationRunner.isAnimationStarted());
@@ -997,7 +998,7 @@
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation not run by the remote handler.
assertFalse(remoteAnimationRunner.isAnimationStarted());
@@ -1024,7 +1025,7 @@
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation should not run by the remote handler when there are non-embedded activities of
// different UID.
@@ -1051,7 +1052,7 @@
// Prepare and start transition.
prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation should not run by the remote handler when there is wallpaper in the transition.
assertFalse(remoteAnimationRunner.isAnimationStarted());
@@ -1085,7 +1086,7 @@
// Prepare and start transition.
prepareAndTriggerAppTransition(activity1, null /* closingActivity */, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// The animation will be animated remotely by client and all activities are input disabled
// for untrusted animation.
@@ -1136,7 +1137,7 @@
// Prepare and start transition.
prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// The animation will be animated remotely by client and all activities are input disabled
// for untrusted animation.
@@ -1178,7 +1179,7 @@
// Prepare and start transition.
prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// The animation will be animated remotely by client, but input should not be dropped for
// fully trusted.
@@ -1302,4 +1303,4 @@
doReturn(mock(RemoteAnimationTarget.class)).when(activity).createRemoteAnimationTarget(
any());
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index ea175a5..f70dceb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -51,6 +51,7 @@
public void setUp() throws Exception {
mImeProvider = mDisplayContent.getInsetsStateController().getImeSourceProvider();
mImeProvider.getSource().setVisible(true);
+ mWm.mAnimator.ready();
}
@Test
@@ -151,8 +152,8 @@
assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertFalse(mImeProvider.isImeShowing());
- // Starting the afterPrepareSurfacesRunnable picks up the show scheduled above.
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ // Waiting for the afterPrepareSurfacesRunnable picks up the show scheduled above.
+ waitUntilWindowAnimatorIdle();
// No longer scheduled as it was already shown.
assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertTrue(mImeProvider.isImeShowing());
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 6190807..e8d089c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -187,8 +187,8 @@
assertEquals(mProvider.getControlTarget(), target);
assertNull(mProvider.getLeash(target));
- // After surface transactions are applied, the leash is ready for dispatching.
- mProvider.onSurfaceTransactionApplied();
+ // Set the leash to be ready for dispatching.
+ mProvider.mIsLeashReadyForDispatching = true;
assertNotNull(mProvider.getLeash(target));
// We do have fake control for the fake control target, but that has no leash.
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 964264d..d0d7c06 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -388,6 +388,7 @@
mDisplayContent.getInsetsPolicy().updateBarControlTarget(app);
mDisplayContent.getInsetsPolicy().showTransient(statusBars(),
true /* isGestureOnSystemBar */);
+ mWm.mAnimator.ready();
waitUntilWindowAnimatorIdle();
assertTrue(mDisplayContent.getInsetsPolicy().isTransient(statusBars()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 6f7d0dc..c5cbedb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -110,6 +110,7 @@
runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0);
mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter,
mHandler, false /*isActivityEmbedding*/);
+ mWm.mAnimator.ready();
}
private WindowState createAppOverlayWindow() {
@@ -133,7 +134,7 @@
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -165,7 +166,7 @@
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -287,7 +288,7 @@
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -333,7 +334,7 @@
task.applyAnimationUnchecked(null /* lp */, true /* enter */, TRANSIT_OLD_TASK_OPEN,
false /* isVoiceInteraction */, null /* sources */);
mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
try {
@@ -360,7 +361,7 @@
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -414,7 +415,7 @@
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -468,7 +469,7 @@
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -523,7 +524,7 @@
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -556,7 +557,7 @@
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -592,7 +593,7 @@
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -642,7 +643,7 @@
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -742,7 +743,7 @@
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(transit);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
return adapter;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index ae722807..71cfbfd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -571,7 +571,7 @@
// This makes sure all previous messages in the handler are fully processed vs. just popping
// them from the message queue.
final AtomicBoolean currentMessagesProcessed = new AtomicBoolean(false);
- wm.mAnimator.getChoreographer().postFrameCallback(time -> {
+ wm.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
synchronized (currentMessagesProcessed) {
currentMessagesProcessed.set(true);
currentMessagesProcessed.notifyAll();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 7320c0b..52a80b0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2005,10 +2005,10 @@
@DisableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_disableAnimOptionsPerChange() {
- ActivityRecord r = initializeOverrideAnimationOptionsTest();
+ initializeOverrideAnimationOptionsTest();
TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
.makeCommonAnimOptions("testPackage");
- mTransition.setOverrideAnimation(options, r, null /* startCallback */,
+ mTransition.setOverrideAnimation(options, null /* startCallback */,
null /* finishCallback */);
mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
@@ -2019,10 +2019,10 @@
@EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_fromStyleAnimOptions() {
- ActivityRecord r = initializeOverrideAnimationOptionsTest();
+ initializeOverrideAnimationOptionsTest();
TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
.makeCommonAnimOptions("testPackage");
- mTransition.setOverrideAnimation(options, r, null /* startCallback */,
+ mTransition.setOverrideAnimation(options, null /* startCallback */,
null /* finishCallback */);
mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
@@ -2045,10 +2045,10 @@
@EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_sceneAnimOptions() {
- ActivityRecord r = initializeOverrideAnimationOptionsTest();
+ initializeOverrideAnimationOptionsTest();
TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
.makeSceneTransitionAnimOptions();
- mTransition.setOverrideAnimation(options, r, null /* startCallback */,
+ mTransition.setOverrideAnimation(options, null /* startCallback */,
null /* finishCallback */);
mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
@@ -2071,10 +2071,10 @@
@EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_crossProfileAnimOptions() {
- ActivityRecord r = initializeOverrideAnimationOptionsTest();
+ initializeOverrideAnimationOptionsTest();
TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
.makeCrossProfileAnimOptions();
- mTransition.setOverrideAnimation(options, r, null /* startCallback */,
+ mTransition.setOverrideAnimation(options, null /* startCallback */,
null /* finishCallback */);
final TransitionInfo.Change displayChange = mInfo.getChanges().get(0);
@@ -2099,13 +2099,13 @@
@EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptions() {
- ActivityRecord r = initializeOverrideAnimationOptionsTest();
+ initializeOverrideAnimationOptionsTest();
TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
.makeCustomAnimOptions("testPackage", Resources.ID_NULL,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
Color.GREEN, false /* overrideTaskTransition */);
- mTransition.setOverrideAnimation(options, r, null /* startCallback */,
+ mTransition.setOverrideAnimation(options, null /* startCallback */,
null /* finishCallback */);
mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
@@ -2132,7 +2132,7 @@
@EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_haveTaskFragmentAnimParams() {
- ActivityRecord r = initializeOverrideAnimationOptionsTest();
+ initializeOverrideAnimationOptionsTest();
final TaskFragment embeddedTf = mTransition.mTargets.get(2).mContainer.asTaskFragment();
embeddedTf.setAnimationParams(new TaskFragmentAnimationParams.Builder()
@@ -2145,7 +2145,7 @@
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
Color.GREEN, false /* overrideTaskTransition */);
- mTransition.setOverrideAnimation(options, r, null /* startCallback */,
+ mTransition.setOverrideAnimation(options, null /* startCallback */,
null /* finishCallback */);
final TransitionInfo.Change displayChange = mInfo.getChanges().get(0);
@@ -2181,13 +2181,13 @@
@EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptionsWithTaskOverride() {
- ActivityRecord r = initializeOverrideAnimationOptionsTest();
+ initializeOverrideAnimationOptionsTest();
TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
.makeCustomAnimOptions("testPackage", Resources.ID_NULL,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
Color.GREEN, true /* overrideTaskTransition */);
- mTransition.setOverrideAnimation(options, r, null /* startCallback */,
+ mTransition.setOverrideAnimation(options, null /* startCallback */,
null /* finishCallback */);
mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
@@ -2213,7 +2213,7 @@
options.getBackgroundColor(), activityChange.getBackgroundColor());
}
- private ActivityRecord initializeOverrideAnimationOptionsTest() {
+ private void initializeOverrideAnimationOptionsTest() {
mTransition = createTestTransition(TRANSIT_OPEN);
// Test set AnimationOptions for Activity and Task.
@@ -2241,7 +2241,6 @@
embeddedTf.getAnimationLeash()));
mInfo.addChange(new TransitionInfo.Change(null /* container */,
nonEmbeddedActivity.getAnimationLeash()));
- return nonEmbeddedActivity;
}
@Test
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index 6482432..ff9cba2 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -33,7 +33,6 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
import java.util.ArrayList;
@@ -253,21 +252,17 @@
// 3. It has not been installed as an update from its system built-in version
// 4. It is in default state (not explicitly DISABLED/DISABLED_BY_USER/ENABLED)
// 5. It is currently installed for the calling user
- // TODO(b/329739019):
- // 1. Merge the nested if conditions below during flag cleaning up phase
- // 2. Support user case that NEW carrier app is added during OTA, when emerge.
- if (!Flags.hidePreinstalledCarrierAppAtMostOnce() || !hasRunEver) {
- if (!isUpdatedSystemApp(ai) && enabledSetting
- == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
- && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
- Log.i(TAG, "Update state (" + packageName
- + "): DISABLED_UNTIL_USED for user " + userId);
- context.createContextAsUser(UserHandle.of(userId), 0)
- .getPackageManager()
- .setSystemAppState(
- packageName,
- PackageManager.SYSTEM_APP_STATE_UNINSTALLED);
- }
+ // TODO(b/329739019):Support user case that NEW carrier app is added during OTA
+ if (!hasRunEver && !isUpdatedSystemApp(ai) && enabledSetting
+ == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+ Log.i(TAG, "Update state (" + packageName
+ + "): DISABLED_UNTIL_USED for user " + userId);
+ context.createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .setSystemAppState(
+ packageName,
+ PackageManager.SYSTEM_APP_STATE_UNINSTALLED);
}
// Associated apps are more brittle, because we can't rely on the distinction
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
index 162fe2b..24377c7 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
@@ -22,7 +22,7 @@
@Backing(type="int")
enum SatelliteModemState {
/**
- * Satellite modem is in idle state.
+ * Satellite Idle state during which modem will scan for TN networks.
*/
SATELLITE_MODEM_STATE_IDLE = 0,
/**
@@ -46,13 +46,13 @@
*/
SATELLITE_MODEM_STATE_UNAVAILABLE = 5,
/**
- * The satellite modem is powered on but the device is not registered to a satellite cell.
+ * The satellite modem is powered on but the device is out of service.
*/
- SATELLITE_MODEM_STATE_NOT_CONNECTED = 6,
+ SATELLITE_MODEM_STATE_OUT_OF_SERVICE = 6,
/**
- * The satellite modem is powered on and the device is registered to a satellite cell.
+ * The satellite modem is powered on and the device is registered and in service.
*/
- SATELLITE_MODEM_STATE_CONNECTED = 7,
+ SATELLITE_MODEM_STATE_IN_SERVICE = 7,
/**
* Satellite modem state is unknown. This generic modem state should be used only when the
* modem state cannot be mapped to other specific modem states.
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index 6db5f82..27cc923 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -75,6 +75,7 @@
@Presubmit
@RunWith(JUnit4.class)
public class PerfettoProtoLogImplTest {
+ private static final String TEST_PROTOLOG_DATASOURCE_NAME = "test.android.protolog";
private final File mTracingDirectory = InstrumentationRegistry.getInstrumentation()
.getTargetContext().getFilesDir();
@@ -91,6 +92,7 @@
new TraceConfig(false, true, false)
);
+ private ProtoLogConfigurationService mProtoLogConfigurationService;
private PerfettoProtoLogImpl mProtoLog;
private Protolog.ProtoLogViewerConfig.Builder mViewerConfigBuilder;
private File mFile;
@@ -162,9 +164,15 @@
mCacheUpdater = () -> {};
mReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider));
+
+ final ProtoLogDataSourceBuilder dataSourceBuilder =
+ (onStart, onFlush, onStop) -> new ProtoLogDataSource(
+ onStart, onFlush, onStop, TEST_PROTOLOG_DATASOURCE_NAME);
+ mProtoLogConfigurationService =
+ new ProtoLogConfigurationService(dataSourceBuilder);
mProtoLog = new PerfettoProtoLogImpl(
- viewerConfigInputStreamProvider, mReader,
- () -> mCacheUpdater.run(), TestProtoLogGroup.values());
+ viewerConfigInputStreamProvider, mReader, () -> mCacheUpdater.run(),
+ TestProtoLogGroup.values(), dataSourceBuilder, mProtoLogConfigurationService);
}
@After
@@ -183,8 +191,9 @@
@Test
public void isEnabled_returnsTrueAfterStart() {
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
try {
traceMonitor.start();
assertTrue(mProtoLog.isProtoEnabled());
@@ -195,8 +204,9 @@
@Test
public void isEnabled_returnsFalseAfterStop() {
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
try {
traceMonitor.start();
assertTrue(mProtoLog.isProtoEnabled());
@@ -209,8 +219,9 @@
@Test
public void defaultMode() throws IOException {
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(false).build();
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(false, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
try {
traceMonitor.start();
// Shouldn't be logging anything except WTF unless explicitly requested in the group
@@ -238,11 +249,13 @@
@Test
public void respectsOverrideConfigs_defaultMode() throws IOException {
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(
+ true,
List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
- TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, true)))
- .build();
+ TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, true)),
+ TEST_PROTOLOG_DATASOURCE_NAME
+ ).build();
try {
traceMonitor.start();
mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -273,10 +286,12 @@
@Test
public void respectsOverrideConfigs_allEnabledMode() throws IOException {
PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
+ PerfettoTraceMonitor.newBuilder().enableProtoLog(
+ true,
List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
- TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, false)))
- .build();
+ TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, false)),
+ TEST_PROTOLOG_DATASOURCE_NAME
+ ).build();
try {
traceMonitor.start();
mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -304,9 +319,9 @@
@Test
public void respectsAllEnabledMode() throws IOException {
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(true, List.of())
- .build();
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
try {
traceMonitor.start();
mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -405,8 +420,9 @@
ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO,
"My test message :: %s, %d, %o, %x, %f, %e, %g, %b");
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
long before;
long after;
try {
@@ -437,8 +453,9 @@
@Test
public void log_noProcessing() throws IOException {
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
long before;
long after;
try {
@@ -469,8 +486,9 @@
@Test
public void supportsLocationInformation() throws IOException {
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(true).build();
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
try {
traceMonitor.start();
mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -504,8 +522,9 @@
final long messageHash = addMessageToConfig(
ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO,
"My test message :: %s, %d, %f, %b");
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
long before;
long after;
try {
@@ -526,8 +545,9 @@
@Test
public void log_protoDisabled() throws Exception {
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(false).build();
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(false, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
try {
traceMonitor.start();
mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -544,12 +564,14 @@
@Test
public void stackTraceTrimmed() throws IOException {
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(
+ true,
List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG,
- true)))
- .build();
+ true)),
+ TEST_PROTOLOG_DATASOURCE_NAME
+ ).build();
try {
traceMonitor.start();
@@ -579,18 +601,18 @@
final AtomicInteger cacheUpdateCallCount = new AtomicInteger(0);
mCacheUpdater = cacheUpdateCallCount::incrementAndGet;
- PerfettoTraceMonitor traceMonitor1 =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
- List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
- TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN,
- false)))
- .build();
+ PerfettoTraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(true,
+ List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
+ TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN,
+ false)), TEST_PROTOLOG_DATASOURCE_NAME
+ ).build();
PerfettoTraceMonitor traceMonitor2 =
PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG,
- false)))
+ false)), TEST_PROTOLOG_DATASOURCE_NAME)
.build();
Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(0);
@@ -635,14 +657,14 @@
PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN,
- false)))
+ false)), TEST_PROTOLOG_DATASOURCE_NAME)
.build();
PerfettoTraceMonitor traceMonitor2 =
PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG,
- false)))
+ false)), TEST_PROTOLOG_DATASOURCE_NAME)
.build();
try {
@@ -712,9 +734,9 @@
@Test
public void supportsNullString() throws IOException {
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(true)
- .build();
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
try {
traceMonitor.start();
@@ -735,9 +757,9 @@
@Test
public void supportNullParams() throws IOException {
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(true)
- .build();
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
try {
traceMonitor.start();
@@ -758,13 +780,13 @@
@Test
public void handlesConcurrentTracingSessions() throws IOException {
- PerfettoTraceMonitor traceMonitor1 =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(true)
- .build();
+ PerfettoTraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
- PerfettoTraceMonitor traceMonitor2 =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(true)
- .build();
+ PerfettoTraceMonitor traceMonitor2 = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
final ResultWriter writer2 = new ResultWriter()
.forScenario(new ScenarioBuilder()
@@ -800,8 +822,9 @@
@Test
public void usesDefaultLogFromLevel() throws IOException {
- PerfettoTraceMonitor traceMonitor =
- PerfettoTraceMonitor.newBuilder().enableProtoLog(LogLevel.WARN).build();
+ PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableProtoLog(LogLevel.WARN, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+ .build();
try {
traceMonitor.start();
mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,